#region License Information
/* HeuristicLab
* Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using HeuristicLab.Collections;
using HeuristicLab.Common;
using HeuristicLab.Common.Resources;
using HeuristicLab.Core;
using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
namespace HeuristicLab.Optimization {
///
/// A run in which an algorithm is executed for a certain maximum time only.
///
[Item("Timelimit Run", "A run in which an optimizer is executed a certain maximum time.")]
[Creatable(CreatableAttribute.Categories.TestingAndAnalysis, Priority = 115)]
[StorableClass]
public sealed class TimeLimitRun : NamedItem, IOptimizer, IStorableContent, INotifyPropertyChanged {
private readonly ManualResetEvent signaler = new ManualResetEvent(true);
public string Filename { get; set; }
#region ItemImage
public static new Image StaticItemImage {
get { return VSImageLibrary.Event; }
}
public override Image ItemImage {
get { return (Algorithm != null) ? Algorithm.ItemImage : VSImageLibrary.ExecutableStopped; }
}
#endregion
private bool pausedForSnapshot = false;
private bool pausedForTermination = false;
[Storable]
private TimeSpan maximumExecutionTime;
public TimeSpan MaximumExecutionTime {
get { return maximumExecutionTime; }
set {
if (maximumExecutionTime == value) return;
maximumExecutionTime = value;
OnPropertyChanged("MaximumExecutionTime");
}
}
[Storable]
private int snapshotTimesIndex;
[Storable]
private ObservableList snapshotTimes;
public ObservableList SnapshotTimes {
get { return snapshotTimes; }
set {
if (snapshotTimes == value) return;
snapshotTimes = value;
snapshotTimes.Sort();
FindNextSnapshotTimeIndex(ExecutionTime);
OnPropertyChanged("SnapshotTimes");
}
}
[Storable]
private bool storeAlgorithmInEachSnapshot;
[Storable]
public bool StoreAlgorithmInEachSnapshot {
get { return storeAlgorithmInEachSnapshot; }
set {
if (storeAlgorithmInEachSnapshot == value) return;
storeAlgorithmInEachSnapshot = value;
OnPropertyChanged("StoreAlgorithmInEachSnapshot");
}
}
[Storable]
private RunCollection snapshots;
public RunCollection Snapshots {
get { return snapshots; }
set {
if (snapshots == value) return;
snapshots = value;
OnPropertyChanged("Snapshots");
}
}
#region Inherited Properties
public ExecutionState ExecutionState {
get { return (Algorithm != null) ? Algorithm.ExecutionState : ExecutionState.Stopped; }
}
public TimeSpan ExecutionTime {
get { return (Algorithm != null) ? Algorithm.ExecutionTime : TimeSpan.FromSeconds(0); }
}
[Storable]
private IAlgorithm algorithm;
public IAlgorithm Algorithm {
get { return algorithm; }
set {
if (algorithm == value) return;
if (algorithm != null) DeregisterAlgorithmEvents();
algorithm = value;
if (algorithm != null) {
RegisterAlgorithmEvents();
}
OnPropertyChanged("Algorithm");
Prepare();
}
}
[Storable]
private RunCollection runs;
public RunCollection Runs {
get { return runs; }
private set {
if (value == null) throw new ArgumentNullException();
if (runs == value) return;
runs = value;
OnPropertyChanged("Runs");
}
}
public IEnumerable NestedOptimizers {
get {
if (Algorithm == null) yield break;
yield return Algorithm;
foreach (var opt in Algorithm.NestedOptimizers)
yield return opt;
}
}
#endregion
[StorableConstructor]
private TimeLimitRun(bool deserializing) : base(deserializing) { }
private TimeLimitRun(TimeLimitRun original, Cloner cloner)
: base(original, cloner) {
maximumExecutionTime = original.maximumExecutionTime;
snapshotTimes = new ObservableList(original.snapshotTimes);
snapshotTimesIndex = original.snapshotTimesIndex;
snapshots = cloner.Clone(original.snapshots);
storeAlgorithmInEachSnapshot = original.storeAlgorithmInEachSnapshot;
algorithm = cloner.Clone(original.algorithm);
runs = cloner.Clone(original.runs);
Initialize();
}
public TimeLimitRun()
: base() {
name = ItemName;
description = ItemDescription;
maximumExecutionTime = TimeSpan.FromMinutes(.5);
snapshotTimes = new ObservableList(new[] {
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10),
TimeSpan.FromSeconds(15) });
snapshotTimesIndex = 0;
snapshots = new RunCollection();
Runs = new RunCollection { OptimizerName = Name };
Initialize();
}
public TimeLimitRun(string name)
: base(name) {
description = ItemDescription;
maximumExecutionTime = TimeSpan.FromMinutes(.5);
snapshotTimes = new ObservableList(new[] {
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10),
TimeSpan.FromSeconds(15) });
snapshotTimesIndex = 0;
Runs = new RunCollection { OptimizerName = Name };
Initialize();
}
public TimeLimitRun(string name, string description)
: base(name, description) {
maximumExecutionTime = TimeSpan.FromMinutes(.5);
snapshotTimes = new ObservableList(new[] {
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10),
TimeSpan.FromSeconds(15) });
snapshotTimesIndex = 0;
Runs = new RunCollection { OptimizerName = Name };
Initialize();
}
public override IDeepCloneable Clone(Cloner cloner) {
if (ExecutionState == ExecutionState.Started) throw new InvalidOperationException(string.Format("Clone not allowed in execution state \"{0}\".", ExecutionState));
return new TimeLimitRun(this, cloner);
}
[StorableHook(HookType.AfterDeserialization)]
private void AfterDeserialization() {
Initialize();
}
private void Initialize() {
if (algorithm != null) RegisterAlgorithmEvents();
snapshotTimes.ItemsAdded += snapshotTimes_Changed;
snapshotTimes.ItemsMoved += snapshotTimes_Changed;
snapshotTimes.ItemsRemoved += snapshotTimes_Changed;
snapshotTimes.ItemsReplaced += snapshotTimes_Changed;
snapshotTimes.CollectionReset += snapshotTimes_Changed;
}
private void snapshotTimes_Changed(object sender, CollectionItemsChangedEventArgs> e) {
if (e.Items.Any()) snapshotTimes.Sort();
FindNextSnapshotTimeIndex(ExecutionTime);
}
public void Snapshot() {
if (Algorithm == null || Algorithm.ExecutionState != ExecutionState.Paused) throw new InvalidOperationException("Snapshot not allowed in execution states other than Paused");
Task.Factory.StartNew(MakeSnapshot);
}
public void Prepare() {
Prepare(false);
}
public void Prepare(bool clearRuns) {
Algorithm.Prepare(clearRuns);
}
public void Start() {
StartAsync().Wait();
}
public async Task StartAsync() {
await StartAsync(CancellationToken.None);
}
public async Task StartAsync(CancellationToken cancellationToken) {
signaler.Reset();
await Task.Run(async () => {
await Algorithm.StartAsync(cancellationToken);
signaler.WaitOne();
}, cancellationToken);
}
public void Pause() {
Algorithm.Pause();
}
public void Stop() {
Algorithm.Stop();
}
#region Events
protected override void OnNameChanged() {
base.OnNameChanged();
runs.OptimizerName = Name;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string property) {
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(property));
}
#region IExecutable Events
public event EventHandler ExecutionStateChanged;
private void OnExecutionStateChanged() {
var handler = ExecutionStateChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
public event EventHandler ExecutionTimeChanged;
private void OnExecutionTimeChanged() {
var handler = ExecutionTimeChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
public event EventHandler Prepared;
private void OnPrepared() {
var handler = Prepared;
if (handler != null) handler(this, EventArgs.Empty);
}
public event EventHandler Started;
private void OnStarted() {
var handler = Started;
if (handler != null) handler(this, EventArgs.Empty);
}
public event EventHandler Paused;
private void OnPaused() {
if (!pausedForSnapshot && !pausedForTermination) signaler.Set();
var handler = Paused;
if (handler != null) handler(this, EventArgs.Empty);
}
public event EventHandler Stopped;
private void OnStopped() {
signaler.Set();
var handler = Stopped;
if (handler != null) handler(this, EventArgs.Empty);
}
public event EventHandler> ExceptionOccurred;
private void OnExceptionOccurred(Exception exception) {
var handler = ExceptionOccurred;
if (handler != null) handler(this, new EventArgs(exception));
}
#endregion
#region Algorithm Events
private void RegisterAlgorithmEvents() {
algorithm.ExceptionOccurred += Algorithm_ExceptionOccurred;
algorithm.ExecutionTimeChanged += Algorithm_ExecutionTimeChanged;
algorithm.ExecutionStateChanged += Algorithm_ExecutionStateChanged;
algorithm.Paused += Algorithm_Paused;
algorithm.Prepared += Algorithm_Prepared;
algorithm.Started += Algorithm_Started;
algorithm.Stopped += Algorithm_Stopped;
}
private void DeregisterAlgorithmEvents() {
algorithm.ExceptionOccurred -= Algorithm_ExceptionOccurred;
algorithm.ExecutionTimeChanged -= Algorithm_ExecutionTimeChanged;
algorithm.ExecutionStateChanged -= Algorithm_ExecutionStateChanged;
algorithm.Paused -= Algorithm_Paused;
algorithm.Prepared -= Algorithm_Prepared;
algorithm.Started -= Algorithm_Started;
algorithm.Stopped -= Algorithm_Stopped;
}
private void Algorithm_ExceptionOccurred(object sender, EventArgs e) {
OnExceptionOccurred(e.Value);
}
private void Algorithm_ExecutionTimeChanged(object sender, EventArgs e) {
if (snapshotTimesIndex < SnapshotTimes.Count && ExecutionTime >= SnapshotTimes[snapshotTimesIndex]
&& !pausedForSnapshot) {
pausedForSnapshot = true;
Algorithm.Pause();
}
if (ExecutionTime >= MaximumExecutionTime && !pausedForTermination) {
pausedForTermination = true;
if (!pausedForSnapshot) Algorithm.Pause();
}
OnExecutionTimeChanged();
}
private void Algorithm_ExecutionStateChanged(object sender, EventArgs e) {
OnExecutionStateChanged();
}
private void Algorithm_Paused(object sender, EventArgs e) {
var action = pausedForTermination ? ExecutionState.Stopped : (pausedForSnapshot ? ExecutionState.Started : ExecutionState.Paused);
bool pausedByLimit = pausedForSnapshot || pausedForTermination;
if (pausedByLimit) {
MakeSnapshot();
FindNextSnapshotTimeIndex(ExecutionTime);
}
OnPaused();
if (pausedByLimit) pausedForSnapshot = pausedForTermination = false;
if (action == ExecutionState.Started) Algorithm.StartAsync();
else if (action == ExecutionState.Stopped) Algorithm.Stop();
}
private void Algorithm_Prepared(object sender, EventArgs e) {
snapshotTimesIndex = 0;
snapshots.Clear();
OnPrepared();
}
private void Algorithm_Started(object sender, EventArgs e) {
OnStarted();
}
private void Algorithm_Stopped(object sender, EventArgs e) {
var cloner = new Cloner();
var algRun = cloner.Clone(Algorithm.Runs.Last());
var clonedSnapshots = cloner.Clone(snapshots);
algRun.Results.Add("TimeLimitRunSnapshots", clonedSnapshots);
Runs.Add(algRun);
Algorithm.Runs.Clear();
OnStopped();
}
#endregion
#endregion
private void FindNextSnapshotTimeIndex(TimeSpan reference) {
var index = 0;
while (index < snapshotTimes.Count && snapshotTimes[index] <= reference) {
index++;
};
snapshotTimesIndex = index;
}
private void MakeSnapshot() {
string time = Math.Round(ExecutionTime.TotalSeconds, 1).ToString("0.0");
string runName = "Snapshot " + time + "s " + algorithm.Name;
var changed = false;
if (StoreAlgorithmInEachSnapshot && !Algorithm.StoreAlgorithmInEachRun) {
Algorithm.StoreAlgorithmInEachRun = true;
changed = true;
} else if (!StoreAlgorithmInEachSnapshot && Algorithm.StoreAlgorithmInEachRun) {
Algorithm.StoreAlgorithmInEachRun = false;
changed = true;
}
var run = new Run(runName, Algorithm);
if (changed)
Algorithm.StoreAlgorithmInEachRun = !Algorithm.StoreAlgorithmInEachRun;
snapshots.Add(run);
}
}
}