Free cookie consent management tool by TermsFeed Policy Generator

source: branches/Async/HeuristicLab.Optimization/3.3/MetaOptimizers/Experiment.cs @ 14778

Last change on this file since 14778 was 13354, checked in by jkarder, 9 years ago

#2258: improved cancellation support

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