Index: trunk/sources/HeuristicLab.Optimization/3.3/BatchRun.cs
===================================================================
--- trunk/sources/HeuristicLab.Optimization/3.3/BatchRun.cs	(revision 8967)
+++ 	(revision )
@@ -1,454 +1,0 @@
-#region License Information
-/* HeuristicLab
- * Copyright (C) 2002-2012 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 <http://www.gnu.org/licenses/>.
- */
-#endregion
-
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using HeuristicLab.Collections;
-using HeuristicLab.Common;
-using HeuristicLab.Core;
-using HeuristicLab.Data;
-using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
-
-namespace HeuristicLab.Optimization {
-  internal enum BatchRunAction { None, Prepare, Start, Pause, Stop };
-
-  /// <summary>
-  /// A run in which an optimizer is executed a given number of times.
-  /// </summary>
-  [Item("Batch Run", "A run in which an optimizer is executed a given number of times.")]
-  [Creatable("Testing & Analysis")]
-  [StorableClass]
-  public sealed class BatchRun : NamedItem, IOptimizer, IStorableContent {
-    public string Filename { get; set; }
-
-    public static new Image StaticItemImage {
-      get { return HeuristicLab.Common.Resources.VSImageLibrary.Event; }
-    }
-    public override Image ItemImage {
-      get {
-        if (ExecutionState == ExecutionState.Prepared) return HeuristicLab.Common.Resources.VSImageLibrary.BatchRunPrepared;
-        else if (ExecutionState == ExecutionState.Started) return HeuristicLab.Common.Resources.VSImageLibrary.BatchRunStarted;
-        else if (ExecutionState == ExecutionState.Paused) return HeuristicLab.Common.Resources.VSImageLibrary.BatchRunPaused;
-        else if (ExecutionState == ExecutionState.Stopped) return HeuristicLab.Common.Resources.VSImageLibrary.BatchRunStopped;
-        else return base.ItemImage;
-      }
-    }
-
-    [Storable]
-    private ExecutionState executionState;
-    public ExecutionState ExecutionState {
-      get { return executionState; }
-      private set {
-        if (executionState != value) {
-          executionState = value;
-          OnExecutionStateChanged();
-          OnItemImageChanged();
-        }
-      }
-    }
-
-    [Storable]
-    private TimeSpan executionTime;
-    public TimeSpan ExecutionTime {
-      get {
-        if ((Optimizer != null) && (Optimizer.ExecutionState != ExecutionState.Stopped))
-          return executionTime + Optimizer.ExecutionTime;
-        else
-          return executionTime;
-      }
-      private set {
-        executionTime = value;
-        OnExecutionTimeChanged();
-      }
-    }
-
-    [Storable]
-    private TimeSpan runsExecutionTime;
-
-    [Storable]
-    private IOptimizer optimizer;
-    public IOptimizer Optimizer {
-      get { return optimizer; }
-      set {
-        if (optimizer != value) {
-          if (optimizer != null) {
-            DeregisterOptimizerEvents();
-            IEnumerable<IRun> runs = optimizer.Runs;
-            optimizer = null; //necessary to avoid removing the runs from the old optimizer
-            Runs.RemoveRange(runs);
-          }
-          optimizer = value;
-          if (optimizer != null) {
-            RegisterOptimizerEvents();
-            Runs.AddRange(optimizer.Runs);
-          }
-          OnOptimizerChanged();
-          Prepare();
-        }
-      }
-    }
-    // BackwardsCompatibility3.3
-    #region Backwards compatible code (remove with 3.4)
-    [Storable(AllowOneWay = true)]
-    private IAlgorithm algorithm {
-      set { optimizer = value; }
-    }
-    #endregion
-
-    [Storable]
-    private int repetitions;
-    public int Repetitions {
-      get { return repetitions; }
-      set {
-        if (repetitions != value) {
-          repetitions = value;
-          OnRepetitionsChanged();
-          if ((Optimizer != null) && (Optimizer.ExecutionState == ExecutionState.Stopped))
-            Prepare();
-        }
-      }
-    }
-    [Storable]
-    private int repetitionsCounter;
-    public int RepetitionsCounter {
-      get { return repetitionsCounter; }
-      private set {
-        if (value != repetitionsCounter) {
-          repetitionsCounter = value;
-          OnRepetitionsCounterChanged();
-        }
-      }
-    }
-
-    [Storable]
-    private RunCollection runs;
-    public RunCollection Runs {
-      get { return runs; }
-      private set {
-        if (value == null) throw new ArgumentNullException();
-        if (runs != value) {
-          if (runs != null) DeregisterRunsEvents();
-          runs = value;
-          if (runs != null) RegisterRunsEvents();
-        }
-      }
-    }
-
-    public IEnumerable<IOptimizer> NestedOptimizers {
-      get {
-        if (Optimizer == null) yield break;
-
-        yield return Optimizer;
-        foreach (IOptimizer opt in Optimizer.NestedOptimizers)
-          yield return opt;
-      }
-    }
-
-    private BatchRunAction batchRunAction = BatchRunAction.None;
-
-    public BatchRun()
-      : base() {
-      name = ItemName;
-      description = ItemDescription;
-      executionState = ExecutionState.Stopped;
-      executionTime = TimeSpan.Zero;
-      runsExecutionTime = TimeSpan.Zero;
-      repetitions = 10;
-      repetitionsCounter = 0;
-      Runs = new RunCollection { OptimizerName = Name };
-    }
-    public BatchRun(string name)
-      : base(name) {
-      description = ItemDescription;
-      executionState = ExecutionState.Stopped;
-      executionTime = TimeSpan.Zero;
-      runsExecutionTime = TimeSpan.Zero;
-      repetitions = 10;
-      repetitionsCounter = 0;
-      Runs = new RunCollection { OptimizerName = Name };
-    }
-    public BatchRun(string name, string description)
-      : base(name, description) {
-      executionState = ExecutionState.Stopped;
-      executionTime = TimeSpan.Zero;
-      runsExecutionTime = TimeSpan.Zero;
-      repetitions = 10;
-      repetitionsCounter = 0;
-      Runs = new RunCollection { OptimizerName = Name };
-    }
-    [StorableConstructor]
-    private BatchRun(bool deserializing) : base(deserializing) { }
-    [StorableHook(HookType.AfterDeserialization)]
-    private void AfterDeserialization() {
-      Initialize();
-    }
-
-    private BatchRun(BatchRun original, Cloner cloner)
-      : base(original, cloner) {
-      executionState = original.executionState;
-      executionTime = original.executionTime;
-      runsExecutionTime = original.runsExecutionTime;
-      optimizer = cloner.Clone(original.optimizer);
-      repetitions = original.repetitions;
-      repetitionsCounter = original.repetitionsCounter;
-      runs = cloner.Clone(original.runs);
-      batchRunAction = original.batchRunAction;
-      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 BatchRun(this, cloner);
-    }
-
-    private void Initialize() {
-      if (optimizer != null) RegisterOptimizerEvents();
-      if (runs != null) RegisterRunsEvents();
-    }
-
-    public void Prepare() {
-      Prepare(false);
-    }
-    public void Prepare(bool clearRuns) {
-      if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused) && (ExecutionState != ExecutionState.Stopped))
-        throw new InvalidOperationException(string.Format("Prepare not allowed in execution state \"{0}\".", ExecutionState));
-      if (Optimizer != null) {
-        ExecutionTime = TimeSpan.Zero;
-        RepetitionsCounter = 0;
-        if (clearRuns) runs.Clear();
-        batchRunAction = BatchRunAction.Prepare;
-        // a race-condition may occur when the optimizer has changed the state by itself in the meantime
-        try { Optimizer.Prepare(clearRuns); }
-        catch (InvalidOperationException) { }
-      } else {
-        ExecutionState = ExecutionState.Stopped;
-      }
-    }
-    public void Start() {
-      if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused))
-        throw new InvalidOperationException(string.Format("Start not allowed in execution state \"{0}\".", ExecutionState));
-      if (Optimizer == null) return;
-      batchRunAction = BatchRunAction.Start;
-      if (Optimizer.ExecutionState == ExecutionState.Stopped) Optimizer.Prepare();
-      // a race-condition may occur when the optimizer has changed the state by itself in the meantime
-      try { Optimizer.Start(); }
-      catch (InvalidOperationException) { }
-    }
-    public void Pause() {
-      if (ExecutionState != ExecutionState.Started)
-        throw new InvalidOperationException(string.Format("Pause not allowed in execution state \"{0}\".", ExecutionState));
-      if (Optimizer == null) return;
-      batchRunAction = BatchRunAction.Pause;
-      if (Optimizer.ExecutionState != ExecutionState.Started) return;
-      // a race-condition may occur when the optimizer has changed the state by itself in the meantime
-      try { Optimizer.Pause(); }
-      catch (InvalidOperationException) { }
-    }
-    public void Stop() {
-      if ((ExecutionState != ExecutionState.Started) && (ExecutionState != ExecutionState.Paused))
-        throw new InvalidOperationException(string.Format("Stop not allowed in execution state \"{0}\".", ExecutionState));
-      if (Optimizer == null) return;
-      batchRunAction = BatchRunAction.Stop;
-      if (Optimizer.ExecutionState != ExecutionState.Started && Optimizer.ExecutionState != ExecutionState.Paused) {
-        OnStopped();
-        return;
-      }
-      // a race-condition may occur when the optimizer has changed the state by itself in the meantime
-      try { Optimizer.Stop(); }
-      catch (InvalidOperationException) { }
-    }
-
-    #region Events
-    protected override void OnNameChanged() {
-      base.OnNameChanged();
-      runs.OptimizerName = Name;
-    }
-
-    public event EventHandler ExecutionStateChanged;
-    private void OnExecutionStateChanged() {
-      EventHandler handler = ExecutionStateChanged;
-      if (handler != null) handler(this, EventArgs.Empty);
-    }
-    public event EventHandler ExecutionTimeChanged;
-    private void OnExecutionTimeChanged() {
-      EventHandler handler = ExecutionTimeChanged;
-      if (handler != null) handler(this, EventArgs.Empty);
-    }
-    public event EventHandler OptimizerChanged;
-    private void OnOptimizerChanged() {
-      EventHandler handler = OptimizerChanged;
-      if (handler != null) handler(this, EventArgs.Empty);
-    }
-    public event EventHandler RepetitionsChanged;
-    private void OnRepetitionsChanged() {
-      EventHandler handler = RepetitionsChanged;
-      if (handler != null) handler(this, EventArgs.Empty);
-    }
-    public event EventHandler RepetetionsCounterChanged;
-    private void OnRepetitionsCounterChanged() {
-      EventHandler handler = RepetetionsCounterChanged;
-      if (handler != null) handler(this, EventArgs.Empty);
-    }
-    public event EventHandler Prepared;
-    private void OnPrepared() {
-      batchRunAction = BatchRunAction.None;
-      ExecutionState = ExecutionState.Prepared;
-      EventHandler handler = Prepared;
-      if (handler != null) handler(this, EventArgs.Empty);
-    }
-    public event EventHandler Started;
-    private void OnStarted() {
-      // no reset of BatchRunAction.Started, because we need to differ which of the two was started by the user
-      ExecutionState = ExecutionState.Started;
-      EventHandler handler = Started;
-      if (handler != null) handler(this, EventArgs.Empty);
-    }
-    public event EventHandler Paused;
-    private void OnPaused() {
-      batchRunAction = BatchRunAction.None;
-      ExecutionState = ExecutionState.Paused;
-      EventHandler handler = Paused;
-      if (handler != null) handler(this, EventArgs.Empty);
-    }
-    public event EventHandler Stopped;
-    private void OnStopped() {
-      batchRunAction = BatchRunAction.None;
-      ExecutionState = ExecutionState.Stopped;
-      EventHandler handler = Stopped;
-      if (handler != null) handler(this, EventArgs.Empty);
-    }
-    public event EventHandler<EventArgs<Exception>> ExceptionOccurred;
-    private void OnExceptionOccurred(Exception exception) {
-      EventHandler<EventArgs<Exception>> handler = ExceptionOccurred;
-      if (handler != null) handler(this, new EventArgs<Exception>(exception));
-    }
-
-    private void RegisterOptimizerEvents() {
-      optimizer.ExceptionOccurred += new EventHandler<EventArgs<Exception>>(Optimizer_ExceptionOccurred);
-      optimizer.ExecutionTimeChanged += new EventHandler(Optimizer_ExecutionTimeChanged);
-      optimizer.Paused += new EventHandler(Optimizer_Paused);
-      optimizer.Prepared += new EventHandler(Optimizer_Prepared);
-      optimizer.Started += new EventHandler(Optimizer_Started);
-      optimizer.Stopped += new EventHandler(Optimizer_Stopped);
-      optimizer.Runs.CollectionReset += new CollectionItemsChangedEventHandler<IRun>(Optimizer_Runs_CollectionReset);
-      optimizer.Runs.ItemsAdded += new CollectionItemsChangedEventHandler<IRun>(Optimizer_Runs_ItemsAdded);
-      optimizer.Runs.ItemsRemoved += new CollectionItemsChangedEventHandler<IRun>(Optimizer_Runs_ItemsRemoved);
-    }
-    private void DeregisterOptimizerEvents() {
-      optimizer.ExceptionOccurred -= new EventHandler<EventArgs<Exception>>(Optimizer_ExceptionOccurred);
-      optimizer.ExecutionTimeChanged -= new EventHandler(Optimizer_ExecutionTimeChanged);
-      optimizer.Paused -= new EventHandler(Optimizer_Paused);
-      optimizer.Prepared -= new EventHandler(Optimizer_Prepared);
-      optimizer.Started -= new EventHandler(Optimizer_Started);
-      optimizer.Stopped -= new EventHandler(Optimizer_Stopped);
-      optimizer.Runs.CollectionReset -= new CollectionItemsChangedEventHandler<IRun>(Optimizer_Runs_CollectionReset);
-      optimizer.Runs.ItemsAdded -= new CollectionItemsChangedEventHandler<IRun>(Optimizer_Runs_ItemsAdded);
-      optimizer.Runs.ItemsRemoved -= new CollectionItemsChangedEventHandler<IRun>(Optimizer_Runs_ItemsRemoved);
-    }
-    private void Optimizer_ExceptionOccurred(object sender, EventArgs<Exception> e) {
-      OnExceptionOccurred(e.Value);
-    }
-    private void Optimizer_ExecutionTimeChanged(object sender, EventArgs e) {
-      OnExecutionTimeChanged();
-    }
-    private void Optimizer_Paused(object sender, EventArgs e) {
-      if (ExecutionState == ExecutionState.Started) {
-        OnPaused();
-      }
-    }
-    private void Optimizer_Prepared(object sender, EventArgs e) {
-      if (batchRunAction == BatchRunAction.Prepare || ExecutionState == ExecutionState.Stopped) {
-        ExecutionTime = TimeSpan.Zero;
-        runsExecutionTime = TimeSpan.Zero;
-        RepetitionsCounter = 0;
-        OnPrepared();
-      }
-    }
-    private void Optimizer_Started(object sender, EventArgs e) {
-      if (ExecutionState != ExecutionState.Started)
-        OnStarted();
-    }
-    private void Optimizer_Stopped(object sender, EventArgs e) {
-      RepetitionsCounter++;
-      ExecutionTime += runsExecutionTime;
-      runsExecutionTime = TimeSpan.Zero;
-
-      if (batchRunAction == BatchRunAction.Stop) OnStopped();
-      else if (repetitionsCounter >= repetitions) OnStopped();
-      else if (batchRunAction == BatchRunAction.Pause) OnPaused();
-      else if (batchRunAction == BatchRunAction.Start) {
-        Optimizer.Prepare();
-        Optimizer.Start();
-      } else if (executionState == ExecutionState.Started) {
-        // if the batch run hasn't been started but the inner optimizer was run then pause
-        OnPaused();
-      } else OnStopped();
-    }
-    private void Optimizer_Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
-      Runs.RemoveRange(e.OldItems);
-      Runs.AddRange(e.Items);
-    }
-    private void Optimizer_Runs_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IRun> e) {
-      Runs.AddRange(e.Items);
-    }
-    private void Optimizer_Runs_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
-      Runs.RemoveRange(e.Items);
-    }
-
-    private void RegisterRunsEvents() {
-      runs.CollectionReset += new CollectionItemsChangedEventHandler<IRun>(Runs_CollectionReset);
-      runs.ItemsAdded += new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsAdded);
-      runs.ItemsRemoved += new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsRemoved);
-    }
-
-    private void DeregisterRunsEvents() {
-      runs.CollectionReset -= new CollectionItemsChangedEventHandler<IRun>(Runs_CollectionReset);
-      runs.ItemsAdded -= new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsAdded);
-      runs.ItemsRemoved -= new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsRemoved);
-    }
-    private void Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
-      if (Optimizer != null) Optimizer.Runs.RemoveRange(e.OldItems);
-      foreach (IRun run in e.Items) {
-        IItem item;
-        run.Results.TryGetValue("Execution Time", out item);
-        TimeSpanValue executionTime = item as TimeSpanValue;
-        if (executionTime != null) ExecutionTime += executionTime.Value;
-      }
-    }
-    private void Runs_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IRun> e) {
-      foreach (IRun run in e.Items) {
-        IItem item;
-        run.Results.TryGetValue("Execution Time", out item);
-        TimeSpanValue executionTime = item as TimeSpanValue;
-        if (executionTime != null) {
-          if (Optimizer.ExecutionState == ExecutionState.Started)
-            runsExecutionTime += executionTime.Value;
-          else
-            ExecutionTime += executionTime.Value;
-        }
-      }
-    }
-    private void Runs_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
-      if (Optimizer != null) Optimizer.Runs.RemoveRange(e.Items);
-    }
-    #endregion
-  }
-}
Index: trunk/sources/HeuristicLab.Optimization/3.3/Experiment.cs
===================================================================
--- trunk/sources/HeuristicLab.Optimization/3.3/Experiment.cs	(revision 8967)
+++ 	(revision )
@@ -1,412 +1,0 @@
-#region License Information
-/* HeuristicLab
- * Copyright (C) 2002-2012 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 <http://www.gnu.org/licenses/>.
- */
-#endregion
-
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Linq;
-using HeuristicLab.Collections;
-using HeuristicLab.Common;
-using HeuristicLab.Core;
-using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
-
-namespace HeuristicLab.Optimization {
-  /// <summary>
-  /// An experiment which contains multiple algorithms, batch runs or other experiments.
-  /// </summary>
-  [Item("Experiment", "An experiment which contains multiple algorithms, batch runs or other experiments.")]
-  [Creatable("Testing & Analysis")]
-  [StorableClass]
-  public sealed class Experiment : NamedItem, IOptimizer, IStorableContent {
-    public string Filename { get; set; }
-
-    public static new Image StaticItemImage {
-      get { return HeuristicLab.Common.Resources.VSImageLibrary.Event; }
-    }
-    public override Image ItemImage {
-      get {
-        if (ExecutionState == ExecutionState.Prepared) return HeuristicLab.Common.Resources.VSImageLibrary.ExperimentPrepared;
-        else if (ExecutionState == ExecutionState.Started) return HeuristicLab.Common.Resources.VSImageLibrary.ExperimentStarted;
-        else if (ExecutionState == ExecutionState.Paused) return HeuristicLab.Common.Resources.VSImageLibrary.ExperimentPaused;
-        else if (ExecutionState == ExecutionState.Stopped) return HeuristicLab.Common.Resources.VSImageLibrary.ExperimentStopped;
-        else return base.ItemImage;
-      }
-    }
-
-    [Storable]
-    private ExecutionState executionState;
-    public ExecutionState ExecutionState {
-      get { return executionState; }
-      private set {
-        if (executionState != value) {
-          executionState = value;
-          OnExecutionStateChanged();
-          OnItemImageChanged();
-        }
-      }
-    }
-
-    [Storable]
-    private TimeSpan executionTime;
-    public TimeSpan ExecutionTime {
-      get { return executionTime; }
-      private set {
-        executionTime = value;
-        OnExecutionTimeChanged();
-      }
-    }
-
-    [Storable]
-    private OptimizerList optimizers;
-    public OptimizerList Optimizers {
-      get { return optimizers; }
-    }
-
-    [Storable]
-    private RunCollection runs;
-    public RunCollection Runs {
-      get { return runs; }
-      private set {
-        if (value == null) throw new ArgumentNullException();
-        if (runs != value) {
-          if (runs != null) DeregisterRunsEvents();
-          runs = value;
-          if (runs != null) RegisterRunsEvents();
-        }
-      }
-    }
-
-    public IEnumerable<IOptimizer> NestedOptimizers {
-      get {
-        if (Optimizers == null) yield break;
-
-        foreach (IOptimizer opt in Optimizers) {
-          yield return opt;
-          foreach (IOptimizer nestedOpt in opt.NestedOptimizers)
-            yield return nestedOpt;
-        }
-      }
-    }
-
-    private bool experimentStarted = false;
-    private bool experimentStopped = false;
-
-    public Experiment()
-      : base() {
-      name = ItemName;
-      description = ItemDescription;
-      executionState = ExecutionState.Stopped;
-      executionTime = TimeSpan.Zero;
-      optimizers = new OptimizerList();
-      Runs = new RunCollection { OptimizerName = Name };
-      Initialize();
-    }
-    public Experiment(string name)
-      : base(name) {
-      description = ItemDescription;
-      executionState = ExecutionState.Stopped;
-      executionTime = TimeSpan.Zero;
-      optimizers = new OptimizerList();
-      Runs = new RunCollection { OptimizerName = Name };
-      Initialize();
-    }
-    public Experiment(string name, string description)
-      : base(name, description) {
-      executionState = ExecutionState.Stopped;
-      executionTime = TimeSpan.Zero;
-      optimizers = new OptimizerList();
-      Runs = new RunCollection { OptimizerName = Name };
-      Initialize();
-    }
-    [StorableConstructor]
-    private Experiment(bool deserializing) : base(deserializing) { }
-    [StorableHook(HookType.AfterDeserialization)]
-    private void AfterDeserialization() {
-      Initialize();
-    }
-    private Experiment(Experiment original, Cloner cloner)
-      : base(original, cloner) {
-      executionState = original.executionState;
-      executionTime = original.executionTime;
-      optimizers = cloner.Clone(original.optimizers);
-      runs = cloner.Clone(original.runs);
-
-      experimentStarted = original.experimentStarted;
-      experimentStopped = original.experimentStopped;
-      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 Experiment(this, cloner);
-    }
-
-    private void Initialize() {
-      RegisterOptimizersEvents();
-      foreach (IOptimizer optimizer in optimizers)
-        RegisterOptimizerEvents(optimizer);
-      if (runs != null) RegisterRunsEvents();
-    }
-
-    public void Prepare() {
-      Prepare(false);
-    }
-    public void Prepare(bool clearRuns) {
-      if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused) && (ExecutionState != ExecutionState.Stopped))
-        throw new InvalidOperationException(string.Format("Prepare not allowed in execution state \"{0}\".", ExecutionState));
-      if (Optimizers.Count == 0) return;
-
-      if (clearRuns) runs.Clear();
-
-      experimentStarted = false;
-      experimentStopped = false;
-      foreach (IOptimizer optimizer in Optimizers.Where(x => x.ExecutionState != ExecutionState.Started)) {
-        // a race-condition may occur when the optimizer has changed the state by itself in the meantime
-        try { optimizer.Prepare(clearRuns); }
-        catch (InvalidOperationException) { }
-      }
-    }
-    public void Start() {
-      if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused))
-        throw new InvalidOperationException(string.Format("Start not allowed in execution state \"{0}\".", ExecutionState));
-      if (Optimizers.Count == 0) return;
-
-      experimentStarted = true;
-      experimentStopped = false;
-      IOptimizer optimizer = Optimizers.FirstOrDefault(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused));
-      if (optimizer != null) {
-        // a race-condition may occur when the optimizer has changed the state by itself in the meantime
-        try { optimizer.Start(); }
-        catch (InvalidOperationException) { }
-      }
-    }
-    public void Pause() {
-      if (ExecutionState != ExecutionState.Started)
-        throw new InvalidOperationException(string.Format("Pause not allowed in execution state \"{0}\".", ExecutionState));
-      if (Optimizers.Count == 0) return;
-
-      experimentStarted = false;
-      experimentStopped = false;
-      foreach (IOptimizer optimizer in Optimizers.Where(x => x.ExecutionState == ExecutionState.Started)) {
-        // a race-condition may occur when the optimizer has changed the state by itself in the meantime
-        try { optimizer.Pause(); }
-        catch (InvalidOperationException) { }
-      }
-    }
-    public void Stop() {
-      if ((ExecutionState != ExecutionState.Started) && (ExecutionState != ExecutionState.Paused))
-        throw new InvalidOperationException(string.Format("Stop not allowed in execution state \"{0}\".", ExecutionState));
-      if (Optimizers.Count == 0) return;
-
-      experimentStarted = false;
-      experimentStopped = true;
-      if (Optimizers.Any(x => (x.ExecutionState == ExecutionState.Started) || (x.ExecutionState == ExecutionState.Paused))) {
-        foreach (var optimizer in Optimizers.Where(x => (x.ExecutionState == ExecutionState.Started) || (x.ExecutionState == ExecutionState.Paused))) {
-          // a race-condition may occur when the optimizer has changed the state by itself in the meantime
-          try { optimizer.Stop(); }
-          catch (InvalidOperationException) { }
-        }
-      } else {
-        OnStopped();
-      }
-    }
-
-    #region Events
-    protected override void OnNameChanged() {
-      base.OnNameChanged();
-      Runs.OptimizerName = Name;
-    }
-
-    public event EventHandler ExecutionStateChanged;
-    private void OnExecutionStateChanged() {
-      EventHandler handler = ExecutionStateChanged;
-      if (handler != null) handler(this, EventArgs.Empty);
-    }
-    public event EventHandler ExecutionTimeChanged;
-    private void OnExecutionTimeChanged() {
-      EventHandler handler = ExecutionTimeChanged;
-      if (handler != null) handler(this, EventArgs.Empty);
-    }
-    public event EventHandler Prepared;
-    private void OnPrepared() {
-      ExecutionState = ExecutionState.Prepared;
-      EventHandler handler = Prepared;
-      if (handler != null) handler(this, EventArgs.Empty);
-    }
-    public event EventHandler Started;
-    private void OnStarted() {
-      ExecutionState = ExecutionState.Started;
-      EventHandler handler = Started;
-      if (handler != null) handler(this, EventArgs.Empty);
-    }
-    public event EventHandler Paused;
-    private void OnPaused() {
-      ExecutionState = ExecutionState.Paused;
-      EventHandler handler = Paused;
-      if (handler != null) handler(this, EventArgs.Empty);
-    }
-    public event EventHandler Stopped;
-    private void OnStopped() {
-      ExecutionState = ExecutionState.Stopped;
-      EventHandler handler = Stopped;
-      if (handler != null) handler(this, EventArgs.Empty);
-    }
-    public event EventHandler<EventArgs<Exception>> ExceptionOccurred;
-    private void OnExceptionOccurred(Exception exception) {
-      EventHandler<EventArgs<Exception>> handler = ExceptionOccurred;
-      if (handler != null) handler(this, new EventArgs<Exception>(exception));
-    }
-
-    private void RegisterOptimizersEvents() {
-      Optimizers.CollectionReset += new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_CollectionReset);
-      Optimizers.ItemsAdded += new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsAdded);
-      Optimizers.ItemsRemoved += new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsRemoved);
-      Optimizers.ItemsReplaced += new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsReplaced);
-    }
-    private void DeregisterOptimizersEvents() {
-      Optimizers.CollectionReset -= new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_CollectionReset);
-      Optimizers.ItemsAdded -= new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsAdded);
-      Optimizers.ItemsRemoved -= new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsRemoved);
-      Optimizers.ItemsReplaced -= new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsReplaced);
-    }
-    private void Optimizers_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
-      foreach (IndexedItem<IOptimizer> item in e.OldItems)
-        RemoveOptimizer(item.Value);
-      foreach (IndexedItem<IOptimizer> item in e.Items)
-        AddOptimizer(item.Value);
-    }
-    private void Optimizers_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
-      foreach (IndexedItem<IOptimizer> item in e.Items)
-        AddOptimizer(item.Value);
-    }
-    private void Optimizers_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
-      foreach (IndexedItem<IOptimizer> item in e.Items)
-        RemoveOptimizer(item.Value);
-    }
-    private void Optimizers_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
-      foreach (IndexedItem<IOptimizer> item in e.OldItems)
-        RemoveOptimizer(item.Value);
-      foreach (IndexedItem<IOptimizer> item in e.Items)
-        AddOptimizer(item.Value);
-    }
-    private void AddOptimizer(IOptimizer optimizer) {
-      RegisterOptimizerEvents(optimizer);
-      Runs.AddRange(optimizer.Runs);
-      optimizer.Prepare();
-      if (ExecutionState == ExecutionState.Stopped && optimizer.ExecutionState == ExecutionState.Prepared)
-        OnPrepared();
-    }
-    private void RemoveOptimizer(IOptimizer optimizer) {
-      DeregisterOptimizerEvents(optimizer);
-      Runs.RemoveRange(optimizer.Runs);
-      if (ExecutionState == ExecutionState.Prepared && !optimizers.Any(opt => opt.ExecutionState == ExecutionState.Prepared))
-        OnStopped();
-    }
-
-    private void RegisterOptimizerEvents(IOptimizer optimizer) {
-      optimizer.ExceptionOccurred += new EventHandler<EventArgs<Exception>>(optimizer_ExceptionOccurred);
-      optimizer.ExecutionTimeChanged += new EventHandler(optimizer_ExecutionTimeChanged);
-      optimizer.Paused += new EventHandler(optimizer_Paused);
-      optimizer.Prepared += new EventHandler(optimizer_Prepared);
-      optimizer.Started += new EventHandler(optimizer_Started);
-      optimizer.Stopped += new EventHandler(optimizer_Stopped);
-      optimizer.Runs.CollectionReset += new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_CollectionReset);
-      optimizer.Runs.ItemsAdded += new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_ItemsAdded);
-      optimizer.Runs.ItemsRemoved += new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_ItemsRemoved);
-    }
-    private void DeregisterOptimizerEvents(IOptimizer optimizer) {
-      optimizer.ExceptionOccurred -= new EventHandler<EventArgs<Exception>>(optimizer_ExceptionOccurred);
-      optimizer.ExecutionTimeChanged -= new EventHandler(optimizer_ExecutionTimeChanged);
-      optimizer.Paused -= new EventHandler(optimizer_Paused);
-      optimizer.Prepared -= new EventHandler(optimizer_Prepared);
-      optimizer.Started -= new EventHandler(optimizer_Started);
-      optimizer.Stopped -= new EventHandler(optimizer_Stopped);
-      optimizer.Runs.CollectionReset -= new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_CollectionReset);
-      optimizer.Runs.ItemsAdded -= new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_ItemsAdded);
-      optimizer.Runs.ItemsRemoved -= new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_ItemsRemoved);
-    }
-
-    private readonly object locker = new object();
-    private void optimizer_ExceptionOccurred(object sender, EventArgs<Exception> e) {
-      lock (locker)
-        OnExceptionOccurred(e.Value);
-    }
-    private void optimizer_ExecutionTimeChanged(object sender, EventArgs e) {
-      lock (locker)
-        ExecutionTime = Optimizers.Aggregate(TimeSpan.Zero, (t, o) => t + o.ExecutionTime);
-    }
-    private void optimizer_Paused(object sender, EventArgs e) {
-      lock (locker)
-        if (Optimizers.All(x => x.ExecutionState != ExecutionState.Started)) OnPaused();
-    }
-    private void optimizer_Prepared(object sender, EventArgs e) {
-      lock (locker)
-        if (Optimizers.All(x => x.ExecutionState == ExecutionState.Prepared)) OnPrepared();
-    }
-    private void optimizer_Started(object sender, EventArgs e) {
-      lock (locker)
-        if (ExecutionState != ExecutionState.Started) OnStarted();
-    }
-    private void optimizer_Stopped(object sender, EventArgs e) {
-      lock (locker) {
-        if (experimentStopped) {
-          if (Optimizers.All(x => (x.ExecutionState == ExecutionState.Stopped) || (x.ExecutionState == ExecutionState.Prepared))) OnStopped();
-        } else {
-          if (experimentStarted && Optimizers.Any(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused))) {
-            Optimizers.First(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused)).Start();
-          } else if (Optimizers.All(x => x.ExecutionState == ExecutionState.Stopped)) OnStopped();
-          else if (Optimizers.Any(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused)) && Optimizers.All(o => o.ExecutionState != ExecutionState.Started)) OnPaused();
-        }
-      }
-    }
-    private void optimizer_Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
-      lock (locker) {
-        Runs.RemoveRange(e.OldItems);
-        Runs.AddRange(e.Items);
-      }
-    }
-    private void optimizer_Runs_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IRun> e) {
-      lock (locker)
-        Runs.AddRange(e.Items);
-    }
-    private void optimizer_Runs_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
-      lock (locker)
-        Runs.RemoveRange(e.Items);
-    }
-
-    private void RegisterRunsEvents() {
-      runs.CollectionReset += new CollectionItemsChangedEventHandler<IRun>(Runs_CollectionReset);
-      runs.ItemsRemoved += new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsRemoved);
-    }
-    private void DeregisterRunsEvents() {
-      runs.CollectionReset -= new CollectionItemsChangedEventHandler<IRun>(Runs_CollectionReset);
-      runs.ItemsRemoved -= new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsRemoved);
-    }
-    private void Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
-      foreach (IOptimizer optimizer in Optimizers)
-        optimizer.Runs.RemoveRange(e.OldItems);
-    }
-    private void Runs_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
-      foreach (IOptimizer optimizer in Optimizers)
-        optimizer.Runs.RemoveRange(e.Items);
-    }
-    #endregion
-  }
-}
Index: trunk/sources/HeuristicLab.Optimization/3.3/HeuristicLab.Optimization-3.3.csproj
===================================================================
--- trunk/sources/HeuristicLab.Optimization/3.3/HeuristicLab.Optimization-3.3.csproj	(revision 8967)
+++ trunk/sources/HeuristicLab.Optimization/3.3/HeuristicLab.Optimization-3.3.csproj	(revision 9079)
@@ -121,4 +121,7 @@
     <Compile Include="Interfaces\ISimilarityBasedOperator.cs" />
     <Compile Include="Interfaces\ISolutionSimilarityCalculator.cs" />
+    <Compile Include="MetaOptimizers\BatchRun.cs" />
+    <Compile Include="MetaOptimizers\Experiment.cs" />
+    <Compile Include="MetaOptimizers\TimeLimitRun.cs" />
     <Compile Include="RunCollectionModification\RunCollectionRunRemover.cs" />
     <Compile Include="Plugin.cs" />
@@ -130,5 +133,4 @@
     <None Include="Plugin.cs.frame" />
     <Compile Include="Algorithms\Algorithm.cs" />
-    <Compile Include="BatchRun.cs" />
     <Compile Include="RunCollectionModification\Calculator.cs" />
     <Compile Include="Interfaces\IDiscreteDoubleMatrixModifier.cs" />
@@ -173,5 +175,4 @@
     <Compile Include="RunCollectionConstraints\IRunCollectionConstraint.cs" />
     <Compile Include="OptimizerList.cs" />
-    <Compile Include="Experiment.cs" />
     <Compile Include="Interfaces\IOptimizer.cs" />
     <Compile Include="RunCollection.cs" />
@@ -281,5 +282,5 @@
   -->
   <PropertyGroup>
-   <PreBuildEvent Condition=" '$(OS)' == 'Windows_NT' ">set Path=%25Path%25;$(ProjectDir);$(SolutionDir)
+    <PreBuildEvent Condition=" '$(OS)' == 'Windows_NT' ">set Path=%25Path%25;$(ProjectDir);$(SolutionDir)
 set ProjectDir=$(ProjectDir)
 set SolutionDir=$(SolutionDir)
@@ -288,5 +289,5 @@
 call PreBuildEvent.cmd
 </PreBuildEvent>
-<PreBuildEvent Condition=" '$(OS)' != 'Windows_NT' ">
+    <PreBuildEvent Condition=" '$(OS)' != 'Windows_NT' ">
 export ProjectDir=$(ProjectDir)
 export SolutionDir=$(SolutionDir)
Index: trunk/sources/HeuristicLab.Optimization/3.3/MetaOptimizers/BatchRun.cs
===================================================================
--- trunk/sources/HeuristicLab.Optimization/3.3/MetaOptimizers/BatchRun.cs	(revision 9079)
+++ trunk/sources/HeuristicLab.Optimization/3.3/MetaOptimizers/BatchRun.cs	(revision 9079)
@@ -0,0 +1,450 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2012 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 <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using HeuristicLab.Collections;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Optimization {
+  internal enum BatchRunAction { None, Prepare, Start, Pause, Stop };
+
+  /// <summary>
+  /// A run in which an optimizer is executed a given number of times.
+  /// </summary>
+  [Item("Batch Run", "A run in which an optimizer is executed a given number of times.")]
+  [Creatable("Testing & Analysis")]
+  [StorableClass]
+  public sealed class BatchRun : NamedItem, IOptimizer, IStorableContent {
+    public string Filename { get; set; }
+
+    public static new Image StaticItemImage {
+      get { return HeuristicLab.Common.Resources.VSImageLibrary.Event; }
+    }
+    public override Image ItemImage {
+      get {
+        if (ExecutionState == ExecutionState.Prepared) return HeuristicLab.Common.Resources.VSImageLibrary.BatchRunPrepared;
+        else if (ExecutionState == ExecutionState.Started) return HeuristicLab.Common.Resources.VSImageLibrary.BatchRunStarted;
+        else if (ExecutionState == ExecutionState.Paused) return HeuristicLab.Common.Resources.VSImageLibrary.BatchRunPaused;
+        else if (ExecutionState == ExecutionState.Stopped) return HeuristicLab.Common.Resources.VSImageLibrary.BatchRunStopped;
+        else return base.ItemImage;
+      }
+    }
+
+    [Storable]
+    private ExecutionState executionState;
+    public ExecutionState ExecutionState {
+      get { return executionState; }
+      private set {
+        if (executionState != value) {
+          executionState = value;
+          OnExecutionStateChanged();
+          OnItemImageChanged();
+        }
+      }
+    }
+
+    [Storable]
+    private TimeSpan executionTime;
+    public TimeSpan ExecutionTime {
+      get {
+        if ((Optimizer != null) && (Optimizer.ExecutionState != ExecutionState.Stopped))
+          return executionTime + Optimizer.ExecutionTime;
+        else
+          return executionTime;
+      }
+      private set {
+        executionTime = value;
+        OnExecutionTimeChanged();
+      }
+    }
+
+    [Storable]
+    private TimeSpan runsExecutionTime;
+
+    [Storable]
+    private IOptimizer optimizer;
+    public IOptimizer Optimizer {
+      get { return optimizer; }
+      set {
+        if (optimizer != value) {
+          if (optimizer != null) {
+            DeregisterOptimizerEvents();
+            IEnumerable<IRun> runs = optimizer.Runs;
+            optimizer = null; //necessary to avoid removing the runs from the old optimizer
+            Runs.RemoveRange(runs);
+          }
+          optimizer = value;
+          if (optimizer != null) {
+            RegisterOptimizerEvents();
+            Runs.AddRange(optimizer.Runs);
+          }
+          OnOptimizerChanged();
+          Prepare();
+        }
+      }
+    }
+    // BackwardsCompatibility3.3
+    #region Backwards compatible code (remove with 3.4)
+    [Storable(AllowOneWay = true)]
+    private IAlgorithm algorithm {
+      set { optimizer = value; }
+    }
+    #endregion
+
+    [Storable]
+    private int repetitions;
+    public int Repetitions {
+      get { return repetitions; }
+      set {
+        if (repetitions != value) {
+          repetitions = value;
+          OnRepetitionsChanged();
+          if ((Optimizer != null) && (Optimizer.ExecutionState == ExecutionState.Stopped))
+            Prepare();
+        }
+      }
+    }
+    [Storable]
+    private int repetitionsCounter;
+    public int RepetitionsCounter {
+      get { return repetitionsCounter; }
+      private set {
+        if (value != repetitionsCounter) {
+          repetitionsCounter = value;
+          OnRepetitionsCounterChanged();
+        }
+      }
+    }
+
+    [Storable]
+    private RunCollection runs;
+    public RunCollection Runs {
+      get { return runs; }
+      private set {
+        if (value == null) throw new ArgumentNullException();
+        if (runs != value) {
+          if (runs != null) DeregisterRunsEvents();
+          runs = value;
+          if (runs != null) RegisterRunsEvents();
+        }
+      }
+    }
+
+    public IEnumerable<IOptimizer> NestedOptimizers {
+      get {
+        if (Optimizer == null) yield break;
+
+        yield return Optimizer;
+        foreach (IOptimizer opt in Optimizer.NestedOptimizers)
+          yield return opt;
+      }
+    }
+
+    private BatchRunAction batchRunAction = BatchRunAction.None;
+
+    public BatchRun()
+      : base() {
+      name = ItemName;
+      description = ItemDescription;
+      executionState = ExecutionState.Stopped;
+      executionTime = TimeSpan.Zero;
+      runsExecutionTime = TimeSpan.Zero;
+      repetitions = 10;
+      repetitionsCounter = 0;
+      Runs = new RunCollection { OptimizerName = Name };
+    }
+    public BatchRun(string name)
+      : base(name) {
+      description = ItemDescription;
+      executionState = ExecutionState.Stopped;
+      executionTime = TimeSpan.Zero;
+      runsExecutionTime = TimeSpan.Zero;
+      repetitions = 10;
+      repetitionsCounter = 0;
+      Runs = new RunCollection { OptimizerName = Name };
+    }
+    public BatchRun(string name, string description)
+      : base(name, description) {
+      executionState = ExecutionState.Stopped;
+      executionTime = TimeSpan.Zero;
+      runsExecutionTime = TimeSpan.Zero;
+      repetitions = 10;
+      repetitionsCounter = 0;
+      Runs = new RunCollection { OptimizerName = Name };
+    }
+    [StorableConstructor]
+    private BatchRun(bool deserializing) : base(deserializing) { }
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      Initialize();
+    }
+
+    private BatchRun(BatchRun original, Cloner cloner)
+      : base(original, cloner) {
+      executionState = original.executionState;
+      executionTime = original.executionTime;
+      runsExecutionTime = original.runsExecutionTime;
+      optimizer = cloner.Clone(original.optimizer);
+      repetitions = original.repetitions;
+      repetitionsCounter = original.repetitionsCounter;
+      runs = cloner.Clone(original.runs);
+      batchRunAction = original.batchRunAction;
+      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 BatchRun(this, cloner);
+    }
+
+    private void Initialize() {
+      if (optimizer != null) RegisterOptimizerEvents();
+      if (runs != null) RegisterRunsEvents();
+    }
+
+    public void Prepare() {
+      Prepare(false);
+    }
+    public void Prepare(bool clearRuns) {
+      if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused) && (ExecutionState != ExecutionState.Stopped))
+        throw new InvalidOperationException(string.Format("Prepare not allowed in execution state \"{0}\".", ExecutionState));
+      if (Optimizer != null) {
+        ExecutionTime = TimeSpan.Zero;
+        RepetitionsCounter = 0;
+        if (clearRuns) runs.Clear();
+        batchRunAction = BatchRunAction.Prepare;
+        // a race-condition may occur when the optimizer has changed the state by itself in the meantime
+        try { Optimizer.Prepare(clearRuns); } catch (InvalidOperationException) { }
+      } else {
+        ExecutionState = ExecutionState.Stopped;
+      }
+    }
+    public void Start() {
+      if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused))
+        throw new InvalidOperationException(string.Format("Start not allowed in execution state \"{0}\".", ExecutionState));
+      if (Optimizer == null) return;
+      batchRunAction = BatchRunAction.Start;
+      if (Optimizer.ExecutionState == ExecutionState.Stopped) Optimizer.Prepare();
+      // a race-condition may occur when the optimizer has changed the state by itself in the meantime
+      try { Optimizer.Start(); } catch (InvalidOperationException) { }
+    }
+    public void Pause() {
+      if (ExecutionState != ExecutionState.Started)
+        throw new InvalidOperationException(string.Format("Pause not allowed in execution state \"{0}\".", ExecutionState));
+      if (Optimizer == null) return;
+      batchRunAction = BatchRunAction.Pause;
+      if (Optimizer.ExecutionState != ExecutionState.Started) return;
+      // a race-condition may occur when the optimizer has changed the state by itself in the meantime
+      try { Optimizer.Pause(); } catch (InvalidOperationException) { }
+    }
+    public void Stop() {
+      if ((ExecutionState != ExecutionState.Started) && (ExecutionState != ExecutionState.Paused))
+        throw new InvalidOperationException(string.Format("Stop not allowed in execution state \"{0}\".", ExecutionState));
+      if (Optimizer == null) return;
+      batchRunAction = BatchRunAction.Stop;
+      if (Optimizer.ExecutionState != ExecutionState.Started && Optimizer.ExecutionState != ExecutionState.Paused) {
+        OnStopped();
+        return;
+      }
+      // a race-condition may occur when the optimizer has changed the state by itself in the meantime
+      try { Optimizer.Stop(); } catch (InvalidOperationException) { }
+    }
+
+    #region Events
+    protected override void OnNameChanged() {
+      base.OnNameChanged();
+      runs.OptimizerName = Name;
+    }
+
+    public event EventHandler ExecutionStateChanged;
+    private void OnExecutionStateChanged() {
+      EventHandler handler = ExecutionStateChanged;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler ExecutionTimeChanged;
+    private void OnExecutionTimeChanged() {
+      EventHandler handler = ExecutionTimeChanged;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler OptimizerChanged;
+    private void OnOptimizerChanged() {
+      EventHandler handler = OptimizerChanged;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler RepetitionsChanged;
+    private void OnRepetitionsChanged() {
+      EventHandler handler = RepetitionsChanged;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler RepetetionsCounterChanged;
+    private void OnRepetitionsCounterChanged() {
+      EventHandler handler = RepetetionsCounterChanged;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler Prepared;
+    private void OnPrepared() {
+      batchRunAction = BatchRunAction.None;
+      ExecutionState = ExecutionState.Prepared;
+      EventHandler handler = Prepared;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler Started;
+    private void OnStarted() {
+      // no reset of BatchRunAction.Started, because we need to differ which of the two was started by the user
+      ExecutionState = ExecutionState.Started;
+      EventHandler handler = Started;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler Paused;
+    private void OnPaused() {
+      batchRunAction = BatchRunAction.None;
+      ExecutionState = ExecutionState.Paused;
+      EventHandler handler = Paused;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler Stopped;
+    private void OnStopped() {
+      batchRunAction = BatchRunAction.None;
+      ExecutionState = ExecutionState.Stopped;
+      EventHandler handler = Stopped;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler<EventArgs<Exception>> ExceptionOccurred;
+    private void OnExceptionOccurred(Exception exception) {
+      EventHandler<EventArgs<Exception>> handler = ExceptionOccurred;
+      if (handler != null) handler(this, new EventArgs<Exception>(exception));
+    }
+
+    private void RegisterOptimizerEvents() {
+      optimizer.ExceptionOccurred += new EventHandler<EventArgs<Exception>>(Optimizer_ExceptionOccurred);
+      optimizer.ExecutionTimeChanged += new EventHandler(Optimizer_ExecutionTimeChanged);
+      optimizer.Paused += new EventHandler(Optimizer_Paused);
+      optimizer.Prepared += new EventHandler(Optimizer_Prepared);
+      optimizer.Started += new EventHandler(Optimizer_Started);
+      optimizer.Stopped += new EventHandler(Optimizer_Stopped);
+      optimizer.Runs.CollectionReset += new CollectionItemsChangedEventHandler<IRun>(Optimizer_Runs_CollectionReset);
+      optimizer.Runs.ItemsAdded += new CollectionItemsChangedEventHandler<IRun>(Optimizer_Runs_ItemsAdded);
+      optimizer.Runs.ItemsRemoved += new CollectionItemsChangedEventHandler<IRun>(Optimizer_Runs_ItemsRemoved);
+    }
+    private void DeregisterOptimizerEvents() {
+      optimizer.ExceptionOccurred -= new EventHandler<EventArgs<Exception>>(Optimizer_ExceptionOccurred);
+      optimizer.ExecutionTimeChanged -= new EventHandler(Optimizer_ExecutionTimeChanged);
+      optimizer.Paused -= new EventHandler(Optimizer_Paused);
+      optimizer.Prepared -= new EventHandler(Optimizer_Prepared);
+      optimizer.Started -= new EventHandler(Optimizer_Started);
+      optimizer.Stopped -= new EventHandler(Optimizer_Stopped);
+      optimizer.Runs.CollectionReset -= new CollectionItemsChangedEventHandler<IRun>(Optimizer_Runs_CollectionReset);
+      optimizer.Runs.ItemsAdded -= new CollectionItemsChangedEventHandler<IRun>(Optimizer_Runs_ItemsAdded);
+      optimizer.Runs.ItemsRemoved -= new CollectionItemsChangedEventHandler<IRun>(Optimizer_Runs_ItemsRemoved);
+    }
+    private void Optimizer_ExceptionOccurred(object sender, EventArgs<Exception> e) {
+      OnExceptionOccurred(e.Value);
+    }
+    private void Optimizer_ExecutionTimeChanged(object sender, EventArgs e) {
+      OnExecutionTimeChanged();
+    }
+    private void Optimizer_Paused(object sender, EventArgs e) {
+      if (ExecutionState == ExecutionState.Started) {
+        OnPaused();
+      }
+    }
+    private void Optimizer_Prepared(object sender, EventArgs e) {
+      if (batchRunAction == BatchRunAction.Prepare || ExecutionState == ExecutionState.Stopped) {
+        ExecutionTime = TimeSpan.Zero;
+        runsExecutionTime = TimeSpan.Zero;
+        RepetitionsCounter = 0;
+        OnPrepared();
+      }
+    }
+    private void Optimizer_Started(object sender, EventArgs e) {
+      if (ExecutionState != ExecutionState.Started)
+        OnStarted();
+    }
+    private void Optimizer_Stopped(object sender, EventArgs e) {
+      RepetitionsCounter++;
+      ExecutionTime += runsExecutionTime;
+      runsExecutionTime = TimeSpan.Zero;
+
+      if (batchRunAction == BatchRunAction.Stop) OnStopped();
+      else if (repetitionsCounter >= repetitions) OnStopped();
+      else if (batchRunAction == BatchRunAction.Pause) OnPaused();
+      else if (batchRunAction == BatchRunAction.Start) {
+        Optimizer.Prepare();
+        Optimizer.Start();
+      } else if (executionState == ExecutionState.Started) {
+        // if the batch run hasn't been started but the inner optimizer was run then pause
+        OnPaused();
+      } else OnStopped();
+    }
+    private void Optimizer_Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
+      Runs.RemoveRange(e.OldItems);
+      Runs.AddRange(e.Items);
+    }
+    private void Optimizer_Runs_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IRun> e) {
+      Runs.AddRange(e.Items);
+    }
+    private void Optimizer_Runs_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
+      Runs.RemoveRange(e.Items);
+    }
+
+    private void RegisterRunsEvents() {
+      runs.CollectionReset += new CollectionItemsChangedEventHandler<IRun>(Runs_CollectionReset);
+      runs.ItemsAdded += new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsAdded);
+      runs.ItemsRemoved += new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsRemoved);
+    }
+
+    private void DeregisterRunsEvents() {
+      runs.CollectionReset -= new CollectionItemsChangedEventHandler<IRun>(Runs_CollectionReset);
+      runs.ItemsAdded -= new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsAdded);
+      runs.ItemsRemoved -= new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsRemoved);
+    }
+    private void Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
+      if (Optimizer != null) Optimizer.Runs.RemoveRange(e.OldItems);
+      foreach (IRun run in e.Items) {
+        IItem item;
+        run.Results.TryGetValue("Execution Time", out item);
+        TimeSpanValue executionTime = item as TimeSpanValue;
+        if (executionTime != null) ExecutionTime += executionTime.Value;
+      }
+    }
+    private void Runs_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IRun> e) {
+      foreach (IRun run in e.Items) {
+        IItem item;
+        run.Results.TryGetValue("Execution Time", out item);
+        TimeSpanValue executionTime = item as TimeSpanValue;
+        if (executionTime != null) {
+          if (Optimizer.ExecutionState == ExecutionState.Started)
+            runsExecutionTime += executionTime.Value;
+          else
+            ExecutionTime += executionTime.Value;
+        }
+      }
+    }
+    private void Runs_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
+      if (Optimizer != null) Optimizer.Runs.RemoveRange(e.Items);
+    }
+    #endregion
+  }
+}
Index: trunk/sources/HeuristicLab.Optimization/3.3/MetaOptimizers/Experiment.cs
===================================================================
--- trunk/sources/HeuristicLab.Optimization/3.3/MetaOptimizers/Experiment.cs	(revision 9079)
+++ trunk/sources/HeuristicLab.Optimization/3.3/MetaOptimizers/Experiment.cs	(revision 9079)
@@ -0,0 +1,408 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2012 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 <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using HeuristicLab.Collections;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Optimization {
+  /// <summary>
+  /// An experiment which contains multiple algorithms, batch runs or other experiments.
+  /// </summary>
+  [Item("Experiment", "An experiment which contains multiple algorithms, batch runs or other experiments.")]
+  [Creatable("Testing & Analysis")]
+  [StorableClass]
+  public sealed class Experiment : NamedItem, IOptimizer, IStorableContent {
+    public string Filename { get; set; }
+
+    public static new Image StaticItemImage {
+      get { return HeuristicLab.Common.Resources.VSImageLibrary.Event; }
+    }
+    public override Image ItemImage {
+      get {
+        if (ExecutionState == ExecutionState.Prepared) return HeuristicLab.Common.Resources.VSImageLibrary.ExperimentPrepared;
+        else if (ExecutionState == ExecutionState.Started) return HeuristicLab.Common.Resources.VSImageLibrary.ExperimentStarted;
+        else if (ExecutionState == ExecutionState.Paused) return HeuristicLab.Common.Resources.VSImageLibrary.ExperimentPaused;
+        else if (ExecutionState == ExecutionState.Stopped) return HeuristicLab.Common.Resources.VSImageLibrary.ExperimentStopped;
+        else return base.ItemImage;
+      }
+    }
+
+    [Storable]
+    private ExecutionState executionState;
+    public ExecutionState ExecutionState {
+      get { return executionState; }
+      private set {
+        if (executionState != value) {
+          executionState = value;
+          OnExecutionStateChanged();
+          OnItemImageChanged();
+        }
+      }
+    }
+
+    [Storable]
+    private TimeSpan executionTime;
+    public TimeSpan ExecutionTime {
+      get { return executionTime; }
+      private set {
+        executionTime = value;
+        OnExecutionTimeChanged();
+      }
+    }
+
+    [Storable]
+    private OptimizerList optimizers;
+    public OptimizerList Optimizers {
+      get { return optimizers; }
+    }
+
+    [Storable]
+    private RunCollection runs;
+    public RunCollection Runs {
+      get { return runs; }
+      private set {
+        if (value == null) throw new ArgumentNullException();
+        if (runs != value) {
+          if (runs != null) DeregisterRunsEvents();
+          runs = value;
+          if (runs != null) RegisterRunsEvents();
+        }
+      }
+    }
+
+    public IEnumerable<IOptimizer> NestedOptimizers {
+      get {
+        if (Optimizers == null) yield break;
+
+        foreach (IOptimizer opt in Optimizers) {
+          yield return opt;
+          foreach (IOptimizer nestedOpt in opt.NestedOptimizers)
+            yield return nestedOpt;
+        }
+      }
+    }
+
+    private bool experimentStarted = false;
+    private bool experimentStopped = false;
+
+    public Experiment()
+      : base() {
+      name = ItemName;
+      description = ItemDescription;
+      executionState = ExecutionState.Stopped;
+      executionTime = TimeSpan.Zero;
+      optimizers = new OptimizerList();
+      Runs = new RunCollection { OptimizerName = Name };
+      Initialize();
+    }
+    public Experiment(string name)
+      : base(name) {
+      description = ItemDescription;
+      executionState = ExecutionState.Stopped;
+      executionTime = TimeSpan.Zero;
+      optimizers = new OptimizerList();
+      Runs = new RunCollection { OptimizerName = Name };
+      Initialize();
+    }
+    public Experiment(string name, string description)
+      : base(name, description) {
+      executionState = ExecutionState.Stopped;
+      executionTime = TimeSpan.Zero;
+      optimizers = new OptimizerList();
+      Runs = new RunCollection { OptimizerName = Name };
+      Initialize();
+    }
+    [StorableConstructor]
+    private Experiment(bool deserializing) : base(deserializing) { }
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      Initialize();
+    }
+    private Experiment(Experiment original, Cloner cloner)
+      : base(original, cloner) {
+      executionState = original.executionState;
+      executionTime = original.executionTime;
+      optimizers = cloner.Clone(original.optimizers);
+      runs = cloner.Clone(original.runs);
+
+      experimentStarted = original.experimentStarted;
+      experimentStopped = original.experimentStopped;
+      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 Experiment(this, cloner);
+    }
+
+    private void Initialize() {
+      RegisterOptimizersEvents();
+      foreach (IOptimizer optimizer in optimizers)
+        RegisterOptimizerEvents(optimizer);
+      if (runs != null) RegisterRunsEvents();
+    }
+
+    public void Prepare() {
+      Prepare(false);
+    }
+    public void Prepare(bool clearRuns) {
+      if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused) && (ExecutionState != ExecutionState.Stopped))
+        throw new InvalidOperationException(string.Format("Prepare not allowed in execution state \"{0}\".", ExecutionState));
+      if (Optimizers.Count == 0) return;
+
+      if (clearRuns) runs.Clear();
+
+      experimentStarted = false;
+      experimentStopped = false;
+      foreach (IOptimizer optimizer in Optimizers.Where(x => x.ExecutionState != ExecutionState.Started)) {
+        // a race-condition may occur when the optimizer has changed the state by itself in the meantime
+        try { optimizer.Prepare(clearRuns); } catch (InvalidOperationException) { }
+      }
+    }
+    public void Start() {
+      if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused))
+        throw new InvalidOperationException(string.Format("Start not allowed in execution state \"{0}\".", ExecutionState));
+      if (Optimizers.Count == 0) return;
+
+      experimentStarted = true;
+      experimentStopped = false;
+      IOptimizer optimizer = Optimizers.FirstOrDefault(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused));
+      if (optimizer != null) {
+        // a race-condition may occur when the optimizer has changed the state by itself in the meantime
+        try { optimizer.Start(); } catch (InvalidOperationException) { }
+      }
+    }
+    public void Pause() {
+      if (ExecutionState != ExecutionState.Started)
+        throw new InvalidOperationException(string.Format("Pause not allowed in execution state \"{0}\".", ExecutionState));
+      if (Optimizers.Count == 0) return;
+
+      experimentStarted = false;
+      experimentStopped = false;
+      foreach (IOptimizer optimizer in Optimizers.Where(x => x.ExecutionState == ExecutionState.Started)) {
+        // a race-condition may occur when the optimizer has changed the state by itself in the meantime
+        try { optimizer.Pause(); } catch (InvalidOperationException) { }
+      }
+    }
+    public void Stop() {
+      if ((ExecutionState != ExecutionState.Started) && (ExecutionState != ExecutionState.Paused))
+        throw new InvalidOperationException(string.Format("Stop not allowed in execution state \"{0}\".", ExecutionState));
+      if (Optimizers.Count == 0) return;
+
+      experimentStarted = false;
+      experimentStopped = true;
+      if (Optimizers.Any(x => (x.ExecutionState == ExecutionState.Started) || (x.ExecutionState == ExecutionState.Paused))) {
+        foreach (var optimizer in Optimizers.Where(x => (x.ExecutionState == ExecutionState.Started) || (x.ExecutionState == ExecutionState.Paused))) {
+          // a race-condition may occur when the optimizer has changed the state by itself in the meantime
+          try { optimizer.Stop(); } catch (InvalidOperationException) { }
+        }
+      } else {
+        OnStopped();
+      }
+    }
+
+    #region Events
+    protected override void OnNameChanged() {
+      base.OnNameChanged();
+      Runs.OptimizerName = Name;
+    }
+
+    public event EventHandler ExecutionStateChanged;
+    private void OnExecutionStateChanged() {
+      EventHandler handler = ExecutionStateChanged;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler ExecutionTimeChanged;
+    private void OnExecutionTimeChanged() {
+      EventHandler handler = ExecutionTimeChanged;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler Prepared;
+    private void OnPrepared() {
+      ExecutionState = ExecutionState.Prepared;
+      EventHandler handler = Prepared;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler Started;
+    private void OnStarted() {
+      ExecutionState = ExecutionState.Started;
+      EventHandler handler = Started;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler Paused;
+    private void OnPaused() {
+      ExecutionState = ExecutionState.Paused;
+      EventHandler handler = Paused;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler Stopped;
+    private void OnStopped() {
+      ExecutionState = ExecutionState.Stopped;
+      EventHandler handler = Stopped;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler<EventArgs<Exception>> ExceptionOccurred;
+    private void OnExceptionOccurred(Exception exception) {
+      EventHandler<EventArgs<Exception>> handler = ExceptionOccurred;
+      if (handler != null) handler(this, new EventArgs<Exception>(exception));
+    }
+
+    private void RegisterOptimizersEvents() {
+      Optimizers.CollectionReset += new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_CollectionReset);
+      Optimizers.ItemsAdded += new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsAdded);
+      Optimizers.ItemsRemoved += new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsRemoved);
+      Optimizers.ItemsReplaced += new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsReplaced);
+    }
+    private void DeregisterOptimizersEvents() {
+      Optimizers.CollectionReset -= new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_CollectionReset);
+      Optimizers.ItemsAdded -= new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsAdded);
+      Optimizers.ItemsRemoved -= new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsRemoved);
+      Optimizers.ItemsReplaced -= new CollectionItemsChangedEventHandler<IndexedItem<IOptimizer>>(Optimizers_ItemsReplaced);
+    }
+    private void Optimizers_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
+      foreach (IndexedItem<IOptimizer> item in e.OldItems)
+        RemoveOptimizer(item.Value);
+      foreach (IndexedItem<IOptimizer> item in e.Items)
+        AddOptimizer(item.Value);
+    }
+    private void Optimizers_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
+      foreach (IndexedItem<IOptimizer> item in e.Items)
+        AddOptimizer(item.Value);
+    }
+    private void Optimizers_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
+      foreach (IndexedItem<IOptimizer> item in e.Items)
+        RemoveOptimizer(item.Value);
+    }
+    private void Optimizers_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<IOptimizer>> e) {
+      foreach (IndexedItem<IOptimizer> item in e.OldItems)
+        RemoveOptimizer(item.Value);
+      foreach (IndexedItem<IOptimizer> item in e.Items)
+        AddOptimizer(item.Value);
+    }
+    private void AddOptimizer(IOptimizer optimizer) {
+      RegisterOptimizerEvents(optimizer);
+      Runs.AddRange(optimizer.Runs);
+      optimizer.Prepare();
+      if (ExecutionState == ExecutionState.Stopped && optimizer.ExecutionState == ExecutionState.Prepared)
+        OnPrepared();
+    }
+    private void RemoveOptimizer(IOptimizer optimizer) {
+      DeregisterOptimizerEvents(optimizer);
+      Runs.RemoveRange(optimizer.Runs);
+      if (ExecutionState == ExecutionState.Prepared && !optimizers.Any(opt => opt.ExecutionState == ExecutionState.Prepared))
+        OnStopped();
+    }
+
+    private void RegisterOptimizerEvents(IOptimizer optimizer) {
+      optimizer.ExceptionOccurred += new EventHandler<EventArgs<Exception>>(optimizer_ExceptionOccurred);
+      optimizer.ExecutionTimeChanged += new EventHandler(optimizer_ExecutionTimeChanged);
+      optimizer.Paused += new EventHandler(optimizer_Paused);
+      optimizer.Prepared += new EventHandler(optimizer_Prepared);
+      optimizer.Started += new EventHandler(optimizer_Started);
+      optimizer.Stopped += new EventHandler(optimizer_Stopped);
+      optimizer.Runs.CollectionReset += new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_CollectionReset);
+      optimizer.Runs.ItemsAdded += new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_ItemsAdded);
+      optimizer.Runs.ItemsRemoved += new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_ItemsRemoved);
+    }
+    private void DeregisterOptimizerEvents(IOptimizer optimizer) {
+      optimizer.ExceptionOccurred -= new EventHandler<EventArgs<Exception>>(optimizer_ExceptionOccurred);
+      optimizer.ExecutionTimeChanged -= new EventHandler(optimizer_ExecutionTimeChanged);
+      optimizer.Paused -= new EventHandler(optimizer_Paused);
+      optimizer.Prepared -= new EventHandler(optimizer_Prepared);
+      optimizer.Started -= new EventHandler(optimizer_Started);
+      optimizer.Stopped -= new EventHandler(optimizer_Stopped);
+      optimizer.Runs.CollectionReset -= new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_CollectionReset);
+      optimizer.Runs.ItemsAdded -= new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_ItemsAdded);
+      optimizer.Runs.ItemsRemoved -= new CollectionItemsChangedEventHandler<IRun>(optimizer_Runs_ItemsRemoved);
+    }
+
+    private readonly object locker = new object();
+    private void optimizer_ExceptionOccurred(object sender, EventArgs<Exception> e) {
+      lock (locker)
+        OnExceptionOccurred(e.Value);
+    }
+    private void optimizer_ExecutionTimeChanged(object sender, EventArgs e) {
+      lock (locker)
+        ExecutionTime = Optimizers.Aggregate(TimeSpan.Zero, (t, o) => t + o.ExecutionTime);
+    }
+    private void optimizer_Paused(object sender, EventArgs e) {
+      lock (locker)
+        if (Optimizers.All(x => x.ExecutionState != ExecutionState.Started)) OnPaused();
+    }
+    private void optimizer_Prepared(object sender, EventArgs e) {
+      lock (locker)
+        if (Optimizers.All(x => x.ExecutionState == ExecutionState.Prepared)) OnPrepared();
+    }
+    private void optimizer_Started(object sender, EventArgs e) {
+      lock (locker)
+        if (ExecutionState != ExecutionState.Started) OnStarted();
+    }
+    private void optimizer_Stopped(object sender, EventArgs e) {
+      lock (locker) {
+        if (experimentStopped) {
+          if (Optimizers.All(x => (x.ExecutionState == ExecutionState.Stopped) || (x.ExecutionState == ExecutionState.Prepared))) OnStopped();
+        } else {
+          if (experimentStarted && Optimizers.Any(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused))) {
+            Optimizers.First(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused)).Start();
+          } else if (Optimizers.All(x => x.ExecutionState == ExecutionState.Stopped)) OnStopped();
+          else if (Optimizers.Any(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused)) && Optimizers.All(o => o.ExecutionState != ExecutionState.Started)) OnPaused();
+        }
+      }
+    }
+    private void optimizer_Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
+      lock (locker) {
+        Runs.RemoveRange(e.OldItems);
+        Runs.AddRange(e.Items);
+      }
+    }
+    private void optimizer_Runs_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IRun> e) {
+      lock (locker)
+        Runs.AddRange(e.Items);
+    }
+    private void optimizer_Runs_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
+      lock (locker)
+        Runs.RemoveRange(e.Items);
+    }
+
+    private void RegisterRunsEvents() {
+      runs.CollectionReset += new CollectionItemsChangedEventHandler<IRun>(Runs_CollectionReset);
+      runs.ItemsRemoved += new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsRemoved);
+    }
+    private void DeregisterRunsEvents() {
+      runs.CollectionReset -= new CollectionItemsChangedEventHandler<IRun>(Runs_CollectionReset);
+      runs.ItemsRemoved -= new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsRemoved);
+    }
+    private void Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
+      foreach (IOptimizer optimizer in Optimizers)
+        optimizer.Runs.RemoveRange(e.OldItems);
+    }
+    private void Runs_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
+      foreach (IOptimizer optimizer in Optimizers)
+        optimizer.Runs.RemoveRange(e.Items);
+    }
+    #endregion
+  }
+}
Index: trunk/sources/HeuristicLab.Optimization/3.3/MetaOptimizers/TimeLimitRun.cs
===================================================================
--- trunk/sources/HeuristicLab.Optimization/3.3/MetaOptimizers/TimeLimitRun.cs	(revision 9079)
+++ trunk/sources/HeuristicLab.Optimization/3.3/MetaOptimizers/TimeLimitRun.cs	(revision 9079)
@@ -0,0 +1,402 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2012 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 <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Linq;
+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 {
+  internal enum TimeLimitedRunAction { None, Prepare, Start, Pause, Stop };
+
+  /// <summary>
+  /// A run in which an algorithm is executed for a certain maximum time only.
+  /// </summary>
+  [Item("TimeLimit Run", "A run in which an optimizer is executed a certain maximum time.")]
+  [Creatable("Testing & Analysis")]
+  [StorableClass]
+  public sealed class TimeLimitRun : NamedItem, IOptimizer, IStorableContent, INotifyPropertyChanged {
+    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<TimeSpan> snapshotTimes;
+    public ObservableList<TimeSpan> 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<IOptimizer> 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<TimeSpan>(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<TimeSpan>(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<TimeSpan>(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<TimeSpan>(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<IndexedItem<TimeSpan>> 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() {
+      Algorithm.Start();
+    }
+    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() {
+      var handler = Paused;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler Stopped;
+    private void OnStopped() {
+      var handler = Stopped;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    public event EventHandler<EventArgs<Exception>> ExceptionOccurred;
+    private void OnExceptionOccurred(Exception exception) {
+      var handler = ExceptionOccurred;
+      if (handler != null) handler(this, new EventArgs<Exception>(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<Exception> 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);
+      if (pausedForSnapshot || pausedForTermination) {
+        pausedForSnapshot = pausedForTermination = false;
+        MakeSnapshot();
+        FindNextSnapshotTimeIndex(ExecutionTime);
+      }
+      OnPaused();
+      if (action == ExecutionState.Started) Algorithm.Start();
+      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;
+      Run run;
+      if (StoreAlgorithmInEachSnapshot) {
+        var temporarilySetStoreFlag = !Algorithm.StoreAlgorithmInEachRun;
+        if (temporarilySetStoreFlag) Algorithm.StoreAlgorithmInEachRun = true;
+        run = new Run(runName, Algorithm);
+        if (temporarilySetStoreFlag) Algorithm.StoreAlgorithmInEachRun = false;
+      } else {
+        // duplicate code of Run.Initialize - necessary, because Algorithm property doesn't have a setter
+        run = new Run() { Name = runName };
+        var parameters = new Dictionary<string, IItem>();
+        var results = new Dictionary<string, IItem>();
+        algorithm.CollectParameterValues(parameters);
+        algorithm.CollectResultValues(results);
+        var cloner = new Cloner();
+        foreach (var kvp in parameters.Select(x => new KeyValuePair<string, IItem>(x.Key, cloner.Clone(x.Value))).ToDictionary(x => x.Key, x => x.Value))
+          run.Parameters.Add(kvp);
+        foreach (var kvp in results.Select(x => new KeyValuePair<string, IItem>(x.Key, cloner.Clone(x.Value))).ToDictionary(x => x.Key, x => x.Value))
+          run.Results.Add(kvp);
+      }
+      snapshots.Add(run);
+    }
+  }
+}
