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

Last change on this file since 13000 was 13000, checked in by gkronber, 4 years ago

#2061: merged r12948:12950 from branch to trunk

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