Free cookie consent management tool by TermsFeed Policy Generator

source: branches/Sliding Window GP/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SlidingWindow/SlidingWindowBestSolutionsCollection.cs @ 10720

Last change on this file since 10720 was 10720, checked in by bburlacu, 10 years ago

#1837: Moved quality calculation inside the SlidingWindowBestSolutionsCollection so that data can be cached across views. Added background worker for calculating qualities for each solution on all intervals. Added method for updating the qualities on the fly during the run (without recalculating). Added progress indicator for views.

File size: 11.3 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2013 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.ComponentModel;
25using System.Linq;
26using HeuristicLab.Common;
27using HeuristicLab.Core;
28using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
29using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
30
31namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
32  [StorableClass]
33  [Item("SlidingWindowBestSolutionsCollection", "An object holding a collection of the best sliding window solutions.")]
34  public abstract class SlidingWindowBestSolutionsCollection : Item {
35    [Storable]
36    private List<SlidingWindowRange> slidingWindowRanges;
37    public List<SlidingWindowRange> SlidingWindowRanges {
38      get { return slidingWindowRanges; }
39      private set { slidingWindowRanges = value; }
40    }
41
42    [Storable(AllowOneWay = true, Name = "bestSolutions")]
43    private Dictionary<Tuple<int, int>, ISymbolicExpressionTree> StorableBestSolutions {
44      set {
45        var bestSolutions = value;
46        var ranges = bestSolutions.Keys.OrderBy(x => x.Item1).ToList();
47        slidingWindowRanges = ranges.Select(x => new SlidingWindowRange(x.Item1, x.Item2)).ToList();
48        slidingWindowBestSolutions = new Dictionary<SlidingWindowRange, ISymbolicExpressionTree>();
49        for (int i = 0; i < slidingWindowRanges.Count; ++i) {
50          slidingWindowBestSolutions.Add(slidingWindowRanges[i], bestSolutions[ranges[i]]);
51        }
52      }
53    }
54
55    [Storable]
56    private Dictionary<SlidingWindowRange, ISymbolicExpressionTree> slidingWindowBestSolutions;
57    public Dictionary<SlidingWindowRange, ISymbolicExpressionTree> SlidingWindowBestSolutions {
58      get { return slidingWindowBestSolutions; }
59      set { slidingWindowBestSolutions = value; }
60    }
61
62    [Storable]
63    private IDataAnalysisProblemData problemData;
64    public IDataAnalysisProblemData ProblemData {
65      get { return problemData; }
66      set { problemData = value; }
67    }
68
69    [Storable]
70    private ISymbolicDataAnalysisExpressionTreeInterpreter interpreter;
71    public ISymbolicDataAnalysisExpressionTreeInterpreter Interpreter {
72      get { return interpreter; }
73      set { interpreter = value; }
74    }
75
76    [Storable]
77    private bool applyLinearScaling;
78    public bool ApplyLinearScaling {
79      get { return applyLinearScaling; }
80      set { applyLinearScaling = value; }
81    }
82
83    [StorableHook(HookType.AfterDeserialization)]
84    private void AfterDeserialization() {
85      if (bw == null) {
86        bw = new BackgroundWorker();
87        bw.WorkerSupportsCancellation = true;
88        bw.WorkerReportsProgress = true;
89        bw.DoWork += CalculateQualities;
90      }
91    }
92
93    public double[,] SlidingWindowQualities { get; set; }
94
95    private BackgroundWorker bw;
96
97    public enum QualityMeasures { PEARSON, MSE };
98
99    private QualityMeasures qualityMeasure;
100    public QualityMeasures QualityMeasure {
101      get { return qualityMeasure; }
102      set {
103        if (qualityMeasure != value) {
104          qualityMeasure = value;
105          CalculateQualities();
106        }
107      }
108    }
109
110    public bool QualitiesCalculationInProgress {
111      get { return bw.IsBusy; }
112    }
113
114    public event ProgressChangedEventHandler QualitiesCalculationProgress {
115      add { bw.ProgressChanged += value; }
116      remove { bw.ProgressChanged -= value; }
117    }
118
119    public event RunWorkerCompletedEventHandler QualitiesCalculationCompleted {
120      add { bw.RunWorkerCompleted += value; }
121      remove { bw.RunWorkerCompleted -= value; }
122    }
123
124    public event EventHandler QualitiesUpdated;
125    private void OnQualitiesUpdated(object sender, EventArgs e) {
126      var updated = QualitiesUpdated;
127      if (updated != null) { updated(sender, e); }
128    }
129
130    [StorableConstructor]
131    protected SlidingWindowBestSolutionsCollection(bool deserializing) : base(deserializing) { }
132    protected SlidingWindowBestSolutionsCollection(SlidingWindowBestSolutionsCollection original, Cloner cloner)
133      : base(original, cloner) {
134      this.slidingWindowBestSolutions = original.slidingWindowBestSolutions;
135      this.problemData = original.problemData;
136      this.interpreter = original.interpreter;
137      this.applyLinearScaling = original.ApplyLinearScaling;
138    }
139    protected SlidingWindowBestSolutionsCollection() {
140      slidingWindowBestSolutions = new Dictionary<SlidingWindowRange, ISymbolicExpressionTree>();
141      slidingWindowRanges = new List<SlidingWindowRange>();
142      qualityMeasure = QualityMeasures.PEARSON;
143
144      bw = new BackgroundWorker();
145      bw.WorkerSupportsCancellation = true;
146      bw.WorkerReportsProgress = true;
147
148      bw.DoWork += CalculateQualities;
149    }
150
151    public bool ContainsKey(SlidingWindowRange key) {
152      return slidingWindowBestSolutions.ContainsKey(key);
153    }
154
155    public ISymbolicExpressionTree this[SlidingWindowRange key] {
156      get {
157        return slidingWindowBestSolutions[key];
158      }
159      set {
160        AddSolution(key, value); // this should be fast so there's no need for a background worker
161        OnQualitiesUpdated(this, EventArgs.Empty);
162      }
163    }
164
165    public void Add(SlidingWindowRange range, ISymbolicExpressionTree solution) {
166      if (!slidingWindowBestSolutions.ContainsKey(range)) {
167        slidingWindowBestSolutions.Add(range, solution);
168        slidingWindowRanges.Add(range);
169      } else {
170        slidingWindowBestSolutions[range] = solution;
171      }
172    }
173
174    public void Clear() {
175      if (slidingWindowBestSolutions != null) slidingWindowBestSolutions.Clear();
176      if (slidingWindowRanges != null) slidingWindowRanges.Clear();
177    }
178
179    public abstract ISymbolicDataAnalysisModel CreateModel(ISymbolicExpressionTree tree, ISymbolicDataAnalysisExpressionTreeInterpreter interpreter,
180     double lowerEstimationLimit = double.MinValue, double upperEstimationLimit = double.MaxValue);
181
182    public abstract ISymbolicDataAnalysisSolution CreateSolution(ISymbolicDataAnalysisModel model, IDataAnalysisProblemData problemData);
183
184    private void AddSolution(SlidingWindowRange range, ISymbolicExpressionTree solution) {
185      // add a solution and update the qualities matrix
186      Add(range, solution);
187
188      var solutions = slidingWindowRanges.Select(x => slidingWindowBestSolutions[x]).ToList();
189
190      var nRows = solutions.Count;
191      var nCols = nRows + 1; // an extra column corresponding to the whole trainig partition
192
193      var trainingIndices = problemData.TrainingIndices.ToList();
194      var matrix = new double[nRows, nCols];
195      List<int> rows;
196      // copy old qualities into the new matrix
197      for (int i = 0; i < nRows - 1; ++i) {
198        for (int j = 0; j < nCols - 1; ++j) {
199          matrix[i, j] = SlidingWindowQualities[i, j];
200        }
201      }
202      // copy qualities of new solution into the new matrix
203      for (int i = 0; i < nCols; ++i) {
204        rows = i == nCols - 1 ? trainingIndices : Enumerable.Range(slidingWindowRanges[i].Start, slidingWindowRanges[i].Size).ToList();
205        matrix[nRows - 1, i] = CalculateQuality(solution, rows);
206      }
207      // shift old training qualities one column to the right
208      rows = Enumerable.Range(range.Start, range.Size).ToList();
209      for (int i = 0; i < nRows; ++i) {
210        matrix[i, nCols - 1] = matrix[i, nCols - 2];
211        matrix[i, nCols - 2] = CalculateQuality(solutions[i], rows);
212      }
213      // replace old matrix with new matrix
214      SlidingWindowQualities = matrix;
215    }
216
217    private void CalculateQualities(object sender, DoWorkEventArgs e) {
218      var worker = sender as BackgroundWorker;
219      if (worker == null) return;
220      if (worker.CancellationPending) {
221        e.Cancel = true;
222        return;
223      }
224
225      var ranges = SlidingWindowRanges;
226      var solutions = ranges.Select(x => SlidingWindowBestSolutions[x]).ToList();
227
228      int rows = solutions.Count;
229      int columns = ranges.Count + 1;
230
231      SlidingWindowQualities = new double[rows, columns];
232
233      for (int i = 0; i < rows; ++i) {
234        if (worker.CancellationPending) {
235          e.Cancel = true;
236          return;
237        }
238
239        var solution = solutions[i];
240        for (int j = 0; j < columns; ++j) {
241          var range = (j == columns - 1) ? ProblemData.TrainingIndices : Enumerable.Range(ranges[j].Start, ranges[j].Size);
242          var q = CalculateQuality(solution, range);
243
244          SlidingWindowQualities[i, j] = q;
245        }
246
247        worker.ReportProgress((int)Math.Round(i * 100.0 / rows));
248      }
249    }
250
251    public void CalculateQualities() {
252      bw.RunWorkerAsync();
253    }
254
255    private string GetTargetVariable(IDataAnalysisProblemData problemData) {
256      var regressionProblemData = problemData as IRegressionProblemData;
257      var classificationProblemData = problemData as IClassificationProblemData;
258      if (regressionProblemData != null) return regressionProblemData.TargetVariable;
259      if (classificationProblemData != null) return classificationProblemData.TargetVariable;
260      throw new NotSupportedException();
261    }
262
263    private double CalculateQuality(ISymbolicExpressionTree tree, IEnumerable<int> rows) {
264      var estimatedValues = Interpreter.GetSymbolicExpressionTreeValues(tree, ProblemData.Dataset, rows);
265      var originalValues = ProblemData.Dataset.GetDoubleValues(GetTargetVariable(ProblemData), rows);
266      double quality = 0;
267      var errorState = new OnlineCalculatorError();
268      switch (QualityMeasure) {
269        case QualityMeasures.PEARSON:
270          quality = OnlinePearsonsRSquaredCalculator.Calculate(estimatedValues, originalValues, out errorState);
271          break;
272        case QualityMeasures.MSE:
273          quality = OnlineMeanSquaredErrorCalculator.Calculate(estimatedValues, originalValues, out errorState);
274          break;
275      }
276      return errorState == OnlineCalculatorError.None ? quality : double.NaN;
277    }
278  }
279
280  public class SlidingWindowRange : IEquatable<SlidingWindowRange> {
281    private readonly Tuple<int, int> tuple;
282
283    public int Start { get { return tuple.Item1; } }
284
285    public int End { get { return tuple.Item2; } }
286
287    public SlidingWindowRange(int start, int end) {
288      if (start > end) throw new ArgumentException("SlidingWindowRange: Start cannot be greater than End.");
289      tuple = new Tuple<int, int>(start, end);
290    }
291
292    public bool Equals(SlidingWindowRange other) {
293      return tuple.Equals(other.tuple);
294    }
295
296    public override int GetHashCode() {
297      return tuple.GetHashCode();
298    }
299
300    public int Size {
301      get { return End - Start; }
302    }
303  }
304}
Note: See TracBrowser for help on using the repository browser.