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

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

#2258: added StartAsync to IExecutable

File size: 14.4 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.ComponentModel;
25using System.Drawing;
26using System.Linq;
27using System.Threading;
28using System.Threading.Tasks;
29using HeuristicLab.Collections;
30using HeuristicLab.Common;
31using HeuristicLab.Common.Resources;
32using HeuristicLab.Core;
33using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
34
35namespace HeuristicLab.Optimization {
36  /// <summary>
37  /// A run in which an algorithm is executed for a certain maximum time only.
38  /// </summary>
39  [Item("Timelimit Run", "A run in which an optimizer is executed a certain maximum time.")]
40  [Creatable(CreatableAttribute.Categories.TestingAndAnalysis, Priority = 115)]
41  [StorableClass]
42  public sealed class TimeLimitRun : NamedItem, IOptimizer, IStorableContent, INotifyPropertyChanged {
43    private readonly ManualResetEvent signaler = new ManualResetEvent(true);
44
45    public string Filename { get; set; }
46
47    #region ItemImage
48    public static new Image StaticItemImage {
49      get { return VSImageLibrary.Event; }
50    }
51    public override Image ItemImage {
52      get { return (Algorithm != null) ? Algorithm.ItemImage : VSImageLibrary.ExecutableStopped; }
53    }
54    #endregion
55
56    private bool pausedForSnapshot = false;
57    private bool pausedForTermination = false;
58
59    [Storable]
60    private TimeSpan maximumExecutionTime;
61    public TimeSpan MaximumExecutionTime {
62      get { return maximumExecutionTime; }
63      set {
64        if (maximumExecutionTime == value) return;
65        maximumExecutionTime = value;
66        OnPropertyChanged("MaximumExecutionTime");
67      }
68    }
69
70    [Storable]
71    private int snapshotTimesIndex;
72    [Storable]
73    private ObservableList<TimeSpan> snapshotTimes;
74    public ObservableList<TimeSpan> SnapshotTimes {
75      get { return snapshotTimes; }
76      set {
77        if (snapshotTimes == value) return;
78        snapshotTimes = value;
79        snapshotTimes.Sort();
80        FindNextSnapshotTimeIndex(ExecutionTime);
81        OnPropertyChanged("SnapshotTimes");
82      }
83    }
84
85    [Storable]
86    private bool storeAlgorithmInEachSnapshot;
87    [Storable]
88    public bool StoreAlgorithmInEachSnapshot {
89      get { return storeAlgorithmInEachSnapshot; }
90      set {
91        if (storeAlgorithmInEachSnapshot == value) return;
92        storeAlgorithmInEachSnapshot = value;
93        OnPropertyChanged("StoreAlgorithmInEachSnapshot");
94      }
95    }
96
97    [Storable]
98    private RunCollection snapshots;
99    public RunCollection Snapshots {
100      get { return snapshots; }
101      set {
102        if (snapshots == value) return;
103        snapshots = value;
104        OnPropertyChanged("Snapshots");
105      }
106    }
107
108    #region Inherited Properties
109    public ExecutionState ExecutionState {
110      get { return (Algorithm != null) ? Algorithm.ExecutionState : ExecutionState.Stopped; }
111    }
112
113    public TimeSpan ExecutionTime {
114      get { return (Algorithm != null) ? Algorithm.ExecutionTime : TimeSpan.FromSeconds(0); }
115    }
116
117    [Storable]
118    private IAlgorithm algorithm;
119    public IAlgorithm Algorithm {
120      get { return algorithm; }
121      set {
122        if (algorithm == value) return;
123        if (algorithm != null) DeregisterAlgorithmEvents();
124        algorithm = value;
125        if (algorithm != null) {
126          RegisterAlgorithmEvents();
127        }
128        OnPropertyChanged("Algorithm");
129        Prepare();
130      }
131    }
132
133    [Storable]
134    private RunCollection runs;
135    public RunCollection Runs {
136      get { return runs; }
137      private set {
138        if (value == null) throw new ArgumentNullException();
139        if (runs == value) return;
140        runs = value;
141        OnPropertyChanged("Runs");
142      }
143    }
144
145    public IEnumerable<IOptimizer> NestedOptimizers {
146      get {
147        if (Algorithm == null) yield break;
148        yield return Algorithm;
149        foreach (var opt in Algorithm.NestedOptimizers)
150          yield return opt;
151      }
152    }
153    #endregion
154
155    [StorableConstructor]
156    private TimeLimitRun(bool deserializing) : base(deserializing) { }
157    private TimeLimitRun(TimeLimitRun original, Cloner cloner)
158      : base(original, cloner) {
159      maximumExecutionTime = original.maximumExecutionTime;
160      snapshotTimes = new ObservableList<TimeSpan>(original.snapshotTimes);
161      snapshotTimesIndex = original.snapshotTimesIndex;
162      snapshots = cloner.Clone(original.snapshots);
163      storeAlgorithmInEachSnapshot = original.storeAlgorithmInEachSnapshot;
164      algorithm = cloner.Clone(original.algorithm);
165      runs = cloner.Clone(original.runs);
166
167      Initialize();
168    }
169    public TimeLimitRun()
170      : base() {
171      name = ItemName;
172      description = ItemDescription;
173      maximumExecutionTime = TimeSpan.FromMinutes(.5);
174      snapshotTimes = new ObservableList<TimeSpan>(new[] {
175          TimeSpan.FromSeconds(5),
176          TimeSpan.FromSeconds(10),
177          TimeSpan.FromSeconds(15) });
178      snapshotTimesIndex = 0;
179      snapshots = new RunCollection();
180      Runs = new RunCollection { OptimizerName = Name };
181      Initialize();
182    }
183    public TimeLimitRun(string name)
184      : base(name) {
185      description = ItemDescription;
186      maximumExecutionTime = TimeSpan.FromMinutes(.5);
187      snapshotTimes = new ObservableList<TimeSpan>(new[] {
188          TimeSpan.FromSeconds(5),
189          TimeSpan.FromSeconds(10),
190          TimeSpan.FromSeconds(15) });
191      snapshotTimesIndex = 0;
192      Runs = new RunCollection { OptimizerName = Name };
193      Initialize();
194    }
195    public TimeLimitRun(string name, string description)
196      : base(name, description) {
197      maximumExecutionTime = TimeSpan.FromMinutes(.5);
198      snapshotTimes = new ObservableList<TimeSpan>(new[] {
199          TimeSpan.FromSeconds(5),
200          TimeSpan.FromSeconds(10),
201          TimeSpan.FromSeconds(15) });
202      snapshotTimesIndex = 0;
203      Runs = new RunCollection { OptimizerName = Name };
204      Initialize();
205    }
206
207    public override IDeepCloneable Clone(Cloner cloner) {
208      if (ExecutionState == ExecutionState.Started) throw new InvalidOperationException(string.Format("Clone not allowed in execution state \"{0}\".", ExecutionState));
209      return new TimeLimitRun(this, cloner);
210    }
211
212    [StorableHook(HookType.AfterDeserialization)]
213    private void AfterDeserialization() {
214      Initialize();
215    }
216
217    private void Initialize() {
218      if (algorithm != null) RegisterAlgorithmEvents();
219      snapshotTimes.ItemsAdded += snapshotTimes_Changed;
220      snapshotTimes.ItemsMoved += snapshotTimes_Changed;
221      snapshotTimes.ItemsRemoved += snapshotTimes_Changed;
222      snapshotTimes.ItemsReplaced += snapshotTimes_Changed;
223      snapshotTimes.CollectionReset += snapshotTimes_Changed;
224    }
225
226    private void snapshotTimes_Changed(object sender, CollectionItemsChangedEventArgs<IndexedItem<TimeSpan>> e) {
227      if (e.Items.Any()) snapshotTimes.Sort();
228      FindNextSnapshotTimeIndex(ExecutionTime);
229    }
230
231    public void Snapshot() {
232      if (Algorithm == null || Algorithm.ExecutionState != ExecutionState.Paused) throw new InvalidOperationException("Snapshot not allowed in execution states other than Paused");
233      Task.Factory.StartNew(MakeSnapshot);
234    }
235
236    public void Prepare() {
237      Prepare(false);
238    }
239    public void Prepare(bool clearRuns) {
240      Algorithm.Prepare(clearRuns);
241    }
242    public void Start() {
243      StartAsync().Wait();
244    }
245    public async Task StartAsync() {
246      await StartAsync(new CancellationToken());
247    }
248    public async Task StartAsync(CancellationToken cancellationToken) {
249      signaler.Reset();
250      await Task.Run(async () => {
251        await Algorithm.StartAsync(cancellationToken);
252        signaler.WaitOne();
253      }, cancellationToken);
254    }
255    public void Pause() {
256      Algorithm.Pause();
257    }
258    public void Stop() {
259      Algorithm.Stop();
260    }
261
262    #region Events
263    protected override void OnNameChanged() {
264      base.OnNameChanged();
265      runs.OptimizerName = Name;
266    }
267
268    public event PropertyChangedEventHandler PropertyChanged;
269    private void OnPropertyChanged(string property) {
270      var handler = PropertyChanged;
271      if (handler != null) handler(this, new PropertyChangedEventArgs(property));
272    }
273
274    #region IExecutable Events
275    public event EventHandler ExecutionStateChanged;
276    private void OnExecutionStateChanged() {
277      var handler = ExecutionStateChanged;
278      if (handler != null) handler(this, EventArgs.Empty);
279    }
280    public event EventHandler ExecutionTimeChanged;
281    private void OnExecutionTimeChanged() {
282      var handler = ExecutionTimeChanged;
283      if (handler != null) handler(this, EventArgs.Empty);
284    }
285    public event EventHandler Prepared;
286    private void OnPrepared() {
287      var handler = Prepared;
288      if (handler != null) handler(this, EventArgs.Empty);
289    }
290    public event EventHandler Started;
291    private void OnStarted() {
292      var handler = Started;
293      if (handler != null) handler(this, EventArgs.Empty);
294    }
295    public event EventHandler Paused;
296    private void OnPaused() {
297      if (!pausedForSnapshot && !pausedForTermination) signaler.Set();
298      var handler = Paused;
299      if (handler != null) handler(this, EventArgs.Empty);
300    }
301    public event EventHandler Stopped;
302    private void OnStopped() {
303      signaler.Set();
304      var handler = Stopped;
305      if (handler != null) handler(this, EventArgs.Empty);
306    }
307    public event EventHandler<EventArgs<Exception>> ExceptionOccurred;
308    private void OnExceptionOccurred(Exception exception) {
309      var handler = ExceptionOccurred;
310      if (handler != null) handler(this, new EventArgs<Exception>(exception));
311    }
312    #endregion
313
314    #region Algorithm Events
315    private void RegisterAlgorithmEvents() {
316      algorithm.ExceptionOccurred += Algorithm_ExceptionOccurred;
317      algorithm.ExecutionTimeChanged += Algorithm_ExecutionTimeChanged;
318      algorithm.ExecutionStateChanged += Algorithm_ExecutionStateChanged;
319      algorithm.Paused += Algorithm_Paused;
320      algorithm.Prepared += Algorithm_Prepared;
321      algorithm.Started += Algorithm_Started;
322      algorithm.Stopped += Algorithm_Stopped;
323    }
324    private void DeregisterAlgorithmEvents() {
325      algorithm.ExceptionOccurred -= Algorithm_ExceptionOccurred;
326      algorithm.ExecutionTimeChanged -= Algorithm_ExecutionTimeChanged;
327      algorithm.ExecutionStateChanged -= Algorithm_ExecutionStateChanged;
328      algorithm.Paused -= Algorithm_Paused;
329      algorithm.Prepared -= Algorithm_Prepared;
330      algorithm.Started -= Algorithm_Started;
331      algorithm.Stopped -= Algorithm_Stopped;
332    }
333    private void Algorithm_ExceptionOccurred(object sender, EventArgs<Exception> e) {
334      OnExceptionOccurred(e.Value);
335    }
336    private void Algorithm_ExecutionTimeChanged(object sender, EventArgs e) {
337      if (snapshotTimesIndex < SnapshotTimes.Count && ExecutionTime >= SnapshotTimes[snapshotTimesIndex]
338        && !pausedForSnapshot) {
339        pausedForSnapshot = true;
340        Algorithm.Pause();
341      }
342      if (ExecutionTime >= MaximumExecutionTime && !pausedForTermination) {
343        pausedForTermination = true;
344        if (!pausedForSnapshot) Algorithm.Pause();
345      }
346      OnExecutionTimeChanged();
347    }
348    private void Algorithm_ExecutionStateChanged(object sender, EventArgs e) {
349      OnExecutionStateChanged();
350    }
351    private void Algorithm_Paused(object sender, EventArgs e) {
352      var action = pausedForTermination ? ExecutionState.Stopped : (pausedForSnapshot ? ExecutionState.Started : ExecutionState.Paused);
353      bool pausedByLimit = pausedForSnapshot || pausedForTermination;
354      if (pausedByLimit) {
355        MakeSnapshot();
356        FindNextSnapshotTimeIndex(ExecutionTime);
357      }
358      OnPaused();
359      if (pausedByLimit) pausedForSnapshot = pausedForTermination = false;
360      if (action == ExecutionState.Started) Algorithm.StartAsync();
361      else if (action == ExecutionState.Stopped) Algorithm.Stop();
362    }
363    private void Algorithm_Prepared(object sender, EventArgs e) {
364      snapshotTimesIndex = 0;
365      snapshots.Clear();
366      OnPrepared();
367    }
368    private void Algorithm_Started(object sender, EventArgs e) {
369      OnStarted();
370    }
371    private void Algorithm_Stopped(object sender, EventArgs e) {
372      var cloner = new Cloner();
373      var algRun = cloner.Clone(Algorithm.Runs.Last());
374      var clonedSnapshots = cloner.Clone(snapshots);
375      algRun.Results.Add("TimeLimitRunSnapshots", clonedSnapshots);
376      Runs.Add(algRun);
377      Algorithm.Runs.Clear();
378      OnStopped();
379    }
380    #endregion
381    #endregion
382
383    private void FindNextSnapshotTimeIndex(TimeSpan reference) {
384      var index = 0;
385      while (index < snapshotTimes.Count && snapshotTimes[index] <= reference) {
386        index++;
387      };
388      snapshotTimesIndex = index;
389    }
390
391    private void MakeSnapshot() {
392      string time = Math.Round(ExecutionTime.TotalSeconds, 1).ToString("0.0");
393      string runName = "Snapshot " + time + "s " + algorithm.Name;
394      var changed = false;
395      if (StoreAlgorithmInEachSnapshot && !Algorithm.StoreAlgorithmInEachRun) {
396        Algorithm.StoreAlgorithmInEachRun = true;
397        changed = true;
398      } else if (!StoreAlgorithmInEachSnapshot && Algorithm.StoreAlgorithmInEachRun) {
399        Algorithm.StoreAlgorithmInEachRun = false;
400        changed = true;
401      }
402      var run = new Run(runName, Algorithm);
403      if (changed)
404        Algorithm.StoreAlgorithmInEachRun = !Algorithm.StoreAlgorithmInEachRun;
405
406      snapshots.Add(run);
407    }
408  }
409}
Note: See TracBrowser for help on using the repository browser.