source: branches/Async/HeuristicLab.Optimization/3.3/MetaOptimizers/BatchRun.cs @ 13349

Last change on this file since 13349 was 13349, checked in by jkarder, 4 years ago

#2258: added StartAsync to IExecutable

File size: 18.7 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2015 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.Threading;
26using System.Threading.Tasks;
27using HeuristicLab.Collections;
28using HeuristicLab.Common;
29using HeuristicLab.Core;
30using HeuristicLab.Data;
31using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
32
33namespace HeuristicLab.Optimization {
34  internal enum BatchRunAction { None, Prepare, Start, Pause, Stop };
35
36  /// <summary>
37  /// A run in which an optimizer is executed a given number of times.
38  /// </summary>
39  [Item("Batch Run", "A run in which an optimizer is executed a given number of times.")]
40  [Creatable(CreatableAttribute.Categories.TestingAndAnalysis, Priority = 110)]
41  [StorableClass]
42  public sealed class BatchRun : NamedItem, IOptimizer, IStorableContent {
43    private readonly ManualResetEvent signaler = new ManualResetEvent(true);
44
45    public string Filename { get; set; }
46
47    public static new Image StaticItemImage {
48      get { return HeuristicLab.Common.Resources.VSImageLibrary.Event; }
49    }
50    public override Image ItemImage {
51      get {
52        if (ExecutionState == ExecutionState.Prepared) return HeuristicLab.Common.Resources.VSImageLibrary.BatchRunPrepared;
53        else if (ExecutionState == ExecutionState.Started) return HeuristicLab.Common.Resources.VSImageLibrary.BatchRunStarted;
54        else if (ExecutionState == ExecutionState.Paused) return HeuristicLab.Common.Resources.VSImageLibrary.BatchRunPaused;
55        else if (ExecutionState == ExecutionState.Stopped) return HeuristicLab.Common.Resources.VSImageLibrary.BatchRunStopped;
56        else return base.ItemImage;
57      }
58    }
59
60    [Storable]
61    private ExecutionState executionState;
62    public ExecutionState ExecutionState {
63      get { return executionState; }
64      private set {
65        if (executionState != value) {
66          executionState = value;
67          OnExecutionStateChanged();
68          OnItemImageChanged();
69        }
70      }
71    }
72
73    [Storable]
74    private TimeSpan executionTime;
75    public TimeSpan ExecutionTime {
76      get {
77        if ((Optimizer != null) && (Optimizer.ExecutionState != ExecutionState.Stopped))
78          return executionTime + Optimizer.ExecutionTime;
79        else
80          return executionTime;
81      }
82      private set {
83        executionTime = value;
84        OnExecutionTimeChanged();
85      }
86    }
87
88    [Storable]
89    private TimeSpan runsExecutionTime;
90
91    [Storable]
92    private IOptimizer optimizer;
93    public IOptimizer Optimizer {
94      get { return optimizer; }
95      set {
96        if (optimizer != value) {
97          if (optimizer != null) {
98            DeregisterOptimizerEvents();
99            IEnumerable<IRun> runs = optimizer.Runs;
100            optimizer = null; //necessary to avoid removing the runs from the old optimizer
101            Runs.RemoveRange(runs);
102          }
103          optimizer = value;
104          if (optimizer != null) {
105            RegisterOptimizerEvents();
106            Runs.AddRange(optimizer.Runs);
107          }
108          OnOptimizerChanged();
109          Prepare();
110        }
111      }
112    }
113    // BackwardsCompatibility3.3
114    #region Backwards compatible code (remove with 3.4)
115    [Storable(AllowOneWay = true)]
116    private IAlgorithm algorithm {
117      set { optimizer = value; }
118    }
119    #endregion
120
121    [Storable]
122    private int repetitions;
123    public int Repetitions {
124      get { return repetitions; }
125      set {
126        if (repetitions != value) {
127          repetitions = value;
128          OnRepetitionsChanged();
129          if ((Optimizer != null) && (Optimizer.ExecutionState == ExecutionState.Stopped))
130            Prepare();
131        }
132      }
133    }
134    [Storable]
135    private int repetitionsCounter;
136    public int RepetitionsCounter {
137      get { return repetitionsCounter; }
138      private set {
139        if (value != repetitionsCounter) {
140          repetitionsCounter = value;
141          OnRepetitionsCounterChanged();
142        }
143      }
144    }
145
146    [Storable]
147    private RunCollection runs;
148    public RunCollection Runs {
149      get { return runs; }
150      private set {
151        if (value == null) throw new ArgumentNullException();
152        if (runs != value) {
153          if (runs != null) DeregisterRunsEvents();
154          runs = value;
155          if (runs != null) RegisterRunsEvents();
156        }
157      }
158    }
159
160    public IEnumerable<IOptimizer> NestedOptimizers {
161      get {
162        if (Optimizer == null) yield break;
163
164        yield return Optimizer;
165        foreach (IOptimizer opt in Optimizer.NestedOptimizers)
166          yield return opt;
167      }
168    }
169
170    private BatchRunAction batchRunAction = BatchRunAction.None;
171
172    public BatchRun()
173      : base() {
174      name = ItemName;
175      description = ItemDescription;
176      executionState = ExecutionState.Stopped;
177      executionTime = TimeSpan.Zero;
178      runsExecutionTime = TimeSpan.Zero;
179      repetitions = 10;
180      repetitionsCounter = 0;
181      Runs = new RunCollection { OptimizerName = Name };
182    }
183    public BatchRun(string name)
184      : base(name) {
185      description = ItemDescription;
186      executionState = ExecutionState.Stopped;
187      executionTime = TimeSpan.Zero;
188      runsExecutionTime = TimeSpan.Zero;
189      repetitions = 10;
190      repetitionsCounter = 0;
191      Runs = new RunCollection { OptimizerName = Name };
192    }
193    public BatchRun(string name, string description)
194      : base(name, description) {
195      executionState = ExecutionState.Stopped;
196      executionTime = TimeSpan.Zero;
197      runsExecutionTime = TimeSpan.Zero;
198      repetitions = 10;
199      repetitionsCounter = 0;
200      Runs = new RunCollection { OptimizerName = Name };
201    }
202    [StorableConstructor]
203    private BatchRun(bool deserializing) : base(deserializing) { }
204    [StorableHook(HookType.AfterDeserialization)]
205    private void AfterDeserialization() {
206      Initialize();
207    }
208
209    private BatchRun(BatchRun original, Cloner cloner)
210      : base(original, cloner) {
211      executionState = original.executionState;
212      executionTime = original.executionTime;
213      runsExecutionTime = original.runsExecutionTime;
214      optimizer = cloner.Clone(original.optimizer);
215      repetitions = original.repetitions;
216      repetitionsCounter = original.repetitionsCounter;
217      runs = cloner.Clone(original.runs);
218      batchRunAction = original.batchRunAction;
219      Initialize();
220    }
221    public override IDeepCloneable Clone(Cloner cloner) {
222      if (ExecutionState == ExecutionState.Started) throw new InvalidOperationException(string.Format("Clone not allowed in execution state \"{0}\".", ExecutionState));
223      return new BatchRun(this, cloner);
224    }
225
226    private void Initialize() {
227      if (optimizer != null) RegisterOptimizerEvents();
228      if (runs != null) RegisterRunsEvents();
229    }
230
231    public void Prepare() {
232      Prepare(false);
233    }
234    public void Prepare(bool clearRuns) {
235      if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused) && (ExecutionState != ExecutionState.Stopped))
236        throw new InvalidOperationException(string.Format("Prepare not allowed in execution state \"{0}\".", ExecutionState));
237      if (Optimizer != null) {
238        ExecutionTime = TimeSpan.Zero;
239        RepetitionsCounter = 0;
240        if (clearRuns) runs.Clear();
241        batchRunAction = BatchRunAction.Prepare;
242        // a race-condition may occur when the optimizer has changed the state by itself in the meantime
243        try { Optimizer.Prepare(clearRuns); }
244        catch (InvalidOperationException) { }
245      } else {
246        ExecutionState = ExecutionState.Stopped;
247      }
248    }
249    public void Start() {
250      StartAsync().Wait();
251    }
252    public async Task StartAsync() {
253      await StartAsync(new CancellationToken());
254    }
255    public async Task StartAsync(CancellationToken cancellationToken) {
256      signaler.Reset();
257      await Task.Run(async () => {
258        if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused))
259          throw new InvalidOperationException(string.Format("Start not allowed in execution state \"{0}\".", ExecutionState));
260        if (Optimizer == null) return;
261        batchRunAction = BatchRunAction.Start;
262        if (Optimizer.ExecutionState == ExecutionState.Stopped) Optimizer.Prepare();
263        // a race-condition may occur when the optimizer has changed the state by itself in the meantime
264        try { await Optimizer.StartAsync(cancellationToken); }
265        catch (InvalidOperationException) { }
266        signaler.WaitOne();
267      }, cancellationToken);
268    }
269    public void Pause() {
270      if (ExecutionState != ExecutionState.Started)
271        throw new InvalidOperationException(string.Format("Pause not allowed in execution state \"{0}\".", ExecutionState));
272      if (Optimizer == null) return;
273      batchRunAction = BatchRunAction.Pause;
274      if (Optimizer.ExecutionState != ExecutionState.Started) return;
275      // a race-condition may occur when the optimizer has changed the state by itself in the meantime
276      try { Optimizer.Pause(); }
277      catch (InvalidOperationException) { }
278    }
279    public void Stop() {
280      if ((ExecutionState != ExecutionState.Started) && (ExecutionState != ExecutionState.Paused))
281        throw new InvalidOperationException(string.Format("Stop not allowed in execution state \"{0}\".", ExecutionState));
282      if (Optimizer == null) return;
283      batchRunAction = BatchRunAction.Stop;
284      if (Optimizer.ExecutionState != ExecutionState.Started && Optimizer.ExecutionState != ExecutionState.Paused) {
285        OnStopped();
286        return;
287      }
288      // a race-condition may occur when the optimizer has changed the state by itself in the meantime
289      try { Optimizer.Stop(); }
290      catch (InvalidOperationException) { }
291    }
292
293    #region Events
294    protected override void OnNameChanged() {
295      base.OnNameChanged();
296      runs.OptimizerName = Name;
297    }
298
299    public event EventHandler ExecutionStateChanged;
300    private void OnExecutionStateChanged() {
301      EventHandler handler = ExecutionStateChanged;
302      if (handler != null) handler(this, EventArgs.Empty);
303    }
304    public event EventHandler ExecutionTimeChanged;
305    private void OnExecutionTimeChanged() {
306      EventHandler handler = ExecutionTimeChanged;
307      if (handler != null) handler(this, EventArgs.Empty);
308    }
309    public event EventHandler OptimizerChanged;
310    private void OnOptimizerChanged() {
311      EventHandler handler = OptimizerChanged;
312      if (handler != null) handler(this, EventArgs.Empty);
313    }
314    public event EventHandler RepetitionsChanged;
315    private void OnRepetitionsChanged() {
316      EventHandler handler = RepetitionsChanged;
317      if (handler != null) handler(this, EventArgs.Empty);
318    }
319    public event EventHandler RepetetionsCounterChanged;
320    private void OnRepetitionsCounterChanged() {
321      EventHandler handler = RepetetionsCounterChanged;
322      if (handler != null) handler(this, EventArgs.Empty);
323    }
324    public event EventHandler Prepared;
325    private void OnPrepared() {
326      batchRunAction = BatchRunAction.None;
327      ExecutionState = ExecutionState.Prepared;
328      EventHandler handler = Prepared;
329      if (handler != null) handler(this, EventArgs.Empty);
330    }
331    public event EventHandler Started;
332    private void OnStarted() {
333      // no reset of BatchRunAction.Started, because we need to differ which of the two was started by the user
334      ExecutionState = ExecutionState.Started;
335      EventHandler handler = Started;
336      if (handler != null) handler(this, EventArgs.Empty);
337    }
338    public event EventHandler Paused;
339    private void OnPaused() {
340      batchRunAction = BatchRunAction.None;
341      ExecutionState = ExecutionState.Paused;
342      signaler.Set();
343      EventHandler handler = Paused;
344      if (handler != null) handler(this, EventArgs.Empty);
345    }
346    public event EventHandler Stopped;
347    private void OnStopped() {
348      batchRunAction = BatchRunAction.None;
349      ExecutionState = ExecutionState.Stopped;
350      signaler.Set();
351      EventHandler handler = Stopped;
352      if (handler != null) handler(this, EventArgs.Empty);
353    }
354    public event EventHandler<EventArgs<Exception>> ExceptionOccurred;
355    private void OnExceptionOccurred(Exception exception) {
356      EventHandler<EventArgs<Exception>> handler = ExceptionOccurred;
357      if (handler != null) handler(this, new EventArgs<Exception>(exception));
358    }
359
360    private void RegisterOptimizerEvents() {
361      optimizer.ExceptionOccurred += new EventHandler<EventArgs<Exception>>(Optimizer_ExceptionOccurred);
362      optimizer.ExecutionTimeChanged += new EventHandler(Optimizer_ExecutionTimeChanged);
363      optimizer.Paused += new EventHandler(Optimizer_Paused);
364      optimizer.Prepared += new EventHandler(Optimizer_Prepared);
365      optimizer.Started += new EventHandler(Optimizer_Started);
366      optimizer.Stopped += new EventHandler(Optimizer_Stopped);
367      optimizer.Runs.CollectionReset += new CollectionItemsChangedEventHandler<IRun>(Optimizer_Runs_CollectionReset);
368      optimizer.Runs.ItemsAdded += new CollectionItemsChangedEventHandler<IRun>(Optimizer_Runs_ItemsAdded);
369      optimizer.Runs.ItemsRemoved += new CollectionItemsChangedEventHandler<IRun>(Optimizer_Runs_ItemsRemoved);
370    }
371    private void DeregisterOptimizerEvents() {
372      optimizer.ExceptionOccurred -= new EventHandler<EventArgs<Exception>>(Optimizer_ExceptionOccurred);
373      optimizer.ExecutionTimeChanged -= new EventHandler(Optimizer_ExecutionTimeChanged);
374      optimizer.Paused -= new EventHandler(Optimizer_Paused);
375      optimizer.Prepared -= new EventHandler(Optimizer_Prepared);
376      optimizer.Started -= new EventHandler(Optimizer_Started);
377      optimizer.Stopped -= new EventHandler(Optimizer_Stopped);
378      optimizer.Runs.CollectionReset -= new CollectionItemsChangedEventHandler<IRun>(Optimizer_Runs_CollectionReset);
379      optimizer.Runs.ItemsAdded -= new CollectionItemsChangedEventHandler<IRun>(Optimizer_Runs_ItemsAdded);
380      optimizer.Runs.ItemsRemoved -= new CollectionItemsChangedEventHandler<IRun>(Optimizer_Runs_ItemsRemoved);
381    }
382    private void Optimizer_ExceptionOccurred(object sender, EventArgs<Exception> e) {
383      OnExceptionOccurred(e.Value);
384    }
385    private void Optimizer_ExecutionTimeChanged(object sender, EventArgs e) {
386      OnExecutionTimeChanged();
387    }
388    private void Optimizer_Paused(object sender, EventArgs e) {
389      if (ExecutionState == ExecutionState.Started) {
390        OnPaused();
391      }
392    }
393    private void Optimizer_Prepared(object sender, EventArgs e) {
394      if (batchRunAction == BatchRunAction.Prepare || ExecutionState == ExecutionState.Stopped) {
395        ExecutionTime = TimeSpan.Zero;
396        runsExecutionTime = TimeSpan.Zero;
397        RepetitionsCounter = 0;
398        OnPrepared();
399      }
400    }
401    private void Optimizer_Started(object sender, EventArgs e) {
402      if (ExecutionState != ExecutionState.Started)
403        OnStarted();
404    }
405    private void Optimizer_Stopped(object sender, EventArgs e) {
406      RepetitionsCounter++;
407      ExecutionTime += runsExecutionTime;
408      runsExecutionTime = TimeSpan.Zero;
409
410      if (batchRunAction == BatchRunAction.Stop) OnStopped();
411      else if (repetitionsCounter >= repetitions) OnStopped();
412      else if (batchRunAction == BatchRunAction.Pause) OnPaused();
413      else if (batchRunAction == BatchRunAction.Start) {
414        Optimizer.Prepare();
415        Optimizer.StartAsync();
416      } else if (executionState == ExecutionState.Started) {
417        // if the batch run hasn't been started but the inner optimizer was run then pause
418        OnPaused();
419      } else OnStopped();
420    }
421    private void Optimizer_Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
422      Runs.RemoveRange(e.OldItems);
423      Runs.AddRange(e.Items);
424    }
425    private void Optimizer_Runs_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IRun> e) {
426      Runs.AddRange(e.Items);
427    }
428    private void Optimizer_Runs_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
429      Runs.RemoveRange(e.Items);
430    }
431
432    private void RegisterRunsEvents() {
433      runs.CollectionReset += new CollectionItemsChangedEventHandler<IRun>(Runs_CollectionReset);
434      runs.ItemsAdded += new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsAdded);
435      runs.ItemsRemoved += new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsRemoved);
436    }
437
438    private void DeregisterRunsEvents() {
439      runs.CollectionReset -= new CollectionItemsChangedEventHandler<IRun>(Runs_CollectionReset);
440      runs.ItemsAdded -= new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsAdded);
441      runs.ItemsRemoved -= new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsRemoved);
442    }
443    private void Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
444      if (Optimizer != null) Optimizer.Runs.RemoveRange(e.OldItems);
445      foreach (IRun run in e.Items) {
446        IItem item;
447        run.Results.TryGetValue("Execution Time", out item);
448        TimeSpanValue executionTime = item as TimeSpanValue;
449        if (executionTime != null) ExecutionTime += executionTime.Value;
450      }
451    }
452    private void Runs_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IRun> e) {
453      foreach (IRun run in e.Items) {
454        IItem item;
455        run.Results.TryGetValue("Execution Time", out item);
456        TimeSpanValue executionTime = item as TimeSpanValue;
457        if (executionTime != null) {
458          if (Optimizer.ExecutionState == ExecutionState.Started)
459            runsExecutionTime += executionTime.Value;
460          else
461            ExecutionTime += executionTime.Value;
462        }
463      }
464    }
465    private void Runs_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
466      if (Optimizer != null) Optimizer.Runs.RemoveRange(e.Items);
467    }
468    #endregion
469  }
470}
Note: See TracBrowser for help on using the repository browser.