From b3a1743f5263b57d1f7d8bf9ff6c58c75b4a12d4 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Wed, 6 Sep 2023 14:02:04 +0400 Subject: [PATCH 1/5] #8 -- Add fuzzy rules for example --- src/main/resources/templates/index.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 034b1b2..de9d67f 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -3,6 +3,12 @@ xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
- Индекс + Пример вывода по набору нечетких правил: +
+ если возраст средний и доход высокий то размер кредита большой +
+ если возраст молодой и доход высокий то размер кредита средний +
+ если возраст старый и доход средний то размер кредита средний
-- 2.34.1 From f39aa1c2b63c6d7a5f9b2457ae7a44c0dd62bd02 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Wed, 6 Sep 2023 14:20:56 +0400 Subject: [PATCH 2/5] #8 -- Add antecedent model --- .../java/ru/ulstu/fc/rule/service/Antecedent.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/main/java/ru/ulstu/fc/rule/service/Antecedent.java diff --git a/src/main/java/ru/ulstu/fc/rule/service/Antecedent.java b/src/main/java/ru/ulstu/fc/rule/service/Antecedent.java new file mode 100644 index 0000000..4020c14 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/service/Antecedent.java @@ -0,0 +1,14 @@ +package ru.ulstu.fc.rule.service; + +public class Antecedent { + private String value; + private String description; + + public String getValue() { + return value; + } + + public String getDescription() { + return description; + } +} -- 2.34.1 From 75765558d4ac0d5e768448485c036c39a41d6a7f Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Wed, 6 Sep 2023 15:49:59 +0400 Subject: [PATCH 3/5] #8 -- Add form controller --- .../ru/ulstu/fc/config/MvcConfiguration.java | 9 ++- .../controller/InferenceMvcController.java | 48 ++++++++++++++++ .../rule/{service => model}/Antecedent.java | 7 ++- .../ru/ulstu/fc/rule/model/InferenceForm.java | 22 ++++++++ src/main/resources/templates/index.html | 56 ++++++++++++++++--- 5 files changed, 129 insertions(+), 13 deletions(-) create mode 100644 src/main/java/ru/ulstu/fc/rule/controller/InferenceMvcController.java rename src/main/java/ru/ulstu/fc/rule/{service => model}/Antecedent.java (57%) create mode 100644 src/main/java/ru/ulstu/fc/rule/model/InferenceForm.java 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..1a9d616 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/controller/InferenceMvcController.java @@ -0,0 +1,48 @@ +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 java.util.Arrays; +import java.util.List; + +@Controller +public class InferenceMvcController { + + @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", "123"); + 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/service/Antecedent.java b/src/main/java/ru/ulstu/fc/rule/model/Antecedent.java similarity index 57% rename from src/main/java/ru/ulstu/fc/rule/service/Antecedent.java rename to src/main/java/ru/ulstu/fc/rule/model/Antecedent.java index 4020c14..54915e1 100644 --- a/src/main/java/ru/ulstu/fc/rule/service/Antecedent.java +++ b/src/main/java/ru/ulstu/fc/rule/model/Antecedent.java @@ -1,9 +1,14 @@ -package ru.ulstu.fc.rule.service; +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; } 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/resources/templates/index.html b/src/main/resources/templates/index.html index de9d67f..48b474d 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -3,12 +3,54 @@ xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
- Пример вывода по набору нечетких правил: -
- если возраст средний и доход высокий то размер кредита большой -
- если возраст молодой и доход высокий то размер кредита средний -
- если возраст старый и доход средний то размер кредита средний +
+
+ Пример вывода по набору нечетких правил: +
+
+ если возраст средний и доход высокий то размер кредита большой +
+
+ если возраст молодой и доход высокий то размер кредита средний +
+
+ если возраст старый и доход средний то размер кредита средний +
+
+ Выберите значения: +
+
+
+ Возраст +
+
+ +
+
+
+
+ Доход +
+
+ +
+
+ +
+
-- 2.34.1 From ab9a60cdd2fe991c053052bdeda156f3a3255238 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Thu, 7 Sep 2023 16:06:37 +0400 Subject: [PATCH 4/5] #8 -- Add some fuzzy --- .../controller/InferenceMvcController.java | 8 +- .../rule/service/FuzzyInferenceService.java | 191 ++++++++++-------- 2 files changed, 117 insertions(+), 82 deletions(-) diff --git a/src/main/java/ru/ulstu/fc/rule/controller/InferenceMvcController.java b/src/main/java/ru/ulstu/fc/rule/controller/InferenceMvcController.java index 1a9d616..1875bc8 100644 --- a/src/main/java/ru/ulstu/fc/rule/controller/InferenceMvcController.java +++ b/src/main/java/ru/ulstu/fc/rule/controller/InferenceMvcController.java @@ -8,12 +8,18 @@ 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) { @@ -28,7 +34,7 @@ public class InferenceMvcController { model.addAttribute("ageAntecedents", getAgeAntecedents()); model.addAttribute("incomeAntecedents", getIncomeAntecedents()); model.addAttribute("inferenceForm", inferenceForm); - model.addAttribute("response", "123"); + model.addAttribute("response", fuzzyInferenceService.getFuzzyInference().get(0)); return "index"; } 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..0c16b4c 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,115 @@ 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.ArrayList; +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("небольшой", 200000), + 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.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.setEnabled(true); + output.setAggregation(new Maximum()); + output.setDefuzzifier(new Centroid(10)); + 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 +118,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 +132,25 @@ public class FuzzyInferenceService { ? Map.of(NO_RESULT, 0.0) : outputVariable.fuzzyOutput().getTerms().stream().collect(Collectors.toMap(t -> t.getTerm().getName(), Activated::getDegree)); } + + public List getFuzzyInference() { + //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, getDemoRules())); + Map consequents = getConsequent(engine, Map.of("возраст", 20.0, "доход", 250000.0)); + 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 List.of(0.0); + } + } -- 2.34.1 From 93141932d640787d43b0cb9a1529b5628b246fdb Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Thu, 7 Sep 2023 18:16:40 +0400 Subject: [PATCH 5/5] #8 -- final of the demo --- .../controller/InferenceMvcController.java | 2 +- .../rule/service/FuzzyInferenceService.java | 25 +++++-------------- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/src/main/java/ru/ulstu/fc/rule/controller/InferenceMvcController.java b/src/main/java/ru/ulstu/fc/rule/controller/InferenceMvcController.java index 1875bc8..7267da1 100644 --- a/src/main/java/ru/ulstu/fc/rule/controller/InferenceMvcController.java +++ b/src/main/java/ru/ulstu/fc/rule/controller/InferenceMvcController.java @@ -34,7 +34,7 @@ public class InferenceMvcController { model.addAttribute("ageAntecedents", getAgeAntecedents()); model.addAttribute("incomeAntecedents", getIncomeAntecedents()); model.addAttribute("inferenceForm", inferenceForm); - model.addAttribute("response", fuzzyInferenceService.getFuzzyInference().get(0)); + model.addAttribute("response", fuzzyInferenceService.getFuzzyInference()); return "index"; } 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 0c16b4c..518a9e5 100644 --- a/src/main/java/ru/ulstu/fc/rule/service/FuzzyInferenceService.java +++ b/src/main/java/ru/ulstu/fc/rule/service/FuzzyInferenceService.java @@ -17,7 +17,6 @@ import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.util.AbstractMap; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -45,7 +44,7 @@ public class FuzzyInferenceService { new AbstractMap.SimpleEntry("высокий", 500000))); private Map>> outputFuzzyTerms = Map.of( - "кредит", List.of(new AbstractMap.SimpleEntry("небольшой", 200000), + "кредит", List.of(new AbstractMap.SimpleEntry("небольшой", 20000), new AbstractMap.SimpleEntry("средний", 100000), new AbstractMap.SimpleEntry("большой", 1000000))); @@ -66,6 +65,7 @@ public class FuzzyInferenceService { 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; @@ -81,9 +81,10 @@ public class FuzzyInferenceService { 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(10)); + output.setDefuzzifier(new Centroid(terms.get(terms.size() - 1).getValue())); output.setDefaultValue(Double.NaN); output.setLockValueInRange(false); double prev = 0; @@ -133,24 +134,10 @@ public class FuzzyInferenceService { : outputVariable.fuzzyOutput().getTerms().stream().collect(Collectors.toMap(t -> t.getTerm().getName(), Activated::getDegree)); } - public List getFuzzyInference() { - //variableValues.entrySet().forEach(e -> System.out.println(e.getKey() + " " + e.getValue())); + public Map getFuzzyInference() { Engine engine = getFuzzyEngine(); - //List consequentValues = dbRules.stream().map(DbRule::getId).collect(Collectors.toList()); engine.addRuleBlock(getRuleBlock(engine, getDemoRules())); - Map consequents = getConsequent(engine, Map.of("возраст", 20.0, "доход", 250000.0)); - 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 List.of(0.0); + return getConsequent(engine, Map.of("возраст", 20.0, "доход", 250000.0)); } } -- 2.34.1