diff --git a/src/main/java/ru/ulstu/fc/config/MvcConfiguration.java b/src/main/java/ru/ulstu/fc/config/MvcConfiguration.java index 12d8f9e..1203d38 100644 --- a/src/main/java/ru/ulstu/fc/config/MvcConfiguration.java +++ b/src/main/java/ru/ulstu/fc/config/MvcConfiguration.java @@ -11,17 +11,16 @@ import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.i18n.CookieLocaleResolver; import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; @Configuration public class MvcConfiguration implements WebMvcConfigurer { - @Override - public void addViewControllers(ViewControllerRegistry registry) { - registry.addViewController("/index"); - } +// @Override +// public void addViewControllers(ViewControllerRegistry registry) { +// registry.addViewController("/index"); +// } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { diff --git a/src/main/java/ru/ulstu/fc/rule/controller/InferenceMvcController.java b/src/main/java/ru/ulstu/fc/rule/controller/InferenceMvcController.java new file mode 100644 index 0000000..7267da1 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/controller/InferenceMvcController.java @@ -0,0 +1,54 @@ +package ru.ulstu.fc.rule.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import ru.ulstu.fc.rule.model.Antecedent; +import ru.ulstu.fc.rule.model.InferenceForm; +import ru.ulstu.fc.rule.service.FuzzyInferenceService; + +import java.util.Arrays; +import java.util.List; + +@Controller +public class InferenceMvcController { + private final FuzzyInferenceService fuzzyInferenceService; + + public InferenceMvcController(FuzzyInferenceService fuzzyInferenceService) { + this.fuzzyInferenceService = fuzzyInferenceService; + } + + @GetMapping("/") + public String initInference(Model model) { + model.addAttribute("ageAntecedents", getAgeAntecedents()); + model.addAttribute("incomeAntecedents", getIncomeAntecedents()); + model.addAttribute("inferenceForm", new InferenceForm()); + return "index"; + } + + @RequestMapping(value = "get-inference", method = RequestMethod.POST) + public String getInference(@ModelAttribute InferenceForm inferenceForm, Model model) { + model.addAttribute("ageAntecedents", getAgeAntecedents()); + model.addAttribute("incomeAntecedents", getIncomeAntecedents()); + model.addAttribute("inferenceForm", inferenceForm); + model.addAttribute("response", fuzzyInferenceService.getFuzzyInference()); + return "index"; + } + + private List getAgeAntecedents() { + return Arrays.asList( + new Antecedent("молодой", "young"), + new Antecedent("средний", "average"), + new Antecedent("старый", "old")); + } + + private List getIncomeAntecedents() { + return Arrays.asList( + new Antecedent("малый", "small"), + new Antecedent("средний", "average"), + new Antecedent("высокий", "high")); + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/model/Antecedent.java b/src/main/java/ru/ulstu/fc/rule/model/Antecedent.java new file mode 100644 index 0000000..54915e1 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/model/Antecedent.java @@ -0,0 +1,19 @@ +package ru.ulstu.fc.rule.model; + +public class Antecedent { + private String value; + private String description; + + public Antecedent(String description, String value) { + this.description = description; + this.value = value; + } + + public String getValue() { + return value; + } + + public String getDescription() { + return description; + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/model/InferenceForm.java b/src/main/java/ru/ulstu/fc/rule/model/InferenceForm.java new file mode 100644 index 0000000..d336986 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/model/InferenceForm.java @@ -0,0 +1,22 @@ +package ru.ulstu.fc.rule.model; + +public class InferenceForm { + private String ageAntecedent; + private String incomeAntecedent; + + public String getAgeAntecedent() { + return ageAntecedent; + } + + public void setAgeAntecedent(String ageAntecedent) { + this.ageAntecedent = ageAntecedent; + } + + public String getIncomeAntecedent() { + return incomeAntecedent; + } + + public void setIncomeAntecedent(String incomeAntecedent) { + this.incomeAntecedent = incomeAntecedent; + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/service/FuzzyInferenceService.java b/src/main/java/ru/ulstu/fc/rule/service/FuzzyInferenceService.java index 70a9746..518a9e5 100644 --- a/src/main/java/ru/ulstu/fc/rule/service/FuzzyInferenceService.java +++ b/src/main/java/ru/ulstu/fc/rule/service/FuzzyInferenceService.java @@ -1,84 +1,116 @@ package ru.ulstu.fc.rule.service; import com.fuzzylite.Engine; +import com.fuzzylite.activation.General; +import com.fuzzylite.defuzzifier.Centroid; +import com.fuzzylite.norm.s.Maximum; +import com.fuzzylite.norm.t.AlgebraicProduct; +import com.fuzzylite.norm.t.Minimum; +import com.fuzzylite.rule.Rule; +import com.fuzzylite.rule.RuleBlock; import com.fuzzylite.term.Activated; +import com.fuzzylite.term.Triangle; import com.fuzzylite.variable.InputVariable; import com.fuzzylite.variable.OutputVariable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; +import java.util.AbstractMap; +import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.stream.Collectors; @Service public class FuzzyInferenceService { private final static Logger LOG = LoggerFactory.getLogger(FuzzyInferenceService.class); - private final static String OUTPUT_VARIABLE_NAME = "state"; + private final static String OUTPUT_VARIABLE_NAME = "кредит"; private final static String RULE_TEMPLATE = "if %s is %s and %s is %s then " + OUTPUT_VARIABLE_NAME + " is %s"; private final static String NO_RESULT = "Нет результата"; -// private List mapRulesToString(List dbRules) { -// return dbRules.stream().map(this::getFuzzyRule).collect(Collectors.toList()); -// } + private Map>> inputFuzzyTerms = Map.of( + "возраст", + List.of( + new AbstractMap.SimpleEntry("молодой", 35), + new AbstractMap.SimpleEntry("средний", 60), + new AbstractMap.SimpleEntry("старый", 100)), + "доход", + List.of( + new AbstractMap.SimpleEntry("небольшой", 35000), + new AbstractMap.SimpleEntry("средний", 100000), + new AbstractMap.SimpleEntry("высокий", 500000))); -// private String getFuzzyRule(DbRule dbRule) { -// return format(RULE_TEMPLATE, -// dbRule.getFirstAntecedent().name(), -// dbRule.getFirstAntecedentValue().getAntecedentValue(), -// dbRule.getSecondAntecedent().name(), -// dbRule.getSecondAntecedentValue().getAntecedentValue(), -// dbRule.getId()); -// } + private Map>> outputFuzzyTerms = Map.of( + "кредит", List.of(new AbstractMap.SimpleEntry("небольшой", 20000), + new AbstractMap.SimpleEntry("средний", 100000), + new AbstractMap.SimpleEntry("большой", 1000000))); -// private RuleBlock getRuleBlock(Engine engine, -// List dbRules, -// Map variableValues, -// List antecedentValues, -// List consequentValues) { -// variableValues.forEach((key, value) -> { -// InputVariable input = new InputVariable(); -// input.setName(key); -// input.setDescription(""); -// input.setEnabled(true); -// input.setRange(-1, 1); -// input.setLockValueInRange(false); -// input.addTerm(new Triangle("спад", -1, 0)); -// input.addTerm(new Triangle("стабильно", -0.1, 0.1)); -// input.addTerm(new Triangle("рост", 0, 1)); -// engine.addInputVariable(input); -// }); -// -// OutputVariable output = new OutputVariable(); -// output.setName(OUTPUT_VARIABLE_NAME); -// output.setDescription(""); -// output.setEnabled(true); -// output.setRange(0, consequentValues.size() + 0.1); -// output.setAggregation(new Maximum()); -// output.setDefuzzifier(new Centroid(10)); -// output.setDefaultValue(Double.NaN); -// output.setLockValueInRange(false); -// for (int i = 0; i < consequentValues.size(); i++) { -// output.addTerm(new Triangle(consequentValues.get(i).toString(), i, i + 1)); -// } -// engine.addOutputVariable(output); -// -// RuleBlock mamdani = new RuleBlock(); -// mamdani.setName("mamdani"); -// mamdani.setDescription(""); -// mamdani.setEnabled(true); -// mamdani.setConjunction(new Minimum()); -// //mamdani.setDisjunction(null); -// mamdani.setImplication(new AlgebraicProduct()); -// mamdani.setActivation(new General()); -// mapRulesToString(dbRules).forEach(r -> { -// LOG.info(r); -// mamdani.addRule(Rule.parse(r, engine)); -// }); -// return mamdani; -// } + private List getDemoRules() { + return List.of( + String.format(RULE_TEMPLATE, "возраст", "молодой", "доход", "высокий", "большой"), + String.format(RULE_TEMPLATE, "возраст", "средний", "доход", "высокий", "средний"), + String.format(RULE_TEMPLATE, "возраст", "старый", "доход", "высокий", "средний") + ); + } + + private List getInputVariables() { + return List.of(getInputVariable("возраст", inputFuzzyTerms.get("возраст")), + getInputVariable("доход", inputFuzzyTerms.get("доход"))); + } + + private InputVariable getInputVariable(String name, List> terms) { + final InputVariable input = new InputVariable(); + input.setName(name); + input.setDescription(""); + input.setRange(0, terms.get(terms.size() - 1).getValue()); + input.setEnabled(true); + input.setLockValueInRange(false); + double prev = 0; + for (int i = 0; i < terms.size(); i++) { + Triangle term = new Triangle(terms.get(i).getKey(), prev, terms.get(i).getValue()); + prev = term.getVertexB(); + input.addTerm(term); + } + return input; + } + + private > OutputVariable getOutputVariable(String name, List> terms) { + final OutputVariable output = new OutputVariable(); + output.setName(name); + output.setDescription(""); + output.setRange(0, terms.get(terms.size() - 1).getValue()); + output.setEnabled(true); + output.setAggregation(new Maximum()); + output.setDefuzzifier(new Centroid(terms.get(terms.size() - 1).getValue())); + output.setDefaultValue(Double.NaN); + output.setLockValueInRange(false); + double prev = 0; + for (int i = 0; i < terms.size(); i++) { + Triangle term = new Triangle(terms.get(i).getKey(), prev, terms.get(i).getValue()); + prev = term.getVertexB(); + output.addTerm(term); + } + return output; + } + + private RuleBlock getRuleBlock(Engine engine, + List rules) { + getInputVariables().forEach(engine::addInputVariable); + engine.addOutputVariable(getOutputVariable("кредит", outputFuzzyTerms.get("кредит"))); + + RuleBlock mamdani = new RuleBlock(); + mamdani.setName("mamdani"); + mamdani.setDescription(""); + mamdani.setEnabled(true); + mamdani.setConjunction(new Minimum()); + mamdani.setImplication(new AlgebraicProduct()); + mamdani.setActivation(new General()); + rules.forEach(r -> mamdani.addRule(Rule.parse(r, engine))); + return mamdani; + } private Engine getFuzzyEngine() { Engine engine = new Engine(); @@ -87,29 +119,6 @@ public class FuzzyInferenceService { return engine; } -// public List getFuzzyInference(List dbRules, -// List antecedentValues, -// Map variableValues) { -// validateVariables(variableValues, dbRules); -// variableValues.entrySet().forEach(e -> System.out.println(e.getKey() + " " + e.getValue())); -// Engine engine = getFuzzyEngine(); -// List consequentValues = dbRules.stream().map(DbRule::getId).collect(Collectors.toList()); -// engine.addRuleBlock(getRuleBlock(engine, dbRules, variableValues, antecedentValues, consequentValues)); -// Map consequents = getConsequent(engine, variableValues); -// if (consequents.containsKey(NO_RESULT)) { -// return new ArrayList<>(); -// } -// List assessments = new ArrayList<>(); -// for (Map.Entry consequent : consequents.entrySet()) { -// for (DbRule dbRule : dbRules) { -// if (dbRule.getId().equals(Integer.valueOf(consequent.getKey()))) { -// assessments.add(new Assessment(dbRule, consequent.getValue())); -// } -// } -// } -// return assessments; -// } - private Map getConsequent(Engine engine, Map variableValues) { OutputVariable outputVariable = engine.getOutputVariable(OUTPUT_VARIABLE_NAME); for (Map.Entry variableValue : variableValues.entrySet()) { @@ -124,4 +133,11 @@ public class FuzzyInferenceService { ? Map.of(NO_RESULT, 0.0) : outputVariable.fuzzyOutput().getTerms().stream().collect(Collectors.toMap(t -> t.getTerm().getName(), Activated::getDegree)); } + + public Map getFuzzyInference() { + Engine engine = getFuzzyEngine(); + engine.addRuleBlock(getRuleBlock(engine, getDemoRules())); + return getConsequent(engine, Map.of("возраст", 20.0, "доход", 250000.0)); + } + } diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 034b1b2..48b474d 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -3,6 +3,54 @@ xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
- Индекс +
+
+ Пример вывода по набору нечетких правил: +
+
+ если возраст средний и доход высокий то размер кредита большой +
+
+ если возраст молодой и доход высокий то размер кредита средний +
+
+ если возраст старый и доход средний то размер кредита средний +
+
+ Выберите значения: +
+
+
+ Возраст +
+
+ +
+
+
+
+ Доход +
+
+ +
+
+ +
+