From e885a78bffeaf2145120484d5ec46156e51042e5 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Mon, 6 Nov 2023 14:27:37 +0400 Subject: [PATCH] #8 -- Fix fuzzy conclusions --- build.gradle | 1 - .../controller/InferenceRestController.java | 29 +++++ .../ru/ulstu/fc/rule/model/InferenceData.java | 43 +++++++ .../ru/ulstu/fc/rule/model/OutputValue.java | 27 +++++ .../java/ru/ulstu/fc/rule/model/Variable.java | 24 ++++ .../ru/ulstu/fc/rule/model/VariableValue.java | 30 +++++ .../rule/service/FuzzyInferenceService.java | 111 ++++++++++-------- src/main/resources/application.properties | 2 + 8 files changed, 218 insertions(+), 49 deletions(-) create mode 100644 src/main/java/ru/ulstu/fc/rule/controller/InferenceRestController.java create mode 100644 src/main/java/ru/ulstu/fc/rule/model/InferenceData.java create mode 100644 src/main/java/ru/ulstu/fc/rule/model/OutputValue.java create mode 100644 src/main/java/ru/ulstu/fc/rule/model/Variable.java create mode 100644 src/main/java/ru/ulstu/fc/rule/model/VariableValue.java diff --git a/build.gradle b/build.gradle index 5d5282b..e860137 100644 --- a/build.gradle +++ b/build.gradle @@ -59,7 +59,6 @@ dependencies { implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-hibernate5' implementation group: 'com.h2database', name:'h2' implementation group: 'commons-io', name: 'commons-io', version: '2.6' - implementation group: 'net.sourceforge.htmlunit', name: 'htmlunit', version: '2.35.0' implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0' implementation group: 'io.springfox', name: 'springfox-boot-starter', version: '3.0.0' diff --git a/src/main/java/ru/ulstu/fc/rule/controller/InferenceRestController.java b/src/main/java/ru/ulstu/fc/rule/controller/InferenceRestController.java new file mode 100644 index 0000000..0f3fb7e --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/controller/InferenceRestController.java @@ -0,0 +1,29 @@ +package ru.ulstu.fc.rule.controller; + +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import ru.ulstu.fc.rule.model.InferenceData; +import ru.ulstu.fc.rule.model.OutputValue; +import ru.ulstu.fc.rule.service.FuzzyInferenceService; + +import java.util.List; + +@RestController +@RequestMapping("rest") +public class InferenceRestController { + private final FuzzyInferenceService fuzzyInferenceService; + + public InferenceRestController(FuzzyInferenceService fuzzyInferenceService) { + this.fuzzyInferenceService = fuzzyInferenceService; + } + + @RequestMapping(value = "get-inference", method = RequestMethod.POST) + public List getInference(@RequestBody InferenceData inferenceData) { + return fuzzyInferenceService.getFuzzyInference(inferenceData.getRules(), + inferenceData.getValues(), + inferenceData.getInputVariables(), + inferenceData.getOutputVariable()); + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/model/InferenceData.java b/src/main/java/ru/ulstu/fc/rule/model/InferenceData.java new file mode 100644 index 0000000..74e7ba9 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/model/InferenceData.java @@ -0,0 +1,43 @@ +package ru.ulstu.fc.rule.model; + +import java.util.List; +import java.util.Map; + +public class InferenceData { + private List rules; + private Map values; + private List inputVariables; + private Variable outputVariable; + + public List getRules() { + return rules; + } + + public void setRules(List rules) { + this.rules = rules; + } + + public Map getValues() { + return values; + } + + public void setValues(Map values) { + this.values = values; + } + + public List getInputVariables() { + return inputVariables; + } + + public void setInputVariables(List inputVariables) { + this.inputVariables = inputVariables; + } + + public Variable getOutputVariable() { + return outputVariable; + } + + public void setOutputVariable(Variable outputVariable) { + this.outputVariable = outputVariable; + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/model/OutputValue.java b/src/main/java/ru/ulstu/fc/rule/model/OutputValue.java new file mode 100644 index 0000000..5a177e9 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/model/OutputValue.java @@ -0,0 +1,27 @@ +package ru.ulstu.fc.rule.model; + +public class OutputValue { + private String fuzzyTerm; + private Double degree; + + public OutputValue(String fuzzyTerm, Double degree) { + this.fuzzyTerm = fuzzyTerm; + this.degree = degree; + } + + public String getFuzzyTerm() { + return fuzzyTerm; + } + + public void setFuzzyTerm(String fuzzyTerm) { + this.fuzzyTerm = fuzzyTerm; + } + + public Double getDegree() { + return degree; + } + + public void setDegree(Double degree) { + this.degree = degree; + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/model/Variable.java b/src/main/java/ru/ulstu/fc/rule/model/Variable.java new file mode 100644 index 0000000..ebbc115 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/model/Variable.java @@ -0,0 +1,24 @@ +package ru.ulstu.fc.rule.model; + +import java.util.List; + +public class Variable { + private String name; + private List values; + + public Variable() { + } + + public Variable(String name, List values) { + this.name = name; + this.values = values; + } + + public String getName() { + return name; + } + + public List getValues() { + return values; + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/model/VariableValue.java b/src/main/java/ru/ulstu/fc/rule/model/VariableValue.java new file mode 100644 index 0000000..9bc9550 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/model/VariableValue.java @@ -0,0 +1,30 @@ +package ru.ulstu.fc.rule.model; + +public class VariableValue { + private String fuzzyTerm; + private Double value; + + public VariableValue() { + } + + public VariableValue(String fuzzyTerm, Double value) { + this.fuzzyTerm = fuzzyTerm; + this.value = value; + } + + public String getFuzzyTerm() { + return fuzzyTerm; + } + + public void setFuzzyTerm(String fuzzyTerm) { + this.fuzzyTerm = fuzzyTerm; + } + + public Double getValue() { + return value; + } + + public void setValue(Double value) { + this.value = value; + } +} 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 518a9e5..31389f0 100644 --- a/src/main/java/ru/ulstu/fc/rule/service/FuzzyInferenceService.java +++ b/src/main/java/ru/ulstu/fc/rule/service/FuzzyInferenceService.java @@ -2,24 +2,24 @@ package ru.ulstu.fc.rule.service; import com.fuzzylite.Engine; import com.fuzzylite.activation.General; -import com.fuzzylite.defuzzifier.Centroid; +import com.fuzzylite.defuzzifier.WeightedAverage; 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 ru.ulstu.fc.rule.model.OutputValue; +import ru.ulstu.fc.rule.model.Variable; +import ru.ulstu.fc.rule.model.VariableValue; -import java.util.AbstractMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.stream.Collectors; @Service @@ -31,65 +31,52 @@ public class FuzzyInferenceService { + " is %s"; private final static String NO_RESULT = "Нет результата"; - 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 Map>> outputFuzzyTerms = Map.of( - "кредит", List.of(new AbstractMap.SimpleEntry("небольшой", 20000), - new AbstractMap.SimpleEntry("средний", 100000), - new AbstractMap.SimpleEntry("большой", 1000000))); - private List getDemoRules() { return List.of( String.format(RULE_TEMPLATE, "возраст", "молодой", "доход", "высокий", "большой"), String.format(RULE_TEMPLATE, "возраст", "средний", "доход", "высокий", "средний"), - String.format(RULE_TEMPLATE, "возраст", "старый", "доход", "высокий", "средний") + 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) { + private InputVariable getInputVariable(Variable variable) { final InputVariable input = new InputVariable(); - input.setName(name); + input.setName(variable.getName()); input.setDescription(""); - input.setRange(0, terms.get(terms.size() - 1).getValue()); + input.setRange(0, variable.getValues().get(variable.getValues().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()); + for (int i = 0; i < variable.getValues().size(); i++) { + Triangle term = new Triangle(variable.getValues().get(i).getFuzzyTerm(), + prev, + variable.getValues().get(i).getValue(), + variable.getValues().get(i).getValue() + variable.getValues().get(i).getValue() - prev); prev = term.getVertexB(); input.addTerm(term); } return input; } - private > OutputVariable getOutputVariable(String name, List> terms) { + private > OutputVariable getOutputVariable(Variable variable) { final OutputVariable output = new OutputVariable(); - output.setName(name); + output.setName(variable.getName()); output.setDescription(""); - output.setRange(0, terms.get(terms.size() - 1).getValue()); + output.setRange(0, variable.getValues().get(variable.getValues().size() - 1).getValue()); output.setEnabled(true); output.setAggregation(new Maximum()); - output.setDefuzzifier(new Centroid(terms.get(terms.size() - 1).getValue())); + output.setDefuzzifier(new WeightedAverage()); 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()); + for (int i = 0; i < variable.getValues().size(); i++) { + Triangle term = new Triangle( + variable.getValues().get(i).getFuzzyTerm(), + prev, + variable.getValues().get(i).getValue(), + variable.getValues().get(i).getValue() + variable.getValues().get(i).getValue() - prev); prev = term.getVertexB(); output.addTerm(term); } @@ -97,9 +84,11 @@ public class FuzzyInferenceService { } private RuleBlock getRuleBlock(Engine engine, - List rules) { - getInputVariables().forEach(engine::addInputVariable); - engine.addOutputVariable(getOutputVariable("кредит", outputFuzzyTerms.get("кредит"))); + List rules, + List inputVariables, + Variable outputVariable) { + inputVariables.stream().map(this::getInputVariable).forEach(engine::addInputVariable); + engine.addOutputVariable(getOutputVariable(outputVariable)); RuleBlock mamdani = new RuleBlock(); mamdani.setName("mamdani"); @@ -119,7 +108,7 @@ public class FuzzyInferenceService { return engine; } - private Map getConsequent(Engine engine, Map variableValues) { + private List getConsequent(Engine engine, Map variableValues) { OutputVariable outputVariable = engine.getOutputVariable(OUTPUT_VARIABLE_NAME); for (Map.Entry variableValue : variableValues.entrySet()) { InputVariable inputVariable = engine.getInputVariable(variableValue.getKey()); @@ -130,14 +119,40 @@ public class FuzzyInferenceService { LOG.info("Output: {}", outputVariable.getValue()); } return Double.isNaN(outputVariable.getValue()) - ? Map.of(NO_RESULT, 0.0) - : outputVariable.fuzzyOutput().getTerms().stream().collect(Collectors.toMap(t -> t.getTerm().getName(), Activated::getDegree)); + ? List.of(new OutputValue(NO_RESULT, 0.0)) + : outputVariable.fuzzyOutput() + .getTerms() + .stream() + .map(t -> new OutputValue(t.getTerm().getName(), t.getDegree())) + .collect(Collectors.toList()); } - public Map getFuzzyInference() { + public List getFuzzyInference() { + return getFuzzyInference(getDemoRules(), Map.of("возраст", 20.0, "доход", 250000.0), + List.of(new Variable("возраст", List.of( + new VariableValue("молодой", 35.0), + new VariableValue("средний", 60.0), + new VariableValue("старый", 100.0)) + ), + new Variable("доход", List.of( + new VariableValue("небольшой", 35000.0), + new VariableValue("средний", 100000.0), + new VariableValue("высокий", 500000.0)) + ) + ), + new Variable("кредит", List.of( + new VariableValue("небольшой", 20000.0), + new VariableValue("средний", 100000.0), + new VariableValue("большой", 1000000.0))) + ); + } + + public List getFuzzyInference(List rules, + Map values, + List inputVariables, + Variable outputVariable) { Engine engine = getFuzzyEngine(); - engine.addRuleBlock(getRuleBlock(engine, getDemoRules())); - return getConsequent(engine, Map.of("возраст", 20.0, "доход", 250000.0)); + engine.addRuleBlock(getRuleBlock(engine, rules, inputVariables, outputVariable)); + return getConsequent(engine, values); } - } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index bb03f7e..da111f9 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -17,4 +17,6 @@ spring.datasource.password=password spring.jpa.database-platform=org.hibernate.dialect.H2Dialect spring.h2.console.enabled=true spring.jpa.hibernate.ddl-auto=update +# swagger-ui custom path +springdoc.swagger-ui.path=/swagger-ui.html