source: branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/GoalSeekingOptimizer.cs @ 14526

Last change on this file since 14526 was 14526, checked in by bburlacu, 4 years ago

#2679: Add analyzer for SingleObjectiveGoalSeekingProblem. Add result aggregation in the GoalSeekingOptimizer. Remove unused dataset parameter from the problems.

File size: 21.5 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2016 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.Drawing;
25using System.Linq;
26using HeuristicLab.Analysis;
27using HeuristicLab.Common;
28using HeuristicLab.Core;
29using HeuristicLab.Data;
30using HeuristicLab.Optimization;
31using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
32using HeuristicLab.Problems.DataAnalysis;
33
34namespace HeuristicLab.GoalSeeking {
35  internal enum GoalSeekingOptimizerAction { None, Prepare, Start, Stop, Pause }
36
37  [StorableClass]
38  [Item("GoalSeeking Optimizer", "A wrapper for the GoalSeekingProblem class to facilitate adding models and problem data.")]
39  [Creatable("Algorithms")]
40  public class GoalSeekingOptimizer : NamedItem, IOptimizer, IStorableContent {
41    [Storable]
42    private IGoalSeekingProblem problem;
43    public IGoalSeekingProblem Problem {
44      get { return problem; }
45      set {
46        if (value == null || value == problem)
47          return;
48
49        problem = value;
50        // the problem takes precedence over the algorithm. if the algorithm is unsuitable we remove it.
51        if (optimizer != null && !optimizer.ProblemType.IsInstanceOfType(problem)) {
52          optimizer = null;
53          OnAlgorithmChanged();
54        }
55        if (optimizer != null)
56          optimizer.Problem = problem;
57      }
58    }
59
60    [Storable]
61    private IAlgorithm optimizer;
62    public IAlgorithm Optimizer {
63      get { return optimizer; }
64      set {
65        if (value == null || value == optimizer)
66          return;
67
68        // make sure the algorithm is suitable for the problem.
69        if (problem != null && !value.ProblemType.IsInstanceOfType(problem))
70          throw new InvalidOperationException("The algorithm is incompatible with the problem.");
71
72        optimizer = value;
73
74        if (problem != null)
75          optimizer.Problem = problem;
76
77        RegisterOptimizerEvents();
78        OnAlgorithmChanged();
79      }
80    }
81
82    [Storable]
83    private IRegressionProblemData problemData;
84    public IRegressionProblemData ProblemData {
85      get { return problemData; }
86      set {
87        if (value == null || value == problemData)
88          return;
89
90        if (problem == null)
91          throw new InvalidOperationException("Cannot set problem data. Please configure a problem first.");
92
93        problemData = value;
94        problem.Configure(problemData, problemData.TrainingIndices.First());
95        OnProblemDataChanged();
96      }
97    }
98
99    [Storable]
100    private ResultCollection results;
101    public ResultCollection Results {
102      get { return results; }
103      private set {
104        if (value != null)
105          results = value;
106        OnResultsChanged();
107      }
108    }
109
110    private GoalSeekingOptimizerAction gsoAction;
111
112    public GoalSeekingOptimizer() {
113      Name = "Goal Seeking Optimizer";
114    }
115
116    [StorableConstructor]
117    protected GoalSeekingOptimizer(bool deserializing) : base(deserializing) { }
118
119
120    [StorableHook(HookType.AfterDeserialization)]
121    private void AfterDeserialization() {
122      if (optimizer != null)
123        RegisterOptimizerEvents();
124    }
125
126    protected GoalSeekingOptimizer(GoalSeekingOptimizer original, Cloner cloner) : base(original, cloner) {
127      this.Optimizer = (IAlgorithm)original.Optimizer.Clone(cloner);
128      this.Problem = (IGoalSeekingProblem)original.Problem.Clone(cloner);
129      this.ProblemData = (IRegressionProblemData)original.ProblemData.Clone(cloner);
130      this.Results = (ResultCollection)original.Results.Clone();
131    }
132
133    public override IDeepCloneable Clone(Cloner cloner) {
134      return new GoalSeekingOptimizer(this, cloner);
135    }
136
137    public EventHandler ProblemChanged;
138    private void OnProblemChanged() {
139      var changed = ProblemChanged;
140      if (changed != null)
141        changed(this, EventArgs.Empty);
142    }
143
144    public EventHandler AlgorithmChanged;
145    private void OnAlgorithmChanged() {
146      var changed = AlgorithmChanged;
147      if (changed != null)
148        changed(this, EventArgs.Empty);
149    }
150
151    public EventHandler ProblemDataChanged;
152    private void OnProblemDataChanged() {
153      var changed = ProblemDataChanged;
154      if (changed != null)
155        changed(this, EventArgs.Empty);
156    }
157
158    public EventHandler ResultsChanged;
159    private void OnResultsChanged() {
160      var changed = ResultsChanged;
161      if (changed != null)
162        changed(this, EventArgs.Empty);
163    }
164
165    private int calculatedRows;
166    private int currentRow;
167
168    public void Start() {
169      if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused))
170        throw new InvalidOperationException(string.Format("Start not allowed in execution state \"{0}\".", ExecutionState));
171      if (optimizer == null) return;
172      gsoAction = GoalSeekingOptimizerAction.Start;
173      if (Optimizer.ExecutionState == ExecutionState.Stopped) {
174        Optimizer.Prepare(clearRuns: true);
175        calculatedRows = 0;
176        currentRow = problemData.TrainingIndices.Skip(calculatedRows).First();
177        problem.Configure(problemData, currentRow);
178        optimizer.Prepare();
179      }
180      optimizer.Start();
181    }
182
183    public void Pause() {
184      if (ExecutionState != ExecutionState.Started)
185        throw new InvalidOperationException(string.Format("Pause not allowed in execution state \"{0}\".", ExecutionState));
186      if (optimizer == null) return;
187      gsoAction = GoalSeekingOptimizerAction.Pause;
188      if (optimizer.ExecutionState != ExecutionState.Started) return;
189      // a race-condition may occur when the algorithm has changed the state by itself in the meantime
190      try { optimizer.Pause(); }
191      catch (InvalidOperationException) { }
192    }
193
194    public void Stop() {
195      if ((ExecutionState != ExecutionState.Started) && (ExecutionState != ExecutionState.Paused))
196        throw new InvalidOperationException(string.Format("Stop not allowed in execution state \"{0}\".", ExecutionState));
197      if (optimizer == null) return;
198      gsoAction = GoalSeekingOptimizerAction.Stop;
199      if (optimizer.ExecutionState != ExecutionState.Started && optimizer.ExecutionState != ExecutionState.Paused) {
200        OnStopped();
201        return;
202      }
203      // a race-condition may occur when the algorithm has changed the state by itself in the meantime
204      try { optimizer.Stop(); }
205      catch (InvalidOperationException) { }
206    }
207
208    public void Prepare() {
209      Prepare(false);
210    }
211
212    public void Prepare(bool clearRuns) {
213      if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused) && (ExecutionState != ExecutionState.Stopped))
214        throw new InvalidOperationException(string.Format("Prepare not allowed in execution state \"{0}\".", ExecutionState));
215
216      if (Results != null)
217        Results.Clear();
218
219      if (optimizer != null) {
220        ExecutionTime = TimeSpan.Zero;
221        if (clearRuns) optimizer.Runs.Clear();
222        gsoAction = GoalSeekingOptimizerAction.Prepare;
223        // a race-condition may occur when the algorithm has changed the state by itself in the meantime
224        try { optimizer.Prepare(clearRuns); }
225        catch (InvalidOperationException) { }
226      } else {
227        ExecutionState = ExecutionState.Stopped;
228      }
229    }
230
231    [Storable]
232    private TimeSpan executionTime;
233    public TimeSpan ExecutionTime {
234      get {
235        if ((Optimizer != null) && (Optimizer.ExecutionState != ExecutionState.Stopped))
236          return executionTime + Optimizer.ExecutionTime;
237        else
238          return executionTime;
239      }
240      private set {
241        executionTime = value;
242        OnExecutionTimeChanged();
243      }
244    }
245
246    public RunCollection Runs {
247      get {
248        if (optimizer == null)
249          return new RunCollection(); // return an empty run collection
250        return Optimizer.Runs;
251      }
252    }
253
254    public IEnumerable<IOptimizer> NestedOptimizers { get; private set; }
255
256    public string Filename { get; set; }
257
258    public new static Image StaticItemImage {
259      get { return HeuristicLab.Common.Resources.VSImageLibrary.Event; }
260    }
261    public override Image ItemImage {
262      get {
263        if (ExecutionState == ExecutionState.Prepared) return HeuristicLab.Common.Resources.VSImageLibrary.BatchRunPrepared;
264        else if (ExecutionState == ExecutionState.Started) return HeuristicLab.Common.Resources.VSImageLibrary.BatchRunStarted;
265        else if (ExecutionState == ExecutionState.Paused) return HeuristicLab.Common.Resources.VSImageLibrary.BatchRunPaused;
266        else if (ExecutionState == ExecutionState.Stopped) return HeuristicLab.Common.Resources.VSImageLibrary.BatchRunStopped;
267        else return base.ItemImage;
268      }
269    }
270
271    #region Events
272    protected override void OnNameChanged() {
273      base.OnNameChanged();
274    }
275
276    #region event firing
277    public ExecutionState ExecutionState { get; private set; }
278    public event EventHandler ExecutionStateChanged;
279    private void OnExecutionStateChanged() {
280      EventHandler handler = ExecutionStateChanged;
281      if (handler != null) handler(this, EventArgs.Empty);
282    }
283
284    public event EventHandler ExecutionTimeChanged;
285    private void OnExecutionTimeChanged() {
286      EventHandler handler = ExecutionTimeChanged;
287      if (handler != null) handler(this, EventArgs.Empty);
288    }
289
290    public event EventHandler Prepared;
291    private void OnPrepared() {
292      gsoAction = GoalSeekingOptimizerAction.None;
293      ExecutionState = ExecutionState.Prepared;
294      EventHandler handler = Prepared;
295      if (handler != null) handler(this, EventArgs.Empty);
296    }
297
298    public event EventHandler Started;
299    private void OnStarted() {
300      ExecutionState = ExecutionState.Started;
301      EventHandler handler = Started;
302      if (handler != null) handler(this, EventArgs.Empty);
303    }
304
305    public event EventHandler Paused;
306    private void OnPaused() {
307      gsoAction = GoalSeekingOptimizerAction.None;
308      ExecutionState = ExecutionState.Paused;
309      EventHandler handler = Paused;
310      if (handler != null) handler(this, EventArgs.Empty);
311    }
312
313    public event EventHandler Stopped;
314    private void OnStopped() {
315      gsoAction = GoalSeekingOptimizerAction.None;
316      ExecutionState = ExecutionState.Stopped;
317      EventHandler handler = Stopped;
318      if (handler != null) handler(this, EventArgs.Empty);
319    }
320
321    public event EventHandler<EventArgs<Exception>> ExceptionOccurred;
322    private void OnExceptionOccurred(Exception exception) {
323      EventHandler<EventArgs<Exception>> handler = ExceptionOccurred;
324      if (handler != null) handler(this, new EventArgs<Exception>(exception));
325    }
326
327    public event EventHandler OptimizerChanged;
328    private void OnOptimizerChanged() {
329      EventHandler handler = OptimizerChanged;
330      if (handler != null) handler(this, EventArgs.Empty);
331    }
332
333    public event EventHandler RepetitionsChanged;
334    private void OnRepetitionsChanged() {
335      EventHandler handler = RepetitionsChanged;
336      if (handler != null) handler(this, EventArgs.Empty);
337    }
338
339    public event EventHandler RepetetionsCounterChanged;
340    private void OnRepetitionsCounterChanged() {
341      EventHandler handler = RepetetionsCounterChanged;
342      if (handler != null) handler(this, EventArgs.Empty);
343    }
344    #endregion
345
346    #region optimizer event handlers
347    private void RegisterOptimizerEvents() {
348      optimizer.ExceptionOccurred += Optimizer_ExceptionOccurred;
349      optimizer.ExecutionTimeChanged += Optimizer_ExecutionTimeChanged;
350      optimizer.Paused += Optimizer_Paused;
351      optimizer.Prepared += Optimizer_Prepared;
352      optimizer.Started += Optimizer_Started;
353      optimizer.Stopped += Optimizer_Stopped;
354    }
355
356    private void DeregisterOptimizerEvents() {
357      optimizer.ExceptionOccurred -= Optimizer_ExceptionOccurred;
358      optimizer.ExecutionTimeChanged -= Optimizer_ExecutionTimeChanged;
359      optimizer.Paused -= Optimizer_Paused;
360      optimizer.Prepared -= Optimizer_Prepared;
361      optimizer.Started -= Optimizer_Started;
362      optimizer.Stopped -= Optimizer_Stopped;
363    }
364
365    private void Optimizer_ExceptionOccurred(object sender, EventArgs<Exception> e) {
366      OnExceptionOccurred(e.Value);
367    }
368
369    private void Optimizer_ExecutionTimeChanged(object sender, EventArgs e) {
370      OnExecutionTimeChanged();
371    }
372
373    private void Optimizer_Paused(object sender, EventArgs e) {
374      if (ExecutionState == ExecutionState.Started) {
375        OnPaused();
376      }
377    }
378
379    private void Optimizer_Prepared(object sender, EventArgs e) {
380      if (gsoAction == GoalSeekingOptimizerAction.Prepare || ExecutionState == ExecutionState.Stopped) {
381        calculatedRows = 0;
382        ExecutionTime = TimeSpan.Zero;
383        OnPrepared();
384      }
385    }
386
387    private void Optimizer_Started(object sender, EventArgs e) {
388      if (ExecutionState != ExecutionState.Started) {
389        OnStarted();
390      }
391    }
392
393    private void Optimizer_Stopped(object sender, EventArgs e) {
394      calculatedRows++;
395      ExecutionTime += optimizer.ExecutionTime;
396      var remainingRows = problemData.TrainingIndices.Skip(calculatedRows);
397
398      // extract results from the optimizer
399      GetResults();
400
401      if (gsoAction == GoalSeekingOptimizerAction.Stop)
402        OnStopped();
403      else if (!remainingRows.Any()) {
404        OnStopped();
405      } else switch (gsoAction) {
406          case GoalSeekingOptimizerAction.Pause:
407            OnPaused();
408            break;
409          case GoalSeekingOptimizerAction.Start:
410            currentRow = remainingRows.First();
411            problem.Configure(problemData, currentRow);
412            optimizer.Prepare();
413            optimizer.Start();
414            break;
415          default:
416            if (ExecutionState == ExecutionState.Started) {
417              OnPaused();
418            } else OnStopped();
419            break;
420        }
421    }
422    #endregion
423    #endregion
424
425    #region aggregate results
426    private void GetResults() {
427      if (Problem is MultiObjectiveGoalSeekingProblem) {
428        var m = (DoubleMatrix)optimizer.Results["Pareto Front Solutions"].Value;
429        AggregateMultiObjectiveResults(m);
430      } else if (Problem is SingleObjectiveGoalSeekingProblem) {
431        var m = (DoubleMatrix)optimizer.Results["Best Solution"].Value;
432        AggregateSingleObjectiveResults(m);
433      }
434    }
435
436    private void AggregateSingleObjectiveResults(DoubleMatrix solutions) {
437      var indices = solutions.ColumnNames.Select((x, i) => Tuple.Create(x, i)).ToDictionary(x => x.Item1, x => x.Item2);
438
439      var goals = Problem.Goals.Where(x => x.Active).ToList();
440      var inputs = Problem.Inputs.Where(x => x.Active).ToList();
441
442      if (Results == null)
443        Results = new ResultCollection();
444      // goals
445      if (!Results.ContainsKey("Goals"))
446        Results.Add(new Result("Goals", new ResultCollection()));
447      var goalResults = (ResultCollection)Results["Goals"].Value;
448      foreach (var goal in goals) {
449        var estimatedGoalName = goal.Name + " (estimated)";
450        if (!goalResults.ContainsKey(goal.Name)) {
451          goalResults.Add(new Result(goal.Name, new ScatterPlot(goal.Name, "")));
452        }
453
454        var plot = (ScatterPlot)goalResults[goal.Name].Value;
455        if (!plot.Rows.ContainsKey(goal.Name)) {
456          plot.Rows.Add(new ScatterPlotDataRow { Name = goal.Name });
457          plot.Rows.Add(new ScatterPlotDataRow { Name = estimatedGoalName });
458        }
459
460        plot.Rows[goal.Name].Points.Add(new Point2D<double>(currentRow, solutions[0, indices[goal.Name]]));
461        plot.Rows[estimatedGoalName].Points.Add(new Point2D<double>(currentRow, solutions[0, indices[estimatedGoalName]]));
462      }
463
464      // inputs
465      if (!Results.ContainsKey("Inputs"))
466        Results.Add(new Result("Inputs", new ResultCollection()));
467      var inputResults = (ResultCollection)Results["Inputs"].Value;
468      foreach (var input in inputs) {
469        var estimatedInputName = input.Name + " (estimated)";
470        if (!inputResults.ContainsKey(input.Name)) {
471          inputResults.Add(new Result(input.Name, new ScatterPlot(input.Name, "")));
472        }
473
474        var plot = (ScatterPlot)inputResults[input.Name].Value;
475        if (!plot.Rows.ContainsKey(input.Name)) {
476          plot.Rows.Add(new ScatterPlotDataRow { Name = input.Name });
477          plot.Rows.Add(new ScatterPlotDataRow { Name = estimatedInputName });
478        }
479
480        plot.Rows[input.Name].Points.Add(new Point2D<double>(currentRow, solutions[0, indices[input.Name]]));
481        plot.Rows[estimatedInputName].Points.Add(new Point2D<double>(currentRow, solutions[0, indices[estimatedInputName]]));
482      }
483      // aggregate results as a matrix
484      UpdateResultsMatrix(solutions);
485    }
486
487    private void AggregateMultiObjectiveResults(DoubleMatrix solutions) {
488      int maxSolutionsCount = Math.Min(solutions.Rows, 10);
489
490      var indices = solutions.ColumnNames.Select((x, i) => Tuple.Create(x, i)).ToDictionary(x => x.Item1, x => x.Item2);
491
492      var goals = Problem.Goals.Where(x => x.Active).ToList();
493      var inputs = Problem.Inputs.Where(x => x.Active).ToList();
494
495      if (Results == null)
496        Results = new ResultCollection();
497      // goals
498      if (!Results.ContainsKey("Goals"))
499        Results.Add(new Result("Goals", new ResultCollection()));
500      var goalResults = (ResultCollection)Results["Goals"].Value;
501      foreach (var goal in goals) {
502        var estimatedGoalName = goal.Name + " (estimated)";
503        if (!goalResults.ContainsKey(goal.Name)) {
504          goalResults.Add(new Result(goal.Name, new ScatterPlot(goal.Name, "")));
505        }
506
507        var plot = (ScatterPlot)goalResults[goal.Name].Value;
508        if (!plot.Rows.ContainsKey(goal.Name)) {
509          plot.Rows.Add(new ScatterPlotDataRow { Name = goal.Name, VisualProperties = { PointSize = 5 } });
510        }
511
512        plot.Rows[goal.Name].Points.Add(new Point2D<double>(currentRow, solutions[0, indices[goal.Name]]));
513        for (int i = 0; i < maxSolutionsCount; ++i) {
514          if (!plot.Rows.ContainsKey(estimatedGoalName + i))
515            plot.Rows.Add(new ScatterPlotDataRow { Name = estimatedGoalName + i, VisualProperties = { PointSize = 5 } });
516          plot.Rows[estimatedGoalName + i].Points.Add(new Point2D<double>(currentRow, solutions[i, indices[estimatedGoalName]]));
517        }
518      }
519      // inputs
520      if (!Results.ContainsKey("Inputs"))
521        Results.Add(new Result("Inputs", new ResultCollection()));
522      var inputResults = (ResultCollection)Results["Inputs"].Value;
523      foreach (var input in inputs) {
524        var estimatedInputName = input.Name + " (estimated)";
525        if (!inputResults.ContainsKey(input.Name)) {
526          inputResults.Add(new Result(input.Name, new ScatterPlot(input.Name, "")));
527        }
528
529        var plot = (ScatterPlot)inputResults[input.Name].Value;
530        if (!plot.Rows.ContainsKey(input.Name)) {
531          plot.Rows.Add(new ScatterPlotDataRow { Name = input.Name, VisualProperties = { PointSize = 5 } });
532        }
533
534        plot.Rows[input.Name].Points.Add(new Point2D<double>(currentRow, solutions[0, indices[input.Name]])); // it stays the same for all the rows
535        for (int i = 0; i < maxSolutionsCount; ++i) {
536          if (!plot.Rows.ContainsKey(estimatedInputName + i))
537            plot.Rows.Add(new ScatterPlotDataRow { Name = estimatedInputName + i, VisualProperties = { PointSize = 5 } });
538          plot.Rows[estimatedInputName + i].Points.Add(new Point2D<double>(currentRow, solutions[i, indices[estimatedInputName]]));
539        }
540      }
541      // also add a matrix containing the whole solution data including the row
542      UpdateResultsMatrix(solutions);
543    }
544
545    private void UpdateResultsMatrix(DoubleMatrix solutions) {
546      // also add a matrix containing the whole solution data including the row
547      var rows = problemData.TrainingIndices.Take(calculatedRows).ToList();
548      var m = new DoubleMatrix(calculatedRows, solutions.Columns + 1) {
549        RowNames = rows.Select(x => x.ToString()),
550        ColumnNames = new[] { "Row" }.Concat(solutions.ColumnNames)
551      };
552      DoubleMatrix old = null;
553      if (Results.ContainsKey("Solutions"))
554        old = (DoubleMatrix)Results["Solutions"].Value;
555      else
556        Results.Add(new Result("Solutions", m));
557
558      if (old != null && calculatedRows > 0) {
559        for (int i = 0; i < old.Rows; ++i) {
560          for (int j = 0; j < old.Columns; ++j) {
561            m[i, j] = old[i, j];
562          }
563        }
564      }
565      m[rows.Count - 1, 0] = currentRow;
566      for (int i = 1; i < m.Columns; ++i) {
567        m[rows.Count - 1, i] = solutions[0, i - 1];
568      }
569      Results["Solutions"].Value = m;
570    }
571    #endregion
572  }
573}
Note: See TracBrowser for help on using the repository browser.