Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2679_HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/GoalSeekingOptimizer.cs @ 16901

Last change on this file since 16901 was 16901, checked in by bburlacu, 5 years ago

#2679: Update branch: framework version 4.6.1, use heal.attic for persistence, some minor refactoring.

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