Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 13349 was 13349, checked in by jkarder, 8 years ago

#2258: added StartAsync to IExecutable

File size: 19.1 KB
RevLine 
[3267]1#region License Information
2/* HeuristicLab
[12012]3 * Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
[3267]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;
[5419]23using System.Collections.Generic;
[3267]24using System.Drawing;
25using System.Linq;
[13000]26using System.Threading;
[13349]27using System.Threading.Tasks;
[3267]28using HeuristicLab.Collections;
29using HeuristicLab.Common;
30using HeuristicLab.Core;
31using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
32
33namespace HeuristicLab.Optimization {
34  /// <summary>
[5300]35  /// An experiment which contains multiple algorithms, batch runs or other experiments.
[3267]36  /// </summary>
[5300]37  [Item("Experiment", "An experiment which contains multiple algorithms, batch runs or other experiments.")]
[12504]38  [Creatable(CreatableAttribute.Categories.TestingAndAnalysis, Priority = 100)]
[3267]39  [StorableClass]
[4419]40  public sealed class Experiment : NamedItem, IOptimizer, IStorableContent {
[13349]41    private readonly ManualResetEvent signaler = new ManualResetEvent(true);
42
[4419]43    public string Filename { get; set; }
44
[7201]45    public static new Image StaticItemImage {
46      get { return HeuristicLab.Common.Resources.VSImageLibrary.Event; }
47    }
[3267]48    public override Image ItemImage {
[3351]49      get {
[6534]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;
[7201]54        else return base.ItemImage;
[3351]55      }
[3267]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();
[3351]66          OnItemImageChanged();
[3267]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
[3280]81    [Storable]
[3274]82    private OptimizerList optimizers;
83    public OptimizerList Optimizers {
84      get { return optimizers; }
[3267]85    }
86
[3275]87    [Storable]
88    private RunCollection runs;
89    public RunCollection Runs {
90      get { return runs; }
[3716]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      }
[3275]99    }
100
[5419]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
[6767]113    private bool experimentStarted = false;
114    private bool experimentStopped = false;
[3267]115
116    public Experiment()
117      : base() {
[3280]118      name = ItemName;
119      description = ItemDescription;
[3267]120      executionState = ExecutionState.Stopped;
121      executionTime = TimeSpan.Zero;
[3280]122      optimizers = new OptimizerList();
[8975]123      Runs = new RunCollection { OptimizerName = Name };
[3280]124      Initialize();
[3267]125    }
[3280]126    public Experiment(string name)
127      : base(name) {
128      description = ItemDescription;
[3267]129      executionState = ExecutionState.Stopped;
130      executionTime = TimeSpan.Zero;
[3280]131      optimizers = new OptimizerList();
[8975]132      Runs = new RunCollection { OptimizerName = Name };
[3280]133      Initialize();
[3267]134    }
[3280]135    public Experiment(string name, string description)
136      : base(name, description) {
[3267]137      executionState = ExecutionState.Stopped;
138      executionTime = TimeSpan.Zero;
[3280]139      optimizers = new OptimizerList();
[8975]140      Runs = new RunCollection { OptimizerName = Name };
[3280]141      Initialize();
[3267]142    }
[3280]143    [StorableConstructor]
[6767]144    private Experiment(bool deserializing) : base(deserializing) { }
[4722]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);
[6767]155
156      experimentStarted = original.experimentStarted;
157      experimentStopped = original.experimentStopped;
[4722]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    }
[3267]164
[3280]165    private void Initialize() {
166      RegisterOptimizersEvents();
167      foreach (IOptimizer optimizer in optimizers)
168        RegisterOptimizerEvents(optimizer);
[3716]169      if (runs != null) RegisterRunsEvents();
[3280]170    }
171
[3267]172    public void Prepare() {
[3275]173      Prepare(false);
[3267]174    }
[3275]175    public void Prepare(bool clearRuns) {
[3267]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));
[6767]178      if (Optimizers.Count == 0) return;
179
180      if (clearRuns) runs.Clear();
[7241]181
182      experimentStarted = false;
183      experimentStopped = false;
[8194]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
[12504]186        try { optimizer.Prepare(clearRuns); }
187        catch (InvalidOperationException) { }
[8194]188      }
[3267]189    }
190    public void Start() {
[13349]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;
[6767]202
[13349]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);
[3267]213    }
214    public void Pause() {
215      if (ExecutionState != ExecutionState.Started)
216        throw new InvalidOperationException(string.Format("Pause not allowed in execution state \"{0}\".", ExecutionState));
[6767]217      if (Optimizers.Count == 0) return;
218
219      experimentStarted = false;
220      experimentStopped = false;
[8194]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
[12504]223        try { optimizer.Pause(); }
224        catch (InvalidOperationException) { }
[8194]225      }
[3267]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));
[6767]230      if (Optimizers.Count == 0) return;
231
232      experimentStarted = false;
233      experimentStopped = true;
[7241]234      if (Optimizers.Any(x => (x.ExecutionState == ExecutionState.Started) || (x.ExecutionState == ExecutionState.Paused))) {
[8194]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
[12504]237          try { optimizer.Stop(); }
238          catch (InvalidOperationException) { }
[8194]239        }
[7241]240      } else {
241        OnStopped();
242      }
[3267]243    }
244
245    #region Events
[8738]246    protected override void OnNameChanged() {
247      base.OnNameChanged();
[8975]248      Runs.OptimizerName = Name;
[8738]249    }
250
[3267]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;
[13349]276      signaler.Set();
[3267]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;
[13349]283      signaler.Set();
[3267]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
[3274]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);
[3267]298    }
[3274]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);
[3267]304    }
[3274]305    private void Optimizers_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
[4115]306      foreach (IndexedItem<IOptimizer> item in e.OldItems)
307        RemoveOptimizer(item.Value);
[4110]308      foreach (IndexedItem<IOptimizer> item in e.Items)
309        AddOptimizer(item.Value);
[3267]310    }
[3274]311    private void Optimizers_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
[4110]312      foreach (IndexedItem<IOptimizer> item in e.Items)
313        AddOptimizer(item.Value);
[3267]314    }
[3274]315    private void Optimizers_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
[4115]316      foreach (IndexedItem<IOptimizer> item in e.Items)
317        RemoveOptimizer(item.Value);
[3267]318    }
[3274]319    private void Optimizers_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
[4115]320      foreach (IndexedItem<IOptimizer> item in e.OldItems)
321        RemoveOptimizer(item.Value);
[4110]322      foreach (IndexedItem<IOptimizer> item in e.Items)
323        AddOptimizer(item.Value);
[3267]324    }
[4110]325    private void AddOptimizer(IOptimizer optimizer) {
326      RegisterOptimizerEvents(optimizer);
327      Runs.AddRange(optimizer.Runs);
328      optimizer.Prepare();
[4551]329      if (ExecutionState == ExecutionState.Stopped && optimizer.ExecutionState == ExecutionState.Prepared)
330        OnPrepared();
[4110]331    }
[4115]332    private void RemoveOptimizer(IOptimizer optimizer) {
333      DeregisterOptimizerEvents(optimizer);
334      Runs.RemoveRange(optimizer.Runs);
[4551]335      if (ExecutionState == ExecutionState.Prepared && !optimizers.Any(opt => opt.ExecutionState == ExecutionState.Prepared))
336        OnStopped();
[4115]337    }
[3267]338
[3274]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);
[3280]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);
[3267]349    }
[3274]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);
[3280]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);
[3267]360    }
[8170]361
362    private readonly object locker = new object();
[13000]363    private readonly object runsLocker = new object();
[3274]364    private void optimizer_ExceptionOccurred(object sender, EventArgs<Exception> e) {
[8170]365      lock (locker)
366        OnExceptionOccurred(e.Value);
[3267]367    }
[3274]368    private void optimizer_ExecutionTimeChanged(object sender, EventArgs e) {
[13000]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 {
[8170]373        ExecutionTime = Optimizers.Aggregate(TimeSpan.Zero, (t, o) => t + o.ExecutionTime);
[13000]374      }
375      finally {
376        Monitor.Exit(locker);
377      }
[3274]378    }
379    private void optimizer_Paused(object sender, EventArgs e) {
[8170]380      lock (locker)
381        if (Optimizers.All(x => x.ExecutionState != ExecutionState.Started)) OnPaused();
[3267]382    }
[3274]383    private void optimizer_Prepared(object sender, EventArgs e) {
[8170]384      lock (locker)
385        if (Optimizers.All(x => x.ExecutionState == ExecutionState.Prepared)) OnPrepared();
[3267]386    }
[3274]387    private void optimizer_Started(object sender, EventArgs e) {
[8170]388      lock (locker)
389        if (ExecutionState != ExecutionState.Started) OnStarted();
[3267]390    }
[3274]391    private void optimizer_Stopped(object sender, EventArgs e) {
[8129]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))) {
[13349]397            Optimizers.First(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused)).StartAsync();
[8129]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        }
[3267]401      }
402    }
[3280]403    private void optimizer_Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
[13000]404      lock (runsLocker) {
[8170]405        Runs.RemoveRange(e.OldItems);
406        Runs.AddRange(e.Items);
407      }
[3275]408    }
[3280]409    private void optimizer_Runs_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IRun> e) {
[13000]410      lock (runsLocker)
[8170]411        Runs.AddRange(e.Items);
[3275]412    }
[3280]413    private void optimizer_Runs_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
[13000]414      lock (runsLocker)
[8170]415        Runs.RemoveRange(e.Items);
[3275]416    }
[3716]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    }
[3267]434    #endregion
435  }
436}
Note: See TracBrowser for help on using the repository browser.