source: trunk/sources/HeuristicLab.Optimization/3.3/MetaOptimizers/Experiment.cs @ 15339

Last change on this file since 15339 was 15339, checked in by pfleck, 5 years ago

#2823 Moved wait-handle .Set to OnPaused/OnStopped.

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