source: branches/Async/HeuristicLab.Optimization/3.3/MetaOptimizers/Experiment.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: 19.1 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.Linq;
26using System.Threading;
27using System.Threading.Tasks;
28using HeuristicLab.Collections;
29using HeuristicLab.Common;
30using HeuristicLab.Core;
31using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
32
33namespace HeuristicLab.Optimization {
34  /// <summary>
35  /// An experiment which contains multiple algorithms, batch runs or other experiments.
36  /// </summary>
37  [Item("Experiment", "An experiment which contains multiple algorithms, batch runs or other experiments.")]
38  [Creatable(CreatableAttribute.Categories.TestingAndAnalysis, Priority = 100)]
39  [StorableClass]
40  public sealed class Experiment : NamedItem, IOptimizer, IStorableContent {
41    private readonly ManualResetEvent signaler = new ManualResetEvent(true);
42
43    public string Filename { get; set; }
44
45    public static new Image StaticItemImage {
46      get { return HeuristicLab.Common.Resources.VSImageLibrary.Event; }
47    }
48    public override Image ItemImage {
49      get {
50        if (ExecutionState == ExecutionState.Prepared) return HeuristicLab.Common.Resources.VSImageLibrary.ExperimentPrepared;
51        else if (ExecutionState == ExecutionState.Started) return HeuristicLab.Common.Resources.VSImageLibrary.ExperimentStarted;
52        else if (ExecutionState == ExecutionState.Paused) return HeuristicLab.Common.Resources.VSImageLibrary.ExperimentPaused;
53        else if (ExecutionState == ExecutionState.Stopped) return HeuristicLab.Common.Resources.VSImageLibrary.ExperimentStopped;
54        else return base.ItemImage;
55      }
56    }
57
58    [Storable]
59    private ExecutionState executionState;
60    public ExecutionState ExecutionState {
61      get { return executionState; }
62      private set {
63        if (executionState != value) {
64          executionState = value;
65          OnExecutionStateChanged();
66          OnItemImageChanged();
67        }
68      }
69    }
70
71    [Storable]
72    private TimeSpan executionTime;
73    public TimeSpan ExecutionTime {
74      get { return executionTime; }
75      private set {
76        executionTime = value;
77        OnExecutionTimeChanged();
78      }
79    }
80
81    [Storable]
82    private OptimizerList optimizers;
83    public OptimizerList Optimizers {
84      get { return optimizers; }
85    }
86
87    [Storable]
88    private RunCollection runs;
89    public RunCollection Runs {
90      get { return runs; }
91      private set {
92        if (value == null) throw new ArgumentNullException();
93        if (runs != value) {
94          if (runs != null) DeregisterRunsEvents();
95          runs = value;
96          if (runs != null) RegisterRunsEvents();
97        }
98      }
99    }
100
101    public IEnumerable<IOptimizer> NestedOptimizers {
102      get {
103        if (Optimizers == null) yield break;
104
105        foreach (IOptimizer opt in Optimizers) {
106          yield return opt;
107          foreach (IOptimizer nestedOpt in opt.NestedOptimizers)
108            yield return nestedOpt;
109        }
110      }
111    }
112
113    private bool experimentStarted = false;
114    private bool experimentStopped = false;
115
116    public Experiment()
117      : base() {
118      name = ItemName;
119      description = ItemDescription;
120      executionState = ExecutionState.Stopped;
121      executionTime = TimeSpan.Zero;
122      optimizers = new OptimizerList();
123      Runs = new RunCollection { OptimizerName = Name };
124      Initialize();
125    }
126    public Experiment(string name)
127      : base(name) {
128      description = ItemDescription;
129      executionState = ExecutionState.Stopped;
130      executionTime = TimeSpan.Zero;
131      optimizers = new OptimizerList();
132      Runs = new RunCollection { OptimizerName = Name };
133      Initialize();
134    }
135    public Experiment(string name, string description)
136      : base(name, description) {
137      executionState = ExecutionState.Stopped;
138      executionTime = TimeSpan.Zero;
139      optimizers = new OptimizerList();
140      Runs = new RunCollection { OptimizerName = Name };
141      Initialize();
142    }
143    [StorableConstructor]
144    private Experiment(bool deserializing) : base(deserializing) { }
145    [StorableHook(HookType.AfterDeserialization)]
146    private void AfterDeserialization() {
147      Initialize();
148    }
149    private Experiment(Experiment original, Cloner cloner)
150      : base(original, cloner) {
151      executionState = original.executionState;
152      executionTime = original.executionTime;
153      optimizers = cloner.Clone(original.optimizers);
154      runs = cloner.Clone(original.runs);
155
156      experimentStarted = original.experimentStarted;
157      experimentStopped = original.experimentStopped;
158      Initialize();
159    }
160    public override IDeepCloneable Clone(Cloner cloner) {
161      if (ExecutionState == ExecutionState.Started) throw new InvalidOperationException(string.Format("Clone not allowed in execution state \"{0}\".", ExecutionState));
162      return new Experiment(this, cloner);
163    }
164
165    private void Initialize() {
166      RegisterOptimizersEvents();
167      foreach (IOptimizer optimizer in optimizers)
168        RegisterOptimizerEvents(optimizer);
169      if (runs != null) RegisterRunsEvents();
170    }
171
172    public void Prepare() {
173      Prepare(false);
174    }
175    public void Prepare(bool clearRuns) {
176      if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused) && (ExecutionState != ExecutionState.Stopped))
177        throw new InvalidOperationException(string.Format("Prepare not allowed in execution state \"{0}\".", ExecutionState));
178      if (Optimizers.Count == 0) return;
179
180      if (clearRuns) runs.Clear();
181
182      experimentStarted = false;
183      experimentStopped = false;
184      foreach (IOptimizer optimizer in Optimizers.Where(x => x.ExecutionState != ExecutionState.Started)) {
185        // a race-condition may occur when the optimizer has changed the state by itself in the meantime
186        try { optimizer.Prepare(clearRuns); }
187        catch (InvalidOperationException) { }
188      }
189    }
190    public void Start() {
191      StartAsync().Wait();
192    }
193    public async Task StartAsync() {
194      await StartAsync(new CancellationToken());
195    }
196    public async Task StartAsync(CancellationToken cancellationToken) {
197      signaler.Reset();
198      await Task.Run(async () => {
199        if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused))
200          throw new InvalidOperationException(string.Format("Start not allowed in execution state \"{0}\".", ExecutionState));
201        if (Optimizers.Count == 0) return;
202
203        experimentStarted = true;
204        experimentStopped = false;
205        IOptimizer optimizer = Optimizers.FirstOrDefault(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused));
206        if (optimizer != null) {
207          // a race-condition may occur when the optimizer has changed the state by itself in the meantime
208          try { await optimizer.StartAsync(cancellationToken); }
209          catch (InvalidOperationException) { }
210        }
211        signaler.WaitOne();
212      }, cancellationToken);
213    }
214    public void Pause() {
215      if (ExecutionState != ExecutionState.Started)
216        throw new InvalidOperationException(string.Format("Pause not allowed in execution state \"{0}\".", ExecutionState));
217      if (Optimizers.Count == 0) return;
218
219      experimentStarted = false;
220      experimentStopped = false;
221      foreach (IOptimizer optimizer in Optimizers.Where(x => x.ExecutionState == ExecutionState.Started)) {
222        // a race-condition may occur when the optimizer has changed the state by itself in the meantime
223        try { optimizer.Pause(); }
224        catch (InvalidOperationException) { }
225      }
226    }
227    public void Stop() {
228      if ((ExecutionState != ExecutionState.Started) && (ExecutionState != ExecutionState.Paused))
229        throw new InvalidOperationException(string.Format("Stop not allowed in execution state \"{0}\".", ExecutionState));
230      if (Optimizers.Count == 0) return;
231
232      experimentStarted = false;
233      experimentStopped = true;
234      if (Optimizers.Any(x => (x.ExecutionState == ExecutionState.Started) || (x.ExecutionState == ExecutionState.Paused))) {
235        foreach (var optimizer in Optimizers.Where(x => (x.ExecutionState == ExecutionState.Started) || (x.ExecutionState == ExecutionState.Paused))) {
236          // a race-condition may occur when the optimizer has changed the state by itself in the meantime
237          try { optimizer.Stop(); }
238          catch (InvalidOperationException) { }
239        }
240      } else {
241        OnStopped();
242      }
243    }
244
245    #region Events
246    protected override void OnNameChanged() {
247      base.OnNameChanged();
248      Runs.OptimizerName = Name;
249    }
250
251    public event EventHandler ExecutionStateChanged;
252    private void OnExecutionStateChanged() {
253      EventHandler handler = ExecutionStateChanged;
254      if (handler != null) handler(this, EventArgs.Empty);
255    }
256    public event EventHandler ExecutionTimeChanged;
257    private void OnExecutionTimeChanged() {
258      EventHandler handler = ExecutionTimeChanged;
259      if (handler != null) handler(this, EventArgs.Empty);
260    }
261    public event EventHandler Prepared;
262    private void OnPrepared() {
263      ExecutionState = ExecutionState.Prepared;
264      EventHandler handler = Prepared;
265      if (handler != null) handler(this, EventArgs.Empty);
266    }
267    public event EventHandler Started;
268    private void OnStarted() {
269      ExecutionState = ExecutionState.Started;
270      EventHandler handler = Started;
271      if (handler != null) handler(this, EventArgs.Empty);
272    }
273    public event EventHandler Paused;
274    private void OnPaused() {
275      ExecutionState = ExecutionState.Paused;
276      signaler.Set();
277      EventHandler handler = Paused;
278      if (handler != null) handler(this, EventArgs.Empty);
279    }
280    public event EventHandler Stopped;
281    private void OnStopped() {
282      ExecutionState = ExecutionState.Stopped;
283      signaler.Set();
284      EventHandler handler = Stopped;
285      if (handler != null) handler(this, EventArgs.Empty);
286    }
287    public event EventHandler<EventArgs<Exception>> ExceptionOccurred;
288    private void OnExceptionOccurred(Exception exception) {
289      EventHandler<EventArgs<Exception>> handler = ExceptionOccurred;
290      if (handler != null) handler(this, new EventArgs<Exception>(exception));
291    }
292
293    private void RegisterOptimizersEvents() {
294      Optimizers.CollectionReset += new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_CollectionReset);
295      Optimizers.ItemsAdded += new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsAdded);
296      Optimizers.ItemsRemoved += new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsRemoved);
297      Optimizers.ItemsReplaced += new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsReplaced);
298    }
299    private void DeregisterOptimizersEvents() {
300      Optimizers.CollectionReset -= new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_CollectionReset);
301      Optimizers.ItemsAdded -= new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsAdded);
302      Optimizers.ItemsRemoved -= new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsRemoved);
303      Optimizers.ItemsReplaced -= new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsReplaced);
304    }
305    private void Optimizers_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
306      foreach (IndexedItem<IOptimizer> item in e.OldItems)
307        RemoveOptimizer(item.Value);
308      foreach (IndexedItem<IOptimizer> item in e.Items)
309        AddOptimizer(item.Value);
310    }
311    private void Optimizers_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
312      foreach (IndexedItem<IOptimizer> item in e.Items)
313        AddOptimizer(item.Value);
314    }
315    private void Optimizers_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
316      foreach (IndexedItem<IOptimizer> item in e.Items)
317        RemoveOptimizer(item.Value);
318    }
319    private void Optimizers_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
320      foreach (IndexedItem<IOptimizer> item in e.OldItems)
321        RemoveOptimizer(item.Value);
322      foreach (IndexedItem<IOptimizer> item in e.Items)
323        AddOptimizer(item.Value);
324    }
325    private void AddOptimizer(IOptimizer optimizer) {
326      RegisterOptimizerEvents(optimizer);
327      Runs.AddRange(optimizer.Runs);
328      optimizer.Prepare();
329      if (ExecutionState == ExecutionState.Stopped && optimizer.ExecutionState == ExecutionState.Prepared)
330        OnPrepared();
331    }
332    private void RemoveOptimizer(IOptimizer optimizer) {
333      DeregisterOptimizerEvents(optimizer);
334      Runs.RemoveRange(optimizer.Runs);
335      if (ExecutionState == ExecutionState.Prepared && !optimizers.Any(opt => opt.ExecutionState == ExecutionState.Prepared))
336        OnStopped();
337    }
338
339    private void RegisterOptimizerEvents(IOptimizer optimizer) {
340      optimizer.ExceptionOccurred += new EventHandler<EventArgs<Exception>>(optimizer_ExceptionOccurred);
341      optimizer.ExecutionTimeChanged += new EventHandler(optimizer_ExecutionTimeChanged);
342      optimizer.Paused += new EventHandler(optimizer_Paused);
343      optimizer.Prepared += new EventHandler(optimizer_Prepared);
344      optimizer.Started += new EventHandler(optimizer_Started);
345      optimizer.Stopped += new EventHandler(optimizer_Stopped);
346      optimizer.Runs.CollectionReset += new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_CollectionReset);
347      optimizer.Runs.ItemsAdded += new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_ItemsAdded);
348      optimizer.Runs.ItemsRemoved += new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_ItemsRemoved);
349    }
350    private void DeregisterOptimizerEvents(IOptimizer optimizer) {
351      optimizer.ExceptionOccurred -= new EventHandler<EventArgs<Exception>>(optimizer_ExceptionOccurred);
352      optimizer.ExecutionTimeChanged -= new EventHandler(optimizer_ExecutionTimeChanged);
353      optimizer.Paused -= new EventHandler(optimizer_Paused);
354      optimizer.Prepared -= new EventHandler(optimizer_Prepared);
355      optimizer.Started -= new EventHandler(optimizer_Started);
356      optimizer.Stopped -= new EventHandler(optimizer_Stopped);
357      optimizer.Runs.CollectionReset -= new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_CollectionReset);
358      optimizer.Runs.ItemsAdded -= new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_ItemsAdded);
359      optimizer.Runs.ItemsRemoved -= new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_ItemsRemoved);
360    }
361
362    private readonly object locker = new object();
363    private readonly object runsLocker = new object();
364    private void optimizer_ExceptionOccurred(object sender, EventArgs<Exception> e) {
365      lock (locker)
366        OnExceptionOccurred(e.Value);
367    }
368    private void optimizer_ExecutionTimeChanged(object sender, EventArgs e) {
369      // only wait for maximally 100ms to acquire lock, otherwise return and don't update the execution time
370      var success = Monitor.TryEnter(locker, 100);
371      if (!success) return;
372      try {
373        ExecutionTime = Optimizers.Aggregate(TimeSpan.Zero, (t, o) => t + o.ExecutionTime);
374      }
375      finally {
376        Monitor.Exit(locker);
377      }
378    }
379    private void optimizer_Paused(object sender, EventArgs e) {
380      lock (locker)
381        if (Optimizers.All(x => x.ExecutionState != ExecutionState.Started)) OnPaused();
382    }
383    private void optimizer_Prepared(object sender, EventArgs e) {
384      lock (locker)
385        if (Optimizers.All(x => x.ExecutionState == ExecutionState.Prepared)) OnPrepared();
386    }
387    private void optimizer_Started(object sender, EventArgs e) {
388      lock (locker)
389        if (ExecutionState != ExecutionState.Started) OnStarted();
390    }
391    private void optimizer_Stopped(object sender, EventArgs e) {
392      lock (locker) {
393        if (experimentStopped) {
394          if (Optimizers.All(x => (x.ExecutionState == ExecutionState.Stopped) || (x.ExecutionState == ExecutionState.Prepared))) OnStopped();
395        } else {
396          if (experimentStarted && Optimizers.Any(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused))) {
397            Optimizers.First(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused)).StartAsync();
398          } else if (Optimizers.All(x => x.ExecutionState == ExecutionState.Stopped)) OnStopped();
399          else if (Optimizers.Any(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused)) && Optimizers.All(o => o.ExecutionState != ExecutionState.Started)) OnPaused();
400        }
401      }
402    }
403    private void optimizer_Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
404      lock (runsLocker) {
405        Runs.RemoveRange(e.OldItems);
406        Runs.AddRange(e.Items);
407      }
408    }
409    private void optimizer_Runs_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IRun> e) {
410      lock (runsLocker)
411        Runs.AddRange(e.Items);
412    }
413    private void optimizer_Runs_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
414      lock (runsLocker)
415        Runs.RemoveRange(e.Items);
416    }
417
418    private void RegisterRunsEvents() {
419      runs.CollectionReset += new CollectionItemsChangedEventHandler<IRun>(Runs_CollectionReset);
420      runs.ItemsRemoved += new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsRemoved);
421    }
422    private void DeregisterRunsEvents() {
423      runs.CollectionReset -= new CollectionItemsChangedEventHandler<IRun>(Runs_CollectionReset);
424      runs.ItemsRemoved -= new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsRemoved);
425    }
426    private void Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
427      foreach (IOptimizer optimizer in Optimizers)
428        optimizer.Runs.RemoveRange(e.OldItems);
429    }
430    private void Runs_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
431      foreach (IOptimizer optimizer in Optimizers)
432        optimizer.Runs.RemoveRange(e.Items);
433    }
434    #endregion
435  }
436}
Note: See TracBrowser for help on using the repository browser.