Free cookie consent management tool by TermsFeed Policy Generator

source: branches/ParallelExperiment/HeuristicLab.Optimization/3.3/MetaOptimizers/Experiment.cs @ 15373

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

#2822:

  • Merged recent trunk-changes into branch.
  • Using task-continuations for semaphore-releasing to avoid manually started optimizers to mess with the semaphore.
File size: 20.6 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      if (!Optimizers.Any(x => x.ExecutionState == ExecutionState.Prepared || x.ExecutionState == ExecutionState.Paused)) return;
207
208      experimentStarted = true;
209      experimentStopped = false;
210
211      var startedTasks = new List<Task>();
212      using (var availableWorkers = new SemaphoreSlim(NumberOfWorkers, NumberOfWorkers)) {
213        var runnableOptimizers = Optimizers.Where(o => o.ExecutionState == ExecutionState.Prepared || o.ExecutionState == ExecutionState.Paused).ToList();
214        if (!runnableOptimizers.Any()) return;
215
216        while (runnableOptimizers.Any()) {
217          try {
218            availableWorkers.Wait(cancellationToken);
219            if (experimentStopped || !experimentStarted) break;
220            // some optimizers might be started manually somewhere else
221            runnableOptimizers.RemoveAll(o => !(o.ExecutionState == ExecutionState.Prepared || o.ExecutionState == ExecutionState.Paused));
222            if (runnableOptimizers.Any()) {
223              var optimizer = runnableOptimizers.First();
224              runnableOptimizers.Remove(optimizer);
225              var startedTask = optimizer.StartAsync(cancellationToken).ContinueWith(x => {
226                availableWorkers.Release(); // is not disposed yet because Task.WaitAll at the end of the using is blocking
227              });
228              startedTasks.Add(startedTask);
229            }
230          } catch (InvalidOperationException) { } catch (OperationCanceledException) { }
231        }
232
233        allOptimizerFinished.Wait();
234        Task.WaitAll(startedTasks.ToArray()); // retreive exeptions of the asyncrounously started optimizer
235      }
236    }
237    public async Task StartAsync() { await StartAsync(CancellationToken.None); }
238    public async Task StartAsync(CancellationToken cancellationToken) {
239      await AsyncHelper.DoAsync(Start, cancellationToken);
240    }
241    public void Pause() {
242      if (ExecutionState != ExecutionState.Started)
243        throw new InvalidOperationException(string.Format("Pause not allowed in execution state \"{0}\".", ExecutionState));
244      if (Optimizers.Count == 0) return;
245
246      experimentStarted = false;
247      experimentStopped = false;
248      foreach (IOptimizer optimizer in Optimizers.Where(x => x.ExecutionState == ExecutionState.Started)) {
249        // a race-condition may occur when the optimizer has changed the state by itself in the meantime
250        try { optimizer.Pause(); } catch (InvalidOperationException) { } catch (NotSupportedException) { }
251      }
252    }
253    public void Stop() {
254      if ((ExecutionState != ExecutionState.Started) && (ExecutionState != ExecutionState.Paused))
255        throw new InvalidOperationException(string.Format("Stop not allowed in execution state \"{0}\".", ExecutionState));
256      if (Optimizers.Count == 0) return;
257
258      experimentStarted = false;
259      experimentStopped = true;
260      if (Optimizers.Any(x => (x.ExecutionState == ExecutionState.Started) || (x.ExecutionState == ExecutionState.Paused))) {
261        foreach (var optimizer in Optimizers.Where(x => (x.ExecutionState == ExecutionState.Started) || (x.ExecutionState == ExecutionState.Paused))) {
262          // a race-condition may occur when the optimizer has changed the state by itself in the meantime
263          try { optimizer.Stop(); } catch (InvalidOperationException) { }
264        }
265      } else {
266        OnStopped();
267      }
268    }
269
270    #region Events
271    protected override void OnNameChanged() {
272      base.OnNameChanged();
273      Runs.OptimizerName = Name;
274    }
275
276    public event EventHandler ExecutionStateChanged;
277    private void OnExecutionStateChanged() {
278      EventHandler handler = ExecutionStateChanged;
279      if (handler != null) handler(this, EventArgs.Empty);
280    }
281    public event EventHandler ExecutionTimeChanged;
282    private void OnExecutionTimeChanged() {
283      EventHandler handler = ExecutionTimeChanged;
284      if (handler != null) handler(this, EventArgs.Empty);
285    }
286    public event EventHandler Prepared;
287    private void OnPrepared() {
288      ExecutionState = ExecutionState.Prepared;
289      EventHandler handler = Prepared;
290      if (handler != null) handler(this, EventArgs.Empty);
291    }
292    public event EventHandler Started;
293    private void OnStarted() {
294      ExecutionState = ExecutionState.Started;
295      EventHandler handler = Started;
296      if (handler != null) handler(this, EventArgs.Empty);
297    }
298    public event EventHandler Paused;
299    private void OnPaused() {
300      ExecutionState = ExecutionState.Paused;
301      allOptimizerFinished.Set();
302      EventHandler handler = Paused;
303      if (handler != null) handler(this, EventArgs.Empty);
304    }
305    public event EventHandler Stopped;
306    private void OnStopped() {
307      ExecutionState = ExecutionState.Stopped;
308      allOptimizerFinished.Set();
309      EventHandler handler = Stopped;
310      if (handler != null) handler(this, EventArgs.Empty);
311    }
312    public event EventHandler<EventArgs<Exception>> ExceptionOccurred;
313    private void OnExceptionOccurred(Exception exception) {
314      EventHandler<EventArgs<Exception>> handler = ExceptionOccurred;
315      if (handler != null) handler(this, new EventArgs<Exception>(exception));
316    }
317
318    private void RegisterOptimizersEvents() {
319      Optimizers.CollectionReset += new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_CollectionReset);
320      Optimizers.ItemsAdded += new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsAdded);
321      Optimizers.ItemsRemoved += new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsRemoved);
322      Optimizers.ItemsReplaced += new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsReplaced);
323    }
324    private void DeregisterOptimizersEvents() {
325      Optimizers.CollectionReset -= new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_CollectionReset);
326      Optimizers.ItemsAdded -= new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsAdded);
327      Optimizers.ItemsRemoved -= new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsRemoved);
328      Optimizers.ItemsReplaced -= new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsReplaced);
329    }
330    private void Optimizers_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
331      foreach (IndexedItem<IOptimizer> item in e.OldItems)
332        RemoveOptimizer(item.Value);
333      foreach (IndexedItem<IOptimizer> item in e.Items)
334        AddOptimizer(item.Value);
335    }
336    private void Optimizers_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
337      foreach (IndexedItem<IOptimizer> item in e.Items)
338        AddOptimizer(item.Value);
339    }
340    private void Optimizers_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
341      foreach (IndexedItem<IOptimizer> item in e.Items)
342        RemoveOptimizer(item.Value);
343    }
344    private void Optimizers_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
345      foreach (IndexedItem<IOptimizer> item in e.OldItems)
346        RemoveOptimizer(item.Value);
347      foreach (IndexedItem<IOptimizer> item in e.Items)
348        AddOptimizer(item.Value);
349    }
350    private void AddOptimizer(IOptimizer optimizer) {
351      RegisterOptimizerEvents(optimizer);
352      Runs.AddRange(optimizer.Runs);
353      optimizer.Prepare();
354      if (ExecutionState == ExecutionState.Stopped && optimizer.ExecutionState == ExecutionState.Prepared)
355        OnPrepared();
356    }
357    private void RemoveOptimizer(IOptimizer optimizer) {
358      DeregisterOptimizerEvents(optimizer);
359      Runs.RemoveRange(optimizer.Runs);
360      if (ExecutionState == ExecutionState.Prepared && !optimizers.Any(opt => opt.ExecutionState == ExecutionState.Prepared))
361        OnStopped();
362    }
363
364    private void RegisterOptimizerEvents(IOptimizer optimizer) {
365      optimizer.ExceptionOccurred += new EventHandler<EventArgs<Exception>>(optimizer_ExceptionOccurred);
366      optimizer.ExecutionTimeChanged += new EventHandler(optimizer_ExecutionTimeChanged);
367      optimizer.Paused += new EventHandler(optimizer_Paused);
368      optimizer.Prepared += new EventHandler(optimizer_Prepared);
369      optimizer.Started += new EventHandler(optimizer_Started);
370      optimizer.Stopped += new EventHandler(optimizer_Stopped);
371      optimizer.Runs.CollectionReset += new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_CollectionReset);
372      optimizer.Runs.ItemsAdded += new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_ItemsAdded);
373      optimizer.Runs.ItemsRemoved += new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_ItemsRemoved);
374    }
375    private void DeregisterOptimizerEvents(IOptimizer optimizer) {
376      optimizer.ExceptionOccurred -= new EventHandler<EventArgs<Exception>>(optimizer_ExceptionOccurred);
377      optimizer.ExecutionTimeChanged -= new EventHandler(optimizer_ExecutionTimeChanged);
378      optimizer.Paused -= new EventHandler(optimizer_Paused);
379      optimizer.Prepared -= new EventHandler(optimizer_Prepared);
380      optimizer.Started -= new EventHandler(optimizer_Started);
381      optimizer.Stopped -= new EventHandler(optimizer_Stopped);
382      optimizer.Runs.CollectionReset -= new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_CollectionReset);
383      optimizer.Runs.ItemsAdded -= new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_ItemsAdded);
384      optimizer.Runs.ItemsRemoved -= new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_ItemsRemoved);
385    }
386
387    private readonly object locker = new object();
388    private readonly object runsLocker = new object();
389    private void optimizer_ExceptionOccurred(object sender, EventArgs<Exception> e) {
390      lock (locker)
391        OnExceptionOccurred(e.Value);
392    }
393    private void optimizer_ExecutionTimeChanged(object sender, EventArgs e) {
394      // only wait for maximally 100ms to acquire lock, otherwise return and don't update the execution time
395      var success = Monitor.TryEnter(locker, 100);
396      if (!success) return;
397      try {
398        ExecutionTime = Optimizers.Aggregate(TimeSpan.Zero, (t, o) => t + o.ExecutionTime);
399      } finally {
400        Monitor.Exit(locker);
401      }
402    }
403    private void optimizer_Paused(object sender, EventArgs e) {
404      lock (locker) {
405        if (Optimizers.All(x => x.ExecutionState != ExecutionState.Started)) {
406          OnPaused();
407        }
408      }
409    }
410    private void optimizer_Prepared(object sender, EventArgs e) {
411      lock (locker)
412        if (Optimizers.All(x => x.ExecutionState == ExecutionState.Prepared)) OnPrepared();
413    }
414    private void optimizer_Started(object sender, EventArgs e) {
415      lock (locker)
416        if (ExecutionState != ExecutionState.Started) OnStarted();
417    }
418    private void optimizer_Stopped(object sender, EventArgs e) {
419      lock (locker) {
420        if (experimentStopped) {
421          if (Optimizers.All(x => (x.ExecutionState == ExecutionState.Stopped) || (x.ExecutionState == ExecutionState.Prepared))) OnStopped();
422        } else {
423          if (experimentStarted && Optimizers.Any(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused))) return;
424          else if (Optimizers.All(x => x.ExecutionState == ExecutionState.Stopped)) {
425            OnStopped();
426          } else if (Optimizers.Any(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused)) && Optimizers.All(o => o.ExecutionState != ExecutionState.Started)) {
427            OnPaused();
428          }
429        }
430      }
431    }
432    private void optimizer_Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
433      lock (runsLocker) {
434        Runs.RemoveRange(e.OldItems);
435        Runs.AddRange(e.Items);
436      }
437    }
438    private void optimizer_Runs_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IRun> e) {
439      lock (runsLocker)
440        Runs.AddRange(e.Items);
441    }
442    private void optimizer_Runs_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
443      lock (runsLocker)
444        Runs.RemoveRange(e.Items);
445    }
446
447    private void RegisterRunsEvents() {
448      runs.CollectionReset += new CollectionItemsChangedEventHandler<IRun>(Runs_CollectionReset);
449      runs.ItemsRemoved += new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsRemoved);
450    }
451    private void DeregisterRunsEvents() {
452      runs.CollectionReset -= new CollectionItemsChangedEventHandler<IRun>(Runs_CollectionReset);
453      runs.ItemsRemoved -= new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsRemoved);
454    }
455    private void Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
456      foreach (IOptimizer optimizer in Optimizers)
457        optimizer.Runs.RemoveRange(e.OldItems);
458    }
459    private void Runs_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
460      foreach (IOptimizer optimizer in Optimizers)
461        optimizer.Runs.RemoveRange(e.Items);
462    }
463    #endregion
464  }
465}
Note: See TracBrowser for help on using the repository browser.