Merge pull request '14-fuzzy-ts' (#15) from 14-fuzzy-ts into master
Reviewed-on: #15
This commit is contained in:
commit
5132418848
@ -30,6 +30,8 @@ dependencies {
|
|||||||
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-jetty'
|
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-jetty'
|
||||||
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-thymeleaf'
|
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-thymeleaf'
|
||||||
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-test'
|
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-test'
|
||||||
|
// web client
|
||||||
|
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-webflux'
|
||||||
implementation group: 'org.slf4j', name: 'slf4j-api'
|
implementation group: 'org.slf4j', name: 'slf4j-api'
|
||||||
implementation group: 'nz.net.ultraq.thymeleaf', name: 'thymeleaf-layout-dialect'
|
implementation group: 'nz.net.ultraq.thymeleaf', name: 'thymeleaf-layout-dialect'
|
||||||
implementation group: 'org.thymeleaf.extras', name: 'thymeleaf-extras-springsecurity6'
|
implementation group: 'org.thymeleaf.extras', name: 'thymeleaf-extras-springsecurity6'
|
||||||
|
47
src/main/java/ru/ulstu/http/HttpService.java
Normal file
47
src/main/java/ru/ulstu/http/HttpService.java
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package ru.ulstu.http;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.web.reactive.function.client.WebClient;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
import ru.ulstu.method.fuzzy.FuzzyRuleDataDto;
|
||||||
|
import ru.ulstu.method.fuzzy.OutputValue;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class HttpService {
|
||||||
|
private final WebClient client = getWebClient();
|
||||||
|
private final static String USER_NAME = "admin";
|
||||||
|
private final static String PASSWORD = "admin";
|
||||||
|
|
||||||
|
public WebClient getWebClient() {
|
||||||
|
return WebClient.builder()
|
||||||
|
.defaultHeader(HttpHeaders.AUTHORIZATION, "Basic " +
|
||||||
|
Base64.getEncoder().encodeToString((USER_NAME + ":" + PASSWORD).getBytes()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<OutputValue> post(String url, FuzzyRuleDataDto requestBody) {
|
||||||
|
List<OutputValue> result = new ArrayList<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
OutputValue[] res = client
|
||||||
|
.post()
|
||||||
|
.uri(url)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.body(Mono.just(requestBody), FuzzyRuleDataDto.class)
|
||||||
|
.retrieve()
|
||||||
|
.bodyToMono(OutputValue[].class)
|
||||||
|
.block();
|
||||||
|
if (res != null && res.length > 0) {
|
||||||
|
result = Arrays.stream(res).toList();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
33
src/main/java/ru/ulstu/method/fuzzy/FuzzyRuleDataDto.java
Normal file
33
src/main/java/ru/ulstu/method/fuzzy/FuzzyRuleDataDto.java
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package ru.ulstu.method.fuzzy;
|
||||||
|
|
||||||
|
public class FuzzyRuleDataDto {
|
||||||
|
private String[] fuzzyTerms;
|
||||||
|
private int window = 3;
|
||||||
|
private int horizon = 1;
|
||||||
|
|
||||||
|
public FuzzyRuleDataDto(String[] fuzzyTimeSeries, int window, int horizon) {
|
||||||
|
this.fuzzyTerms = fuzzyTimeSeries;
|
||||||
|
this.window = window;
|
||||||
|
this.horizon = horizon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getFuzzyTerms() {
|
||||||
|
return fuzzyTerms;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFuzzyTerms(String[] fuzzyTerms) {
|
||||||
|
this.fuzzyTerms = fuzzyTerms;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWindow() {
|
||||||
|
return window;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWindow(int window) {
|
||||||
|
this.window = window;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHorizon() {
|
||||||
|
return horizon;
|
||||||
|
}
|
||||||
|
}
|
37
src/main/java/ru/ulstu/method/fuzzy/OutputValue.java
Normal file
37
src/main/java/ru/ulstu/method/fuzzy/OutputValue.java
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package ru.ulstu.method.fuzzy;
|
||||||
|
|
||||||
|
public class OutputValue {
|
||||||
|
private String variable;
|
||||||
|
private String fuzzyTerm;
|
||||||
|
private Double degree;
|
||||||
|
|
||||||
|
public OutputValue(String variable, String fuzzyTerm, Double degree) {
|
||||||
|
this.variable = variable;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVariable() {
|
||||||
|
return variable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVariable(String variable) {
|
||||||
|
this.variable = variable;
|
||||||
|
}
|
||||||
|
}
|
99
src/main/java/ru/ulstu/method/fuzzy/PlainFuzzy.java
Normal file
99
src/main/java/ru/ulstu/method/fuzzy/PlainFuzzy.java
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
package ru.ulstu.method.fuzzy;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import ru.ulstu.datamodel.Model;
|
||||||
|
import ru.ulstu.datamodel.exception.ModelingException;
|
||||||
|
import ru.ulstu.datamodel.ts.TimeSeries;
|
||||||
|
import ru.ulstu.datamodel.ts.TimeSeriesValue;
|
||||||
|
import ru.ulstu.http.HttpService;
|
||||||
|
import ru.ulstu.method.Method;
|
||||||
|
import ru.ulstu.method.MethodParamValue;
|
||||||
|
import ru.ulstu.method.MethodParameter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.DoubleSummaryStatistics;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class PlainFuzzy extends Method {
|
||||||
|
private final HttpService httpService = new HttpService();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<MethodParameter> getAvailableParameters() {
|
||||||
|
return PlainFuzzyModel.getAvailableParameters();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Model getModelOfValidTimeSeries(TimeSeries timeSeries, List<MethodParamValue> parameters) {
|
||||||
|
PlainFuzzyModel model = new PlainFuzzyModel(timeSeries, parameters);
|
||||||
|
List<Triangle> fuzzySets = generateFuzzySets(timeSeries, model.getNumberOfFuzzyTerms().getIntValue());
|
||||||
|
model.setFuzzySets(fuzzySets);
|
||||||
|
for (TimeSeriesValue tsVal : timeSeries.getValues()) {
|
||||||
|
model.getTimeSeriesModel().addValue(new TimeSeriesValue(
|
||||||
|
tsVal.getDate(),
|
||||||
|
getMaxMembership(fuzzySets, tsVal).getTop()));
|
||||||
|
model.getFuzzyTimeSeries().add(getMaxMembership(fuzzySets, tsVal));
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Triangle> generateFuzzySets(TimeSeries timeSeries, Integer numberOfFuzzyTerms) {
|
||||||
|
// Universum
|
||||||
|
DoubleSummaryStatistics stat = timeSeries.getValues()
|
||||||
|
.stream()
|
||||||
|
.mapToDouble(TimeSeriesValue::getValue)
|
||||||
|
.summaryStatistics();
|
||||||
|
double min = stat.getMin();
|
||||||
|
double max = stat.getMax();
|
||||||
|
|
||||||
|
// Generate fuzzy sets
|
||||||
|
List<Triangle> fuzzySets = new ArrayList<>();
|
||||||
|
double delta = ((max - min) / (numberOfFuzzyTerms - 1));
|
||||||
|
|
||||||
|
for (int i = 0; i < numberOfFuzzyTerms; i++) {
|
||||||
|
fuzzySets.add(new Triangle(min + i * delta - delta,
|
||||||
|
min + i * delta,
|
||||||
|
min + i * delta + delta, i));
|
||||||
|
}
|
||||||
|
return fuzzySets;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Triangle getMaxMembership(List<Triangle> fuzzySets, TimeSeriesValue tsVal) {
|
||||||
|
Triangle maxTriangle = fuzzySets.get(0);
|
||||||
|
double membersip = 0;
|
||||||
|
for (Triangle triangle : fuzzySets) {
|
||||||
|
if (membersip < triangle.getValueAtPoint(tsVal.getValue())) {
|
||||||
|
maxTriangle = triangle;
|
||||||
|
membersip = triangle.getValueAtPoint(tsVal.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maxTriangle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TimeSeries getForecastWithValidParams(Model model, TimeSeries forecast) throws ModelingException {
|
||||||
|
PlainFuzzyModel pfm = ((PlainFuzzyModel) model);
|
||||||
|
List<String> fuzzyTimeSeries = pfm.getFuzzyTimeSeries().stream().map(Triangle::getLabel).toList();
|
||||||
|
List<OutputValue> result = httpService.post("http://plans.athene.tech/inferenceRest/get-inference-by-generated-rules",
|
||||||
|
new FuzzyRuleDataDto(fuzzyTimeSeries.toArray(String[]::new), 2, forecast.getLength()));
|
||||||
|
List<Double> forecastValues = result.stream().map(r -> pfm.getTopValueByLabel(r.getFuzzyTerm())).toList();
|
||||||
|
List<TimeSeriesValue> values = forecast.getValues();
|
||||||
|
for (int i = 0; i < values.size(); i++) {
|
||||||
|
if (forecastValues.isEmpty()) {
|
||||||
|
values.get(i).setValue(pfm.getTimeSeriesModel().getValues().getLast().getValue());
|
||||||
|
} else {
|
||||||
|
if (forecastValues.size() > i) {
|
||||||
|
values.get(i).setValue(forecastValues.get(i));
|
||||||
|
} else {
|
||||||
|
values.get(i).setValue(forecastValues.getLast());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return forecast;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Нечеткая модель";
|
||||||
|
}
|
||||||
|
}
|
55
src/main/java/ru/ulstu/method/fuzzy/PlainFuzzyModel.java
Normal file
55
src/main/java/ru/ulstu/method/fuzzy/PlainFuzzyModel.java
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package ru.ulstu.method.fuzzy;
|
||||||
|
|
||||||
|
import ru.ulstu.datamodel.Model;
|
||||||
|
import ru.ulstu.datamodel.ts.TimeSeries;
|
||||||
|
import ru.ulstu.method.MethodParamValue;
|
||||||
|
import ru.ulstu.method.MethodParameter;
|
||||||
|
import ru.ulstu.method.fuzzy.parameter.NumberOfFuzzyTerms;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PlainFuzzyModel extends Model {
|
||||||
|
private final MethodParamValue numberOfFuzzyTerms = new MethodParamValue(NumberOfFuzzyTerms.getInstance(), 2);
|
||||||
|
private final List<Triangle> fuzzyTimeSeries = new ArrayList<>();
|
||||||
|
private List<Triangle> fuzzySets = new ArrayList<>();
|
||||||
|
|
||||||
|
protected PlainFuzzyModel(TimeSeries ts, List<MethodParamValue> parameters) {
|
||||||
|
super(ts);
|
||||||
|
for (MethodParamValue parameter : parameters) {
|
||||||
|
if (parameter.getParameter() instanceof NumberOfFuzzyTerms) {
|
||||||
|
numberOfFuzzyTerms.setValue(parameter.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<MethodParameter> getAvailableParameters() {
|
||||||
|
return Collections.singletonList(NumberOfFuzzyTerms.getInstance());
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodParamValue getNumberOfFuzzyTerms() {
|
||||||
|
return numberOfFuzzyTerms;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Triangle> getFuzzyTimeSeries() {
|
||||||
|
return fuzzyTimeSeries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFuzzySets(List<Triangle> fuzzySets) {
|
||||||
|
this.fuzzySets = fuzzySets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Triangle> getFuzzySets() {
|
||||||
|
return fuzzySets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getTopValueByLabel(String label) {
|
||||||
|
return fuzzySets
|
||||||
|
.stream()
|
||||||
|
.filter(fs -> fs.getLabel().equals(label))
|
||||||
|
.findAny()
|
||||||
|
.orElseThrow(() -> new RuntimeException("Неизвестная нечеткая метка"))
|
||||||
|
.getTop();
|
||||||
|
}
|
||||||
|
}
|
58
src/main/java/ru/ulstu/method/fuzzy/Triangle.java
Normal file
58
src/main/java/ru/ulstu/method/fuzzy/Triangle.java
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package ru.ulstu.method.fuzzy;
|
||||||
|
|
||||||
|
public class Triangle {
|
||||||
|
private final static String FUZZY_LABEL_TEMPLATE = "X%s";
|
||||||
|
private String label;
|
||||||
|
private double start; // левая граница треугольника
|
||||||
|
private double end; // правая граница треугольника
|
||||||
|
private double top; // вершина треугольника
|
||||||
|
|
||||||
|
public double getStart() {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Triangle(double start, double top, double end, int number) {
|
||||||
|
this.start = start;
|
||||||
|
this.top = top;
|
||||||
|
this.end = end;
|
||||||
|
this.label = String.format(FUZZY_LABEL_TEMPLATE, number);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStart(int start) {
|
||||||
|
this.start = start;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public double getEnd() {
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnd(int end) {
|
||||||
|
this.end = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getTop() {
|
||||||
|
return top;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTop(int top) {
|
||||||
|
this.top = top;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getValueAtPoint(double crispValue) {
|
||||||
|
if (crispValue == this.getTop()) {
|
||||||
|
return 1;
|
||||||
|
} else if ((crispValue >= this.getEnd()) || (crispValue <= this.getStart())) {
|
||||||
|
return 0;
|
||||||
|
} else if (crispValue < this.getTop()) {
|
||||||
|
return (crispValue - this.getStart()) / (this.getTop() - this.getStart());
|
||||||
|
} else if (crispValue > this.getTop()) {
|
||||||
|
return -(crispValue - this.getEnd()) / (this.getEnd() - this.getTop());
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLabel() {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package ru.ulstu.method.fuzzy.parameter;
|
||||||
|
|
||||||
|
import ru.ulstu.datamodel.ts.TimeSeries;
|
||||||
|
import ru.ulstu.method.MethodParameter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class NumberOfFuzzyTerms extends MethodParameter {
|
||||||
|
private final static int MIN_NUMBER_OF_FUZZY_TERMS = 2;
|
||||||
|
private final static int MIN_INCREASING_STEP_OF_NUMBER_OF_FUZZY_TERMS = 1;
|
||||||
|
private final static int MAX_NUMBER_OF_FUZZY_TERMS = 7;
|
||||||
|
|
||||||
|
public NumberOfFuzzyTerms() {
|
||||||
|
super("Number of fuzzy terms");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NumberOfFuzzyTerms getInstance() {
|
||||||
|
return new NumberOfFuzzyTerms();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Number> getAvailableValues(TimeSeries timeSeries) {
|
||||||
|
List<Number> values = new ArrayList<>();
|
||||||
|
for (double i = MIN_NUMBER_OF_FUZZY_TERMS;
|
||||||
|
i <= MAX_NUMBER_OF_FUZZY_TERMS;
|
||||||
|
i += MIN_INCREASING_STEP_OF_NUMBER_OF_FUZZY_TERMS) {
|
||||||
|
values.add(i);
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user