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-thymeleaf'
|
||||
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: 'nz.net.ultraq.thymeleaf', name: 'thymeleaf-layout-dialect'
|
||||
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