#8 -- Fix fuzzy conclusions

This commit is contained in:
Anton Romanov 2023-11-06 14:27:37 +04:00
parent f3710b6680
commit e885a78bff
8 changed files with 218 additions and 49 deletions

View File

@ -59,7 +59,6 @@ dependencies {
implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-hibernate5' implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-hibernate5'
implementation group: 'com.h2database', name:'h2' implementation group: 'com.h2database', name:'h2'
implementation group: 'commons-io', name: 'commons-io', version: '2.6' 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: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0'
implementation group: 'io.springfox', name: 'springfox-boot-starter', version: '3.0.0' implementation group: 'io.springfox', name: 'springfox-boot-starter', version: '3.0.0'

View File

@ -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<OutputValue> getInference(@RequestBody InferenceData inferenceData) {
return fuzzyInferenceService.getFuzzyInference(inferenceData.getRules(),
inferenceData.getValues(),
inferenceData.getInputVariables(),
inferenceData.getOutputVariable());
}
}

View File

@ -0,0 +1,43 @@
package ru.ulstu.fc.rule.model;
import java.util.List;
import java.util.Map;
public class InferenceData {
private List<String> rules;
private Map<String, Double> values;
private List<Variable> inputVariables;
private Variable outputVariable;
public List<String> getRules() {
return rules;
}
public void setRules(List<String> rules) {
this.rules = rules;
}
public Map<String, Double> getValues() {
return values;
}
public void setValues(Map<String, Double> values) {
this.values = values;
}
public List<Variable> getInputVariables() {
return inputVariables;
}
public void setInputVariables(List<Variable> inputVariables) {
this.inputVariables = inputVariables;
}
public Variable getOutputVariable() {
return outputVariable;
}
public void setOutputVariable(Variable outputVariable) {
this.outputVariable = outputVariable;
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,24 @@
package ru.ulstu.fc.rule.model;
import java.util.List;
public class Variable {
private String name;
private List<VariableValue> values;
public Variable() {
}
public Variable(String name, List<VariableValue> values) {
this.name = name;
this.values = values;
}
public String getName() {
return name;
}
public List<VariableValue> getValues() {
return values;
}
}

View File

@ -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;
}
}

View File

@ -2,24 +2,24 @@ package ru.ulstu.fc.rule.service;
import com.fuzzylite.Engine; import com.fuzzylite.Engine;
import com.fuzzylite.activation.General; 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.s.Maximum;
import com.fuzzylite.norm.t.AlgebraicProduct; import com.fuzzylite.norm.t.AlgebraicProduct;
import com.fuzzylite.norm.t.Minimum; import com.fuzzylite.norm.t.Minimum;
import com.fuzzylite.rule.Rule; import com.fuzzylite.rule.Rule;
import com.fuzzylite.rule.RuleBlock; import com.fuzzylite.rule.RuleBlock;
import com.fuzzylite.term.Activated;
import com.fuzzylite.term.Triangle; import com.fuzzylite.term.Triangle;
import com.fuzzylite.variable.InputVariable; import com.fuzzylite.variable.InputVariable;
import com.fuzzylite.variable.OutputVariable; import com.fuzzylite.variable.OutputVariable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service; 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.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Service @Service
@ -31,65 +31,52 @@ public class FuzzyInferenceService {
+ " is %s"; + " is %s";
private final static String NO_RESULT = "Нет результата"; private final static String NO_RESULT = "Нет результата";
private Map<String, List<Entry<String, Integer>>> 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<String, List<Entry<String, Integer>>> outputFuzzyTerms = Map.of(
"кредит", List.of(new AbstractMap.SimpleEntry("небольшой", 20000),
new AbstractMap.SimpleEntry("средний", 100000),
new AbstractMap.SimpleEntry("большой", 1000000)));
private List<String> getDemoRules() { private List<String> getDemoRules() {
return List.of( 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, "возраст", "старый", "доход", "высокий", "средний"),
String.format(RULE_TEMPLATE, "возраст", "старый", "доход", "небольшой", "небольшой"),
String.format(RULE_TEMPLATE, "возраст", "молодой", "доход", "небольшой", "небольшой")
); );
} }
private List<InputVariable> getInputVariables() { private InputVariable getInputVariable(Variable variable) {
return List.of(getInputVariable("возраст", inputFuzzyTerms.get("возраст")),
getInputVariable("доход", inputFuzzyTerms.get("доход")));
}
private InputVariable getInputVariable(String name, List<Entry<String, Integer>> terms) {
final InputVariable input = new InputVariable(); final InputVariable input = new InputVariable();
input.setName(name); input.setName(variable.getName());
input.setDescription(""); 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.setEnabled(true);
input.setLockValueInRange(false); input.setLockValueInRange(false);
double prev = 0; double prev = 0;
for (int i = 0; i < terms.size(); i++) { for (int i = 0; i < variable.getValues().size(); i++) {
Triangle term = new Triangle(terms.get(i).getKey(), prev, terms.get(i).getValue()); 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(); prev = term.getVertexB();
input.addTerm(term); input.addTerm(term);
} }
return input; return input;
} }
private <T extends Enum<T>> OutputVariable getOutputVariable(String name, List<Entry<String, Integer>> terms) { private <T extends Enum<T>> OutputVariable getOutputVariable(Variable variable) {
final OutputVariable output = new OutputVariable(); final OutputVariable output = new OutputVariable();
output.setName(name); output.setName(variable.getName());
output.setDescription(""); 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.setEnabled(true);
output.setAggregation(new Maximum()); output.setAggregation(new Maximum());
output.setDefuzzifier(new Centroid(terms.get(terms.size() - 1).getValue())); output.setDefuzzifier(new WeightedAverage());
output.setDefaultValue(Double.NaN); output.setDefaultValue(Double.NaN);
output.setLockValueInRange(false); output.setLockValueInRange(false);
double prev = 0; double prev = 0;
for (int i = 0; i < terms.size(); i++) { for (int i = 0; i < variable.getValues().size(); i++) {
Triangle term = new Triangle(terms.get(i).getKey(), prev, terms.get(i).getValue()); 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(); prev = term.getVertexB();
output.addTerm(term); output.addTerm(term);
} }
@ -97,9 +84,11 @@ public class FuzzyInferenceService {
} }
private RuleBlock getRuleBlock(Engine engine, private RuleBlock getRuleBlock(Engine engine,
List<String> rules) { List<String> rules,
getInputVariables().forEach(engine::addInputVariable); List<Variable> inputVariables,
engine.addOutputVariable(getOutputVariable("кредит", outputFuzzyTerms.get("кредит"))); Variable outputVariable) {
inputVariables.stream().map(this::getInputVariable).forEach(engine::addInputVariable);
engine.addOutputVariable(getOutputVariable(outputVariable));
RuleBlock mamdani = new RuleBlock(); RuleBlock mamdani = new RuleBlock();
mamdani.setName("mamdani"); mamdani.setName("mamdani");
@ -119,7 +108,7 @@ public class FuzzyInferenceService {
return engine; return engine;
} }
private Map<String, Double> getConsequent(Engine engine, Map<String, Double> variableValues) { private List<OutputValue> getConsequent(Engine engine, Map<String, Double> variableValues) {
OutputVariable outputVariable = engine.getOutputVariable(OUTPUT_VARIABLE_NAME); OutputVariable outputVariable = engine.getOutputVariable(OUTPUT_VARIABLE_NAME);
for (Map.Entry<String, Double> variableValue : variableValues.entrySet()) { for (Map.Entry<String, Double> variableValue : variableValues.entrySet()) {
InputVariable inputVariable = engine.getInputVariable(variableValue.getKey()); InputVariable inputVariable = engine.getInputVariable(variableValue.getKey());
@ -130,14 +119,40 @@ public class FuzzyInferenceService {
LOG.info("Output: {}", outputVariable.getValue()); LOG.info("Output: {}", outputVariable.getValue());
} }
return Double.isNaN(outputVariable.getValue()) return Double.isNaN(outputVariable.getValue())
? Map.of(NO_RESULT, 0.0) ? List.of(new OutputValue(NO_RESULT, 0.0))
: outputVariable.fuzzyOutput().getTerms().stream().collect(Collectors.toMap(t -> t.getTerm().getName(), Activated::getDegree)); : outputVariable.fuzzyOutput()
.getTerms()
.stream()
.map(t -> new OutputValue(t.getTerm().getName(), t.getDegree()))
.collect(Collectors.toList());
} }
public Map<String, Double> getFuzzyInference() { public List<OutputValue> 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<OutputValue> getFuzzyInference(List<String> rules,
Map<String, Double> values,
List<Variable> inputVariables,
Variable outputVariable) {
Engine engine = getFuzzyEngine(); Engine engine = getFuzzyEngine();
engine.addRuleBlock(getRuleBlock(engine, getDemoRules())); engine.addRuleBlock(getRuleBlock(engine, rules, inputVariables, outputVariable));
return getConsequent(engine, Map.of("возраст", 20.0, "доход", 250000.0)); return getConsequent(engine, values);
} }
} }

View File

@ -17,4 +17,6 @@ spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true spring.h2.console.enabled=true
spring.jpa.hibernate.ddl-auto=update spring.jpa.hibernate.ddl-auto=update
# swagger-ui custom path
springdoc.swagger-ui.path=/swagger-ui.html