Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 15408 was 15408, checked in by pfleck, 7 years ago

#2822 Merged branch to trunk

File size: 20.8 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    [Storable]
100    private int numberOfWorkers = 1;
101    public int NumberOfWorkers {
102      get { return numberOfWorkers; }
103      set {
104        if (value < 1) throw new ArgumentException("Number of Workers must not be lower than one.");
105        numberOfWorkers = value;
106      }
107    }
108
109    public IEnumerable<IOptimizer> NestedOptimizers {
110      get {
111        if (Optimizers == null) yield break;
112
113        foreach (IOptimizer opt in Optimizers) {
114          yield return opt;
115          foreach (IOptimizer nestedOpt in opt.NestedOptimizers)
116            yield return nestedOpt;
117        }
118      }
119    }
120
121    private bool experimentStarted = false;
122    private bool experimentStopped = false;
123
124    private readonly ManualResetEventSlim allOptimizerFinished = new ManualResetEventSlim(false); // this indicates that all started optimizers have been paused or stopped
125
126    public Experiment()
127      : base() {
128      name = ItemName;
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)
137      : base(name) {
138      description = ItemDescription;
139      executionState = ExecutionState.Stopped;
140      executionTime = TimeSpan.Zero;
141      optimizers = new OptimizerList();
142      Runs = new RunCollection { OptimizerName = Name };
143      Initialize();
144    }
145    public Experiment(string name, string description)
146      : base(name, description) {
147      executionState = ExecutionState.Stopped;
148      executionTime = TimeSpan.Zero;
149      optimizers = new OptimizerList();
150      Runs = new RunCollection { OptimizerName = Name };
151      Initialize();
152    }
153    [StorableConstructor]
154    private Experiment(bool deserializing) : base(deserializing) { }
155    [StorableHook(HookType.AfterDeserialization)]
156    private void AfterDeserialization() {
157      Initialize();
158    }
159    private Experiment(Experiment original, Cloner cloner)
160      : base(original, cloner) {
161      executionState = original.executionState;
162      executionTime = original.executionTime;
163      optimizers = cloner.Clone(original.optimizers);
164      runs = cloner.Clone(original.runs);
165
166      experimentStarted = original.experimentStarted;
167      experimentStopped = original.experimentStopped;
168      numberOfWorkers = original.numberOfWorkers;
169      Initialize();
170    }
171    public override IDeepCloneable Clone(Cloner cloner) {
172      if (ExecutionState == ExecutionState.Started) throw new InvalidOperationException(string.Format("Clone not allowed in execution state \"{0}\".", ExecutionState));
173      return new Experiment(this, cloner);
174    }
175
176    private void Initialize() {
177      RegisterOptimizersEvents();
178      foreach (IOptimizer optimizer in optimizers)
179        RegisterOptimizerEvents(optimizer);
180      if (runs != null) RegisterRunsEvents();
181    }
182
183    public void Prepare() {
184      Prepare(false);
185    }
186    public void Prepare(bool clearRuns) {
187      if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused) && (ExecutionState != ExecutionState.Stopped))
188        throw new InvalidOperationException(string.Format("Prepare not allowed in execution state \"{0}\".", ExecutionState));
189      if (Optimizers.Count == 0) return;
190
191      if (clearRuns) runs.Clear();
192
193      experimentStarted = false;
194      experimentStopped = false;
195      foreach (IOptimizer optimizer in Optimizers.Where(x => x.ExecutionState != ExecutionState.Started)) {
196        // a race-condition may occur when the optimizer has changed the state by itself in the meantime
197        try { optimizer.Prepare(clearRuns); } catch (InvalidOperationException) { }
198      }
199    }
200    public void Start() {
201      Start(CancellationToken.None);
202    }
203    public void Start(CancellationToken cancellationToken) {
204      if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused))
205        throw new InvalidOperationException(string.Format("Start not allowed in execution state \"{0}\".", ExecutionState));
206
207      // Multiple enumerations of runnableOptimizers is done on purpose to get the latest list of runnnable optimizers lazily
208      var runnableOptimizers = Optimizers.Where(x => x.ExecutionState == ExecutionState.Prepared || x.ExecutionState == ExecutionState.Paused);
209      if (!runnableOptimizers.Any()) return;
210
211      experimentStarted = true;
212      experimentStopped = false;
213
214      var startedOptimizers = new Dictionary<IOptimizer, Task>(); // track already started optimizers (.StartAsync does not set the executionstate immediately)
215      using (var availableWorkers = new SemaphoreSlim(NumberOfWorkers, NumberOfWorkers)) {
216        while (runnableOptimizers.Any(o => !startedOptimizers.ContainsKey(o))) {
217          try {
218            availableWorkers.Wait(cancellationToken);
219            if (experimentStopped || !experimentStarted) break;
220            var optimizer = runnableOptimizers.FirstOrDefault(o => !startedOptimizers.ContainsKey(o));
221            if (optimizer != null) {
222              var startedTask = optimizer.StartAsync(cancellationToken).ContinueWith(async t => {
223                availableWorkers.Release(); // is guaranteed to be not disposed yet because Task.WaitAll blocks before the end of the using
224                await t; // trigger a potential exception on the optimizerTask
225              });
226              startedOptimizers.Add(optimizer, startedTask.Unwrap()); // unwrap task because lambda of .ContinueWith is async
227            }
228          } catch (InvalidOperationException) { } catch (OperationCanceledException) { }
229        }
230
231        Task.WaitAll(startedOptimizers.Values.ToArray()); // retreive exeptions of the asyncrounously started optimizer
232        allOptimizerFinished.Wait();
233      }
234    }
235    public async Task StartAsync() { await StartAsync(CancellationToken.None); }
236    public async Task StartAsync(CancellationToken cancellationToken) {
237      await AsyncHelper.DoAsync(Start, cancellationToken);
238    }
239    public void Pause() {
240      if (ExecutionState != ExecutionState.Started)
241        throw new InvalidOperationException(string.Format("Pause not allowed in execution state \"{0}\".", ExecutionState));
242      if (Optimizers.Count == 0) return;
243
244      experimentStarted = false;
245      experimentStopped = false;
246      foreach (IOptimizer optimizer in Optimizers.Where(x => x.ExecutionState == ExecutionState.Started)) {
247        // a race-condition may occur when the optimizer has changed the state by itself in the meantime
248        try { optimizer.Pause(); } catch (InvalidOperationException) { } catch (NotSupportedException) { }
249      }
250    }
251    public void Stop() {
252      if ((ExecutionState != ExecutionState.Started) && (ExecutionState != ExecutionState.Paused))
253        throw new InvalidOperationException(string.Format("Stop not allowed in execution state \"{0}\".", ExecutionState));
254      if (Optimizers.Count == 0) return;
255
256      experimentStarted = false;
257      experimentStopped = true;
258      if (Optimizers.Any(x => (x.ExecutionState == ExecutionState.Started) || (x.ExecutionState == ExecutionState.Paused))) {
259        foreach (var optimizer in Optimizers.Where(x => (x.ExecutionState == ExecutionState.Started) || (x.ExecutionState == ExecutionState.Paused))) {
260          // a race-condition may occur when the optimizer has changed the state by itself in the meantime
261          try { optimizer.Stop(); } catch (InvalidOperationException) { }
262        }
263      } else {
264        OnStopped();
265      }
266    }
267
268    #region Events
269    protected override void OnNameChanged() {
270      base.OnNameChanged();
271      Runs.OptimizerName = Name;
272    }
273
274    public event EventHandler ExecutionStateChanged;
275    private void OnExecutionStateChanged() {
276      EventHandler handler = ExecutionStateChanged;
277      if (handler != null) handler(this, EventArgs.Empty);
278    }
279    public event EventHandler ExecutionTimeChanged;
280    private void OnExecutionTimeChanged() {
281      EventHandler handler = ExecutionTimeChanged;
282      if (handler != null) handler(this, EventArgs.Empty);
283    }
284    public event EventHandler Prepared;
285    private void OnPrepared() {
286      ExecutionState = ExecutionState.Prepared;
287      EventHandler handler = Prepared;
288      if (handler != null) handler(this, EventArgs.Empty);
289    }
290    public event EventHandler Started;
291    private void OnStarted() {
292      ExecutionState = ExecutionState.Started;
293      EventHandler handler = Started;
294      if (handler != null) handler(this, EventArgs.Empty);
295    }
296    public event EventHandler Paused;
297    private void OnPaused() {
298      ExecutionState = ExecutionState.Paused;
299      allOptimizerFinished.Set();
300      EventHandler handler = Paused;
301      if (handler != null) handler(this, EventArgs.Empty);
302    }
303    public event EventHandler Stopped;
304    private void OnStopped() {
305      ExecutionState = ExecutionState.Stopped;
306      allOptimizerFinished.Set();
307      EventHandler handler = Stopped;
308      if (handler != null) handler(this, EventArgs.Empty);
309    }
310    public event EventHandler<EventArgs<Exception>> ExceptionOccurred;
311    private void OnExceptionOccurred(Exception exception) {
312      EventHandler<EventArgs<Exception>> handler = ExceptionOccurred;
313      if (handler != null) handler(this, new EventArgs<Exception>(exception));
314    }
315
316    private void RegisterOptimizersEvents() {
317      Optimizers.CollectionReset += new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_CollectionReset);
318      Optimizers.ItemsAdded += new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsAdded);
319      Optimizers.ItemsRemoved += new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsRemoved);
320      Optimizers.ItemsReplaced += new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsReplaced);
321    }
322    private void DeregisterOptimizersEvents() {
323      Optimizers.CollectionReset -= new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_CollectionReset);
324      Optimizers.ItemsAdded -= new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsAdded);
325      Optimizers.ItemsRemoved -= new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsRemoved);
326      Optimizers.ItemsReplaced -= new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsReplaced);
327    }
328    private void Optimizers_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
329      foreach (IndexedItem<IOptimizer> item in e.OldItems)
330        RemoveOptimizer(item.Value);
331      foreach (IndexedItem<IOptimizer> item in e.Items)
332        AddOptimizer(item.Value);
333    }
334    private void Optimizers_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
335      foreach (IndexedItem<IOptimizer> item in e.Items)
336        AddOptimizer(item.Value);
337    }
338    private void Optimizers_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
339      foreach (IndexedItem<IOptimizer> item in e.Items)
340        RemoveOptimizer(item.Value);
341    }
342    private void Optimizers_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
343      foreach (IndexedItem<IOptimizer> item in e.OldItems)
344        RemoveOptimizer(item.Value);
345      foreach (IndexedItem<IOptimizer> item in e.Items)
346        AddOptimizer(item.Value);
347    }
348    private void AddOptimizer(IOptimizer optimizer) {
349      RegisterOptimizerEvents(optimizer);
350      Runs.AddRange(optimizer.Runs);
351      optimizer.Prepare();
352      if (ExecutionState == ExecutionState.Stopped && optimizer.ExecutionState == ExecutionState.Prepared)
353        OnPrepared();
354    }
355    private void RemoveOptimizer(IOptimizer optimizer) {
356      DeregisterOptimizerEvents(optimizer);
357      Runs.RemoveRange(optimizer.Runs);
358      if (ExecutionState == ExecutionState.Prepared && !optimizers.Any(opt => opt.ExecutionState == ExecutionState.Prepared))
359        OnStopped();
360    }
361
362    private void RegisterOptimizerEvents(IOptimizer optimizer) {
363      optimizer.ExceptionOccurred += new EventHandler<EventArgs<Exception>>(optimizer_ExceptionOccurred);
364      optimizer.ExecutionTimeChanged += new EventHandler(optimizer_ExecutionTimeChanged);
365      optimizer.Paused += new EventHandler(optimizer_Paused);
366      optimizer.Prepared += new EventHandler(optimizer_Prepared);
367      optimizer.Started += new EventHandler(optimizer_Started);
368      optimizer.Stopped += new EventHandler(optimizer_Stopped);
369      optimizer.Runs.CollectionReset += new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_CollectionReset);
370      optimizer.Runs.ItemsAdded += new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_ItemsAdded);
371      optimizer.Runs.ItemsRemoved += new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_ItemsRemoved);
372    }
373    private void DeregisterOptimizerEvents(IOptimizer optimizer) {
374      optimizer.ExceptionOccurred -= new EventHandler<EventArgs<Exception>>(optimizer_ExceptionOccurred);
375      optimizer.ExecutionTimeChanged -= new EventHandler(optimizer_ExecutionTimeChanged);
376      optimizer.Paused -= new EventHandler(optimizer_Paused);
377      optimizer.Prepared -= new EventHandler(optimizer_Prepared);
378      optimizer.Started -= new EventHandler(optimizer_Started);
379      optimizer.Stopped -= new EventHandler(optimizer_Stopped);
380      optimizer.Runs.CollectionReset -= new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_CollectionReset);
381      optimizer.Runs.ItemsAdded -= new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_ItemsAdded);
382      optimizer.Runs.ItemsRemoved -= new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_ItemsRemoved);
383    }
384
385    private readonly object locker = new object();
386    private readonly object runsLocker = new object();
387    private void optimizer_ExceptionOccurred(object sender, EventArgs<Exception> e) {
388      lock (locker)
389        OnExceptionOccurred(e.Value);
390    }
391    private void optimizer_ExecutionTimeChanged(object sender, EventArgs e) {
392      // only wait for maximally 100ms to acquire lock, otherwise return and don't update the execution time
393      var success = Monitor.TryEnter(locker, 100);
394      if (!success) return;
395      try {
396        ExecutionTime = Optimizers.Aggregate(TimeSpan.Zero, (t, o) => t + o.ExecutionTime);
397      } finally {
398        Monitor.Exit(locker);
399      }
400    }
401    private void optimizer_Paused(object sender, EventArgs e) {
402      lock (locker) {
403        if (Optimizers.All(x => x.ExecutionState != ExecutionState.Started)) {
404          OnPaused();
405        }
406        experimentStarted = false;
407      }
408    }
409    private void optimizer_Prepared(object sender, EventArgs e) {
410      lock (locker)
411        if (Optimizers.All(x => x.ExecutionState == ExecutionState.Prepared)) OnPrepared();
412    }
413    private void optimizer_Started(object sender, EventArgs e) {
414      lock (locker)
415        if (ExecutionState != ExecutionState.Started) OnStarted();
416    }
417    private void optimizer_Stopped(object sender, EventArgs e) {
418      lock (locker) {
419        if (experimentStopped) {
420          if (Optimizers.All(x => (x.ExecutionState == ExecutionState.Stopped) || (x.ExecutionState == ExecutionState.Prepared))) OnStopped();
421        } else {
422          if (experimentStarted && Optimizers.Any(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused))) return;
423          else if (Optimizers.All(x => x.ExecutionState == ExecutionState.Stopped)) {
424            OnStopped();
425          } else if (Optimizers.Any(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused)) && Optimizers.All(o => o.ExecutionState != ExecutionState.Started)) {
426            OnPaused();
427          }
428        }
429      }
430    }
431    private void optimizer_Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
432      lock (runsLocker) {
433        Runs.RemoveRange(e.OldItems);
434        Runs.AddRange(e.Items);
435      }
436    }
437    private void optimizer_Runs_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IRun> e) {
438      lock (runsLocker)
439        Runs.AddRange(e.Items);
440    }
441    private void optimizer_Runs_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
442      lock (runsLocker)
443        Runs.RemoveRange(e.Items);
444    }
445
446    private void RegisterRunsEvents() {
447      runs.CollectionReset += new CollectionItemsChangedEventHandler<IRun>(Runs_CollectionReset);
448      runs.ItemsRemoved += new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsRemoved);
449    }
450    private void DeregisterRunsEvents() {
451      runs.CollectionReset -= new CollectionItemsChangedEventHandler<IRun>(Runs_CollectionReset);
452      runs.ItemsRemoved -= new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsRemoved);
453    }
454    private void Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
455      foreach (IOptimizer optimizer in Optimizers)
456        optimizer.Runs.RemoveRange(e.OldItems);
457    }
458    private void Runs_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
459      foreach (IOptimizer optimizer in Optimizers)
460        optimizer.Runs.RemoveRange(e.Items);
461    }
462    #endregion
463  }
464}
Note: See TracBrowser for help on using the repository browser.