package de.wwwu.awolf.presenter.evaluation; import de.wwwu.awolf.model.Line; import de.wwwu.awolf.model.LineModel; import de.wwwu.awolf.model.Point; import de.wwwu.awolf.model.communication.Data; import de.wwwu.awolf.model.communication.EvaluationData; import de.wwwu.awolf.model.communication.SubscriberType; import de.wwwu.awolf.presenter.Presenter; import de.wwwu.awolf.presenter.algorithms.Algorithm; import de.wwwu.awolf.presenter.algorithms.advanced.LeastMedianOfSquaresEstimator; import de.wwwu.awolf.presenter.algorithms.advanced.RepeatedMedianEstimator; import de.wwwu.awolf.presenter.algorithms.advanced.TheilSenEstimator; import de.wwwu.awolf.presenter.algorithms.naiv.NaivLeastMedianOfSquaresEstimator; import de.wwwu.awolf.presenter.algorithms.naiv.NaivRepeatedMedianEstimator; import de.wwwu.awolf.presenter.algorithms.naiv.NaivTheilSenEstimator; import de.wwwu.awolf.presenter.generator.DatasetGenerator; import de.wwwu.awolf.presenter.io.DataImporter; import de.wwwu.awolf.presenter.util.IntersectionComputer; import de.wwwu.awolf.presenter.util.Logging; import java.io.File; import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Flow; /** * Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden. * * @Author: Armin Wolf * @Email: a_wolf28@uni-muenster.de * @Date: 01.08.2017. */ public class EvaluateAlgorithms implements Flow.Publisher { private LineModel arrangement; private List lmsL; private List rmL; private List tsL; private List lmsP; private List tsP; private Thread lmsThread; private Thread rmThread; private Thread tsThread; private DatasetGenerator generator; private String[][] names = {{"MSE", "RMSE", "MAE", "MDAE", "Steigung", "y-Achsenabschnitt", "S-MSE", "S-RMSE", "S-MAE", "S-MDAE", "Brute-force Steigung", "Brute-force y-Achsenabschnitt"}, {"MAPE", "MDAPE", "RMSPE", "RMDSPE", "Steigung", "y-Achsenabschnitt"}}; //übergebene Parameter private int type; private int iterations; private int alg; private Flow.Subscriber subscriber; /** * Konstruktor zur evaluation * * @param type Typ der evaluation * @param n Größe des Datensatzes * @param alg 0 = lms, * 1 = rm, * 2 = ts, * 3 = lms, rm, * 4 = lms, ts, * 5 = rm, ts, * 6 = lms, rm, ts, * @param datasettyp typ der zu generierenden Datensatz */ public EvaluateAlgorithms(int type, int n, int alg, String datasettyp, Presenter presenter) { subscribe(presenter); this.arrangement = new LineModel(); generator = new DatasetGenerator(presenter); switch (datasettyp) { case "Punktwolke": arrangement.setLines(generator.generateDataCloud(n)); break; case "Gerade": arrangement.setLines(generator.generateDataLines(n)); break; case "Kreis und Gerade": arrangement.setLines(generator.generateCircle(n)); break; } this.type = type; this.iterations = n; this.alg = alg; IntersectionComputer computer = new IntersectionComputer(arrangement.getLines()); arrangement.setNodes(computer.compute()); lmsL = new LinkedList<>(arrangement.getLines()); rmL = new LinkedList<>(arrangement.getLines()); tsL = new LinkedList<>(arrangement.getLines()); lmsP = new ArrayList<>(arrangement.getNodes()); tsP = new ArrayList<>(arrangement.getNodes()); } /** * Konstruktor zur evaluation * * @param type Typ der evaluation * @param alg 0 = lms, * 1 = rm, * 2 = ts, * 3 = lms, rm, * 4 = lms, ts, * 5 = rm, ts, * 6 = lms, rm, ts, * @param file Datei die importiert werden soll */ public EvaluateAlgorithms(int type, int alg, File file) { this.arrangement = new LineModel(); DataImporter importer = new DataImporter(file, this.subscriber); List importedLines = importer.run(); if (importedLines != null) arrangement.setLines(importedLines); this.type = type; this.alg = alg; IntersectionComputer computer = new IntersectionComputer(arrangement.getLines()); arrangement.setNodes(computer.compute()); lmsL = new LinkedList<>(arrangement.getLines()); rmL = new LinkedList<>(arrangement.getLines()); tsL = new LinkedList<>(arrangement.getLines()); lmsP = new ArrayList<>(arrangement.getNodes()); tsP = new ArrayList<>(arrangement.getNodes()); } /** * Startet die Evaluation zu den passenden Typ. Bei beendigung wird der Beobachter informiert. * * @throws InterruptedException */ public void run() { List result; List> multipleResults = new ArrayList<>(); ExecutorService executorService = Executors.newCachedThreadPool(); switch (type) { case 0: //der alg der gewählt wurde if (alg == 0) { final Line naivEstimator = new Line(0,0); final Line advancedEstimator = new Line(0,0); executorService.submit(() -> { NaivLeastMedianOfSquaresEstimator l = new NaivLeastMedianOfSquaresEstimator(arrangement.getLines()); l.run(); naivEstimator.setM(l.getM()); naivEstimator.setB(l.getB()); }); executorService.submit(() -> { LeastMedianOfSquaresEstimator lmsAlg = new LeastMedianOfSquaresEstimator(lmsL, lmsP); lmsAlg.run(); lmsAlg.pepareResult(); advancedEstimator.setM(lmsAlg.getSlope()); advancedEstimator.setB(lmsAlg.getyInterception()); }); result = getScaleDependentMeasure(arrangement.getLines(), advancedEstimator.getM(), advancedEstimator.getB()); result.addAll(getScaledErrorBasedMeasure(arrangement.getLines(), advancedEstimator.getM(), advancedEstimator.getB(), naivEstimator.getM(), naivEstimator.getB())); Double[] tmp = {advancedEstimator.getM(), advancedEstimator.getB(), naivEstimator.getM(), naivEstimator.getB()}; sendPlotLineResults(Arrays.asList(tmp), Algorithm.Type.LMS); } else if (alg == 1) { final double[] m = new double[1]; final double[] b = new double[1]; Thread t = new Thread(() -> { NaivRepeatedMedianEstimator r = new NaivRepeatedMedianEstimator(arrangement.getLines()); r.run(); m[0] = r.getM(); b[0] = r.getB(); }); t.start(); try { startRM(); } catch (InterruptedException e) { Logging.logError(e.getMessage(), e); Thread.currentThread().interrupt(); } result = getScaleDependentMeasure(arrangement.getLines(), Double.valueOf(rmRes[0]), Double.valueOf(rmRes[1])); result.addAll(getScaledErrorBasedMeasure(arrangement.getLines(), Double.valueOf(rmRes[0]), Double.valueOf(rmRes[1]), m[0], b[0])); Double[] tmp = {Double.valueOf(rmRes[0]), Double.valueOf(rmRes[1]), m[0], b[0]}; sendPlotLineResults(Arrays.asList(tmp), Algorithm.Type.NAIV_RM); } else { final double[] m = new double[1]; final double[] b = new double[1]; Thread t = new Thread(() -> { NaivTheilSenEstimator ts = new NaivTheilSenEstimator(arrangement.getLines()); ts.run(); m[0] = ts.getM(); b[0] = ts.getB(); }); t.start(); try { startTS(); } catch (InterruptedException e) { Logging.logError(e.getMessage(), e); Thread.currentThread().interrupt(); } result = getScaleDependentMeasure(arrangement.getLines(), Double.valueOf(tsRes[0]), Double.valueOf(tsRes[1])); result.addAll(getScaledErrorBasedMeasure(arrangement.getLines(), Double.valueOf(tsRes[0]), Double.valueOf(tsRes[1]), m[0], b[0])); Double[] tmp = { Double.valueOf(tsRes[0]), Double.valueOf(tsRes[1]), m[0], b[0]}; sendPlotLineResults(Arrays.asList(tmp), Algorithm.Type.NAIV_TS); } sendTableApproximationTypes(); sendTableApproximationData(result, alg); break; case 1: List> lineRes; switch (alg) { case 3: try { startLMS(); startRM(); } catch (InterruptedException e) { Logging.logError(e.getMessage(), e); Thread.currentThread().interrupt(); } result = getPercentigeErrorBasedMeasure(arrangement.getLines(), lmsRes[0], lmsRes[1]); multipleResults.add(result); result = getPercentigeErrorBasedMeasure(arrangement.getLines(), rmRes[0], rmRes[1]); multipleResults.add(result); result = fillPseudoResults(); multipleResults.add(result); lineRes = new ArrayList<>(); lineRes.add(Arrays.asList(lmsRes)); lineRes.add(Arrays.asList(rmRes)); sendPloteLineResults(lineRes, Arrays.asList(Algorithm.Type.LMS, Algorithm.Type.RM)); break; case 4: try { startLMS(); startTS(); } catch (InterruptedException e) { Logging.logError(e.getMessage(), e); Thread.currentThread().interrupt(); } result = getPercentigeErrorBasedMeasure(arrangement.getLines(), lmsRes[0], lmsRes[1]); multipleResults.add(result); result = fillPseudoResults(); multipleResults.add(result); result = getPercentigeErrorBasedMeasure(arrangement.getLines(), tsRes[0], tsRes[1]); multipleResults.add(result); lineRes = new ArrayList<>(); lineRes.add(Arrays.asList(lmsRes)); lineRes.add(Arrays.asList(tsRes)); sendPloteLineResults(lineRes, Arrays.asList(Algorithm.Type.LMS, Algorithm.Type.TS)); break; case 5: try { startRM(); startTS(); } catch (InterruptedException e) { Logging.logError(e.getMessage(), e); Thread.currentThread().interrupt(); } result = fillPseudoResults(); multipleResults.add(result); result = getPercentigeErrorBasedMeasure(arrangement.getLines(), rmRes[0], rmRes[1]); multipleResults.add(result); result = getPercentigeErrorBasedMeasure(arrangement.getLines(), tsRes[0], tsRes[1]); multipleResults.add(result); lineRes = new ArrayList<>(); lineRes.add(Arrays.asList(rmRes)); lineRes.add(Arrays.asList(tsRes)); sendPloteLineResults(lineRes, Arrays.asList(Algorithm.Type.RM, Algorithm.Type.TS)); break; case 6: try { startLMS(); startRM(); startTS(); } catch (InterruptedException e) { Logging.logError(e.getMessage(), e); Thread.currentThread().interrupt(); } result = getPercentigeErrorBasedMeasure(arrangement.getLines(), lmsRes[0], lmsRes[1]); multipleResults.add(result); result = getPercentigeErrorBasedMeasure(arrangement.getLines(), rmRes[0], rmRes[1]); multipleResults.add(result); result = getPercentigeErrorBasedMeasure(arrangement.getLines(), tsRes[0], tsRes[1]); multipleResults.add(result); lineRes = new ArrayList<>(); lineRes.add(Arrays.asList(lmsRes)); lineRes.add(Arrays.asList(rmRes)); lineRes.add(Arrays.asList(tsRes)); sendPloteLineResults(lineRes, Arrays.asList(Algorithm.Type.LMS, Algorithm.Type.RM, Algorithm.Type.TS)); break; } sendTableApproximationData(multipleResults); break; } } /** * Die berechneten Ergebnisse werden an den Beobachter übermittelt um dann visualisiert zu werden. * * @param result Ergebnisse * @param col Spalte */ public void sendTableApproximationData(List result, int col) { List tableInput = new ArrayList<>(); for (int i = 0; i < names[type].length; i++) { tableInput.add(result.get(i)); } tableInput.add(""); EvaluationData data = new EvaluationData(); data.setColumn(col); data.setLabels(tableInput); tableInput.clear(); } /** * Die berechneten Ergebnisse werden an den Beobachter übermittelt um dann visualisiert zu werden. * * @param result Ergebnisse */ public void sendTableApproximationData(List> result) { List tableInput = new ArrayList<>(); //TODO Hääää? xD //iteration über die ApproximationsGüten -- Zeilen for (int j = 0; j <= result.get(0).size(); j++) { tableInput.add("eval-ds"); if (j != result.get(0).size()) { tableInput.add(names[type][j]); //iteration über die alg. -- Spalten for (int i = 0; i < 3; i++) { tableInput.add(result.get(i).get(j)); } } else { tableInput.add(""); tableInput.add(""); tableInput.add(""); tableInput.add(""); } EvaluationData data = new EvaluationData(); data.setType(SubscriberType.EVAL_DS); data.setMultipleColumnResult(result); this.subscriber.onNext(data); tableInput.clear(); } } /** * Die Art der Ergebnisse (MSE, RMSE,...) wird an der Beobachter übermittelt. */ public void sendTableApproximationTypes() { EvaluationData data = new EvaluationData(); data.setType(SubscriberType.EVAL_T); data.setLabels(Arrays.asList(names[type])); this.subscriber.onNext(data); } /** * Zur visualisierung der berechneten Geraden wird die Steigung und der y-Achsenabschnitt an den * Beobachter übermittelt. * * @param res Feld mit den Werten für die Steigung und dern y-Achsenabschnitt * @param alg code für welchen Algorithmus sich die Werte beziehen */ public void sendPlotLineResults(List res, Algorithm.Type alg) { EvaluationData data = new EvaluationData(); data.setType(SubscriberType.LINES_RES); data.setAlgorithmtypes(Collections.singletonList(alg)); data.setOneColumnresult(res); this.subscriber.onNext(data); } /** * Zur visualisierung der berechneten Geraden wird die Steigung und der y-Achsenabschnitt an den * Beobachter übermittelt. * * @param res Feld mit den Werten für die Steigung und dern y-Achsenabschnitt (alle) * @param algs codes für welchen Algorithmus sich die Werte beziehen (alle) */ public void sendPloteLineResults(List> res, List algs) { EvaluationData data = new EvaluationData(); data.setType(SubscriberType.LINES_RES_MULT); data.setAlgorithmtypes(algs); data.setMultipleColumnResult(res); this.subscriber.onNext(data); } /** * Startet die Berechnung des Alg. zum LMS-Schätzer * * @throws InterruptedException */ public void startLMS() throws InterruptedException { lmsThread = new Thread(() -> { LeastMedianOfSquaresEstimator lmsAlg = new LeastMedianOfSquaresEstimator(lmsL, lmsP); lmsAlg.run(); lmsAlg.pepareResult(); lmsRes[0] = lmsAlg.getSlope(); lmsRes[1] = lmsAlg.getyInterception(); }); lmsThread.start(); lmsThread.join(); } /** * Startet die Berechnung des Alg. zum RM-Schätzer * * @throws InterruptedException */ public void startRM() throws InterruptedException { rmThread = new Thread(() -> { RepeatedMedianEstimator rmAlg = new RepeatedMedianEstimator(rmL); rmAlg.run(); rmAlg.pepareResult(); rmRes[0] = rmAlg.getSlope(); rmRes[1] = rmAlg.getyInterception(); }); rmThread.start(); rmThread.join(); } /** * Startet die Berechnung des Alg. zum TS-Schätzer * * @throws InterruptedException */ public void startTS() throws InterruptedException { tsThread = new Thread(() -> { TheilSenEstimator tsAlg = new TheilSenEstimator(tsL, tsP); tsAlg.run(); tsAlg.pepareResult(); tsRes[0] = tsAlg.getSlope(); tsRes[1] = tsAlg.getyInterception(); }); tsThread.start(); tsThread.join(); } /** * Startet die Berechnung der skalierungsabbhängigen Maße. * * @param lines Liste der Geraden * @param m Steigung * @param b y-Achsenabschnitt * @return Liste mit den Ergebnissen, bereit zum visualisieren */ public List getScaleDependentMeasure(final List lines, final Double m, final Double b) { ScaleDependentMeasure scaleDependentMeasure = new ScaleDependentMeasure(lines, m, b); List ret = new ArrayList<>(); ret.add(scaleDependentMeasure.mse().toString()); ret.add(scaleDependentMeasure.rmse().toString()); ret.add(scaleDependentMeasure.mae().toString()); ret.add(scaleDependentMeasure.mdae().toString()); ret.add(m.toString()); ret.add(b.toString()); return ret; } /** * Startet die Berechnung der Maße die auf dem prozentualen Fehler basieren. * * @param lines Liste der Geraden * @param m Steigung * @param b y-Achsenabschnitt * @return Liste mit den Ergebnissen, bereit zum visualisieren */ public List getPercentigeErrorBasedMeasure(final List lines, final Double m, final Double b) { PercentageErrorBasedMeasure percentageErrorBasedMeasure = new PercentageErrorBasedMeasure(lines, m, b); ArrayList ret = new ArrayList<>(); ret.add(percentageErrorBasedMeasure.mape().toString()); ret.add(percentageErrorBasedMeasure.mdape().toString()); ret.add(percentageErrorBasedMeasure.rmspe().toString()); ret.add(percentageErrorBasedMeasure.rmdspe().toString()); ret.add(m.toString()); ret.add(b.toString()); return ret; } /** * Startet die Berechnung der skalierungsunabbhängigen Maße. * * @param lines Liste der Geraden * @param m Steigung * @param b y-Achsenabschnitt * @return Liste mit den Ergebnissen, bereit zum visualisieren */ public List getScaledErrorBasedMeasure(final List lines, final Double m, final Double b, final Double nM, final Double nB) { ScaledErrorBasedMeasure scaledErrorBasedMeasure = new ScaledErrorBasedMeasure(lines, m, b, nM, nB); List ret = new ArrayList<>(); ret.add(scaledErrorBasedMeasure.mse().toString()); ret.add(scaledErrorBasedMeasure.rmse().toString()); ret.add(scaledErrorBasedMeasure.mae().toString()); ret.add(scaledErrorBasedMeasure.mdae().toString()); ret.add(nM.toString()); ret.add(nB.toString()); return ret; } /** * Damit es bei der Visualisierung trennende Zeilen gibt. * * @return */ private List fillPseudoResults() { List result = new ArrayList<>(); result.add(" "); result.add(" "); result.add(" "); result.add(" "); result.add(" "); result.add(" "); return result; } /** * @return Liste der Geraden auf der die Berechnungen ausgeführt wurden */ public List getData() { return arrangement.getLines(); } @Override public void subscribe(Flow.Subscriber subscriber) { this.subscriber = subscriber; } }