Changeset 12856
- Timestamp:
- 08/13/15 14:19:29 (9 years ago)
- Location:
- branches/PerformanceComparison/HeuristicLab.Analysis/3.3
- Files:
-
- 1 edited
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
branches/PerformanceComparison/HeuristicLab.Analysis/3.3/HeuristicLab.Analysis-3.3.csproj
r12831 r12856 199 199 <Compile Include="MultiObjective\RankBasedParetoFrontAnalyzer.cs" /> 200 200 <Compile Include="MultiObjective\ParetoFrontAnalyzer.cs" /> 201 <Compile Include="Optimizers\AlgorithmIterator.cs" /> 201 202 <Compile Include="Optimizers\IRRestarter.cs" /> 202 203 <Compile Include="Plugin.cs" /> -
branches/PerformanceComparison/HeuristicLab.Analysis/3.3/Optimizers/AlgorithmIterator.cs
r12839 r12856 21 21 22 22 using System; 23 using System.Collections.Generic;24 using System.ComponentModel;25 using System.Drawing;26 23 using System.Linq; 24 using System.Threading; 27 25 using HeuristicLab.Common; 28 using HeuristicLab.Common.Resources;29 26 using HeuristicLab.Core; 30 27 using HeuristicLab.Data; 31 28 using HeuristicLab.Optimization; 29 using HeuristicLab.Parameters; 32 30 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable; 31 using System.Threading.Tasks; 33 32 34 33 namespace HeuristicLab.Analysis { 35 public enum TerminationCriterium { OnlyByTime, OnlyByEvaluations, OnlyByTarget, ByTargetAndTime, ByTargetAndEvaluations, ByTimeAndEvaluations, WhicheverHitsFirst, WhicheverHitsLast }36 34 /// <summary> 37 35 /// A run in which an algorithm is executed for a certain maximum time only. 38 36 /// </summary> 39 [Item(" Independent Random Restarter", "An optimizerthat repeats an algorithm until either a certain target value is reached or a maximum budget is exceeded.")]40 [Creatable(CreatableAttribute.Categories.TestingAndAnalysis, Priority = 11 7)]37 [Item("Algorithm Iterator", "An algorithm that repeats an algorithm until either a certain target value is reached or a maximum budget is exceeded.")] 38 [Creatable(CreatableAttribute.Categories.TestingAndAnalysis, Priority = 116)] 41 39 [StorableClass] 42 public sealed class IndepdentRandomRestarter : NamedItem, IOptimizer, IStorableContent, INotifyPropertyChanged{40 public sealed class AlgorithmIterator : Algorithm, IStorableContent { 43 41 private const string ExecutionTimeResultName = "Execution Time"; 44 42 private const string BestQualityResultName = "BestQuality"; … … 48 46 private const string QualityPerClockResultName = "QualityPerClock"; 49 47 private const string QualityPerEvaluationsResultName = "QualityPerEvaluations"; 48 private const string EvaluationsResultName = "Evaluations"; 50 49 51 50 public string Filename { get; set; } 52 51 53 #region ItemImage 54 public static new Image StaticItemImage { 55 get { return VSImageLibrary.Event; } 56 } 57 public override Image ItemImage { 58 get { return (Algorithm != null) ? Algorithm.ItemImage : VSImageLibrary.ExecutableStopped; } 59 } 60 #endregion 52 public override Type ProblemType { get { return typeof(ISingleObjectiveHeuristicOptimizationProblem); } } 53 public new ISingleObjectiveHeuristicOptimizationProblem Problem { 54 get { return (ISingleObjectiveHeuristicOptimizationProblem)base.Problem; } 55 set { 56 base.Problem = value; 57 if (Algorithm != null) Algorithm.Problem = Problem; 58 if (Problem != null) UpdateTargetValueFromBestKnownQuality(); 59 } 60 } 61 62 public IValueParameter<TimeSpanValue> MaximumExecutionTimeParameter { 63 get { return (IValueParameter<TimeSpanValue>)Parameters["MaximumExecutionTime"]; } 64 } 65 66 public IValueParameter<IntValue> MaximumEvaluationsParameter { 67 get { return (IValueParameter<IntValue>)Parameters["MaximumEvaluations"]; } 68 } 69 70 public IValueParameter<DoubleValue> TargetValueParameter { 71 get { return (IValueParameter<DoubleValue>)Parameters["TargetValue"]; } 72 } 73 74 private IFixedValueParameter<DoubleValue> MoveCostPerSolutionParameter { 75 get { return (IFixedValueParameter<DoubleValue>)Parameters["MoveCostPerSolution"]; } 76 } 77 78 private IFixedValueParameter<BoolValue> StoreSolutionInRunParameter { 79 get { return (IFixedValueParameter<BoolValue>)Parameters["StoreSolutionInRun"]; } 80 } 81 82 private IValueParameter<IAlgorithm> AlgorithmParameter { 83 get { return (IValueParameter<IAlgorithm>)Parameters["Algorithm"]; } 84 } 61 85 62 86 [Storable] 63 private TerminationCriterium terminationCriterium; 64 public TerminationCriterium TerminationCriterium { 65 get { return terminationCriterium; } 66 set { 67 if (terminationCriterium == value) return; 68 terminationCriterium = value; 69 OnPropertyChanged("TerminationCriterium"); 70 } 71 } 72 73 [Storable] 74 private TimeSpan maximumExecutionTime; 75 public TimeSpan MaximumExecutionTime { 76 get { return maximumExecutionTime; } 77 set { 78 if (maximumExecutionTime == value) return; 79 maximumExecutionTime = value; 80 OnPropertyChanged("MaximumExecutionTime"); 81 } 82 } 83 84 [Storable] 85 private int maximumEvaluations; 86 public int MaximumEvaluations { 87 get { return maximumEvaluations; } 88 set { 89 if (maximumEvaluations == value) return; 90 maximumEvaluations = value; 91 OnPropertyChanged("MaximumEvaluations"); 92 } 93 } 94 95 [Storable] 96 private double targetValue; 97 public double TargetValue { 98 get { return targetValue; } 99 set { 100 if (targetValue == value) return; 101 targetValue = value; 102 OnPropertyChanged("TargetValue"); 103 } 104 } 105 106 [Storable] 107 private bool maximization; 108 public bool Maximization { 109 get { return maximization; } 110 set { 111 if (maximization == value) return; 112 maximization = value; 113 OnPropertyChanged("Maximization"); 114 } 115 } 116 117 [Storable] 118 private double moveCostPerSolution; 119 public double MoveCostPerSolution { 120 get { return moveCostPerSolution; } 121 set { 122 if (moveCostPerSolution == value) return; 123 moveCostPerSolution = value; 124 perEvaluationsAnalyzer.MoveCostPerSolutionParameter.Value = new DoubleValue(moveCostPerSolution); 125 OnPropertyChanged("MoveCostPerSolution"); 126 } 127 } 128 129 [Storable] 130 private bool storeSolutionInRun; 131 public bool StoreSolutionInRun { 132 get { return storeSolutionInRun; } 133 set { 134 if (storeSolutionInRun == value) return; 135 storeSolutionInRun = value; 136 OnPropertyChanged("StoreSolutionInRun"); 137 } 87 private ResultCollection results; 88 public override ResultCollection Results { 89 get { return results; } 138 90 } 139 91 … … 143 95 private QualityPerEvaluationsAnalyzer perEvaluationsAnalyzer; 144 96 145 [Storable] 146 private ExecutionState executionState; 147 public ExecutionState ExecutionState { 148 get { return executionState; } 149 private set { 150 if (executionState != value) { 151 executionState = value; 152 OnExecutionStateChanged(); 153 OnItemImageChanged(); 154 } 155 } 156 } 157 158 private TimeSpan lastAlgorithmExecutionTime; 159 [Storable] 160 private TimeSpan executionTime; 161 public TimeSpan ExecutionTime { 162 get { return executionTime; } 163 set { 164 if (executionTime == value) return; 165 executionTime = value; 166 OnPropertyChanged("ExecutionTime"); 167 OnExecutionTimeChanged(); 168 } 169 } 170 171 [Storable] 172 private double evaluations; 173 public double Evaluations { 174 get { return evaluations; } 175 set { 176 if (evaluations == value) return; 177 evaluations = value; 178 OnPropertyChanged("Evaluations"); 179 } 180 } 181 182 [Storable] 183 private double bestSoFar; 184 public double BestSoFar { 185 get { return bestSoFar; } 186 set { 187 if (bestSoFar == value) return; 188 bestSoFar = value; 189 OnPropertyChanged("BestSoFar"); 190 } 191 } 192 193 [Storable] 194 private IRun currentRun; 195 public IRun CurrentRun { 196 get { return currentRun; } 197 private set { 198 if (currentRun == value) return; 199 currentRun = value; 200 OnPropertyChanged("CurrentRun"); 201 } 97 public double MoveCostPerSolution { 98 get { return MoveCostPerSolutionParameter.Value.Value; } 99 set { MoveCostPerSolutionParameter.Value.Value = value; } 100 } 101 102 public bool StoreSolutionInRun { 103 get { return StoreSolutionInRunParameter.Value.Value; } 104 set { StoreSolutionInRunParameter.Value.Value = value; } 202 105 } 203 106 … … 214 117 algorithm = value; 215 118 if (algorithm != null) { 119 if (algorithm.ExecutionState != ExecutionState.Prepared) 120 algorithm.Prepare(true); 121 algorithm.Problem = Problem; 216 122 RegisterAlgorithmEvents(); 217 123 AddAlgorithmAnalyzers(); 218 124 } 219 OnPropertyChanged("Algorithm"); 125 if (AlgorithmParameter.Value != algorithm) 126 AlgorithmParameter.Value = algorithm; 220 127 Prepare(); 221 128 } 222 129 } 223 130 224 [Storable] 225 private RunCollection runs; 226 public RunCollection Runs { 227 get { return runs; } 228 private set { 229 if (value == null) throw new ArgumentNullException(); 230 if (runs == value) return; 231 runs = value; 232 OnPropertyChanged("Runs"); 233 } 234 } 235 236 public IEnumerable<IOptimizer> NestedOptimizers { 237 get { 238 if (Algorithm == null) yield break; 239 yield return Algorithm; 240 foreach (var opt in Algorithm.NestedOptimizers) 241 yield return opt; 242 } 131 private bool Maximization { 132 get { return Problem != null && ((IValueParameter<BoolValue>)Problem.MaximizationParameter).Value.Value; } 243 133 } 244 134 245 135 private bool IsFinished { 246 136 get { 247 var timeHit = ExecutionTime >= MaximumExecutionTime; 248 var evalHit = Evaluations >= MaximumEvaluations; 249 var targetHit = (Maximization && BestSoFar >= TargetValue || !Maximization && BestSoFar <= TargetValue); 250 251 return timeHit && evalHit && targetHit 252 || timeHit && (TerminationCriterium == TerminationCriterium.OnlyByTime 253 || TerminationCriterium == TerminationCriterium.WhicheverHitsFirst 254 || TerminationCriterium == TerminationCriterium.ByTargetAndTime 255 || TerminationCriterium == TerminationCriterium.ByTimeAndEvaluations) 256 || evalHit && (TerminationCriterium == TerminationCriterium.OnlyByEvaluations 257 || TerminationCriterium == TerminationCriterium.WhicheverHitsFirst 258 || TerminationCriterium == TerminationCriterium.ByTargetAndEvaluations 259 || TerminationCriterium == TerminationCriterium.ByTimeAndEvaluations) 260 || targetHit && (TerminationCriterium == TerminationCriterium.OnlyByTarget 261 || TerminationCriterium == TerminationCriterium.WhicheverHitsFirst 262 || TerminationCriterium == TerminationCriterium.ByTargetAndTime 263 || TerminationCriterium == TerminationCriterium.ByTargetAndEvaluations); 264 } 265 } 266 267 private ISingleObjectiveHeuristicOptimizationProblem problem; 137 var executionTime = Results.ContainsKey(ExecutionTimeResultName) ? ((TimeSpanValue)Results[ExecutionTimeResultName].Value).Value : TimeSpan.Zero; 138 var evaluations = Results.ContainsKey(EvaluationsResultName) ? ((DoubleValue)Results[EvaluationsResultName].Value).Value : 0; 139 var bestQuality = Results.ContainsKey(BestQualityResultName) ? ((DoubleValue)Results[BestQualityResultName].Value).Value 140 : (Maximization ? double.MinValue : double.MaxValue); 141 var targetValue = TargetValueParameter.Value != null ? TargetValueParameter.Value.Value 142 : Maximization ? double.MaxValue : double.MinValue; 143 144 var timeHit = MaximumExecutionTimeParameter.Value != null && executionTime >= MaximumExecutionTimeParameter.Value.Value; 145 var evalHit = MaximumEvaluationsParameter.Value != null && evaluations >= MaximumEvaluationsParameter.Value.Value; 146 var targetHit = Maximization && bestQuality >= targetValue || !Maximization && bestQuality <= targetValue; 147 148 return timeHit || evalHit || targetHit; 149 } 150 } 268 151 269 152 [StorableConstructor] 270 private IndepdentRandomRestarter(bool deserializing) : base(deserializing) { }271 private IndepdentRandomRestarter(IndepdentRandomRestarter original, Cloner cloner)153 private AlgorithmIterator(bool deserializing) : base(deserializing) { } 154 private AlgorithmIterator(AlgorithmIterator original, Cloner cloner) 272 155 : base(original, cloner) { 273 terminationCriterium = original.terminationCriterium; 274 maximumExecutionTime = original.maximumExecutionTime; 275 maximumEvaluations = original.maximumEvaluations; 276 moveCostPerSolution = original.moveCostPerSolution; 277 storeSolutionInRun = original.storeSolutionInRun; 278 targetValue = original.targetValue; 279 maximization = original.maximization; 280 executionTime = original.executionTime; 281 evaluations = original.evaluations; 282 bestSoFar = original.bestSoFar; 283 lastAlgorithmExecutionTime = original.lastAlgorithmExecutionTime; 284 156 results = cloner.Clone(original.Results); 285 157 perClockAnalyzer = cloner.Clone(original.perClockAnalyzer); 286 158 perEvaluationsAnalyzer = cloner.Clone(original.perEvaluationsAnalyzer); 287 159 288 algorithm = cloner.Clone(original.algorithm); 289 runs = cloner.Clone(original.runs); 290 291 ExecutionState = original.ExecutionState; 292 293 Initialize(); 294 } 295 public IndepdentRandomRestarter() 160 RegisterEventHandlers(); 161 } 162 public AlgorithmIterator() 296 163 : base() { 297 name = ItemName; 298 description = ItemDescription; 299 terminationCriterium = TerminationCriterium.ByTargetAndEvaluations; 300 maximumExecutionTime = TimeSpan.FromMinutes(1); 301 maximumEvaluations = 10000000; // 10 mio 302 moveCostPerSolution = 1; 303 storeSolutionInRun = false; 304 targetValue = 0; 305 maximization = false; 306 executionTime = TimeSpan.Zero; 307 evaluations = 0; 308 bestSoFar = double.NaN; 309 lastAlgorithmExecutionTime = TimeSpan.Zero; 164 results = new ResultCollection(); 165 Parameters.Add(new OptionalValueParameter<TimeSpanValue>("MaximumExecutionTime", "The maximum wall-clock time that the algorithm should run.")); 166 Parameters.Add(new OptionalValueParameter<IntValue>("MaximumEvaluations", "The maximum number of function evaluations that the algorithm should run.", new IntValue(100000000))); 167 Parameters.Add(new OptionalValueParameter<DoubleValue>("TargetValue", "The target value that the algorithm should run for.")); 168 Parameters.Add(new FixedValueParameter<DoubleValue>("MoveCostPerSolution", "The amount of solution evaluation equivalents of a single move. Use 1 for a black-box scenario.", new DoubleValue(1))); 169 Parameters.Add(new FixedValueParameter<BoolValue>("StoreSolutionInRun", "Whether the solution data types should be kept in the run.")); 170 Parameters.Add(new ValueParameter<IAlgorithm>("Algorithm", "The algorithm to iterate.") { GetsCollected = false }); // due to storage efficiency, by default we don't want to store the algorithm instance in the run 310 171 311 172 perClockAnalyzer = new QualityPerClockAnalyzer(); 312 173 perEvaluationsAnalyzer = new QualityPerEvaluationsAnalyzer(); 313 174 314 Runs = new RunCollection { OptimizerName = Name }; 315 Initialize(); 316 } 317 public IndepdentRandomRestarter(string name) 318 : base(name) { 319 description = ItemDescription; 320 terminationCriterium = TerminationCriterium.ByTargetAndEvaluations; 321 maximumExecutionTime = TimeSpan.FromMinutes(1); 322 maximumEvaluations = 10000000; // 10 mio 323 moveCostPerSolution = 1; 324 storeSolutionInRun = false; 325 targetValue = 0; 326 maximization = false; 327 executionTime = TimeSpan.Zero; 328 evaluations = 0; 329 bestSoFar = double.NaN; 330 lastAlgorithmExecutionTime = TimeSpan.Zero; 331 332 perClockAnalyzer = new QualityPerClockAnalyzer(); 333 perEvaluationsAnalyzer = new QualityPerEvaluationsAnalyzer(); 334 335 Runs = new RunCollection { OptimizerName = Name }; 336 Initialize(); 337 } 338 public IndepdentRandomRestarter(string name, string description) 339 : base(name, description) { 340 terminationCriterium = TerminationCriterium.ByTargetAndEvaluations; 341 maximumExecutionTime = TimeSpan.FromMinutes(1); 342 maximumEvaluations = 10000000; // 10 mio 343 moveCostPerSolution = 1; 344 storeSolutionInRun = false; 345 targetValue = 0; 346 maximization = false; 347 executionTime = TimeSpan.Zero; 348 evaluations = 0; 349 bestSoFar = double.NaN; 350 lastAlgorithmExecutionTime = TimeSpan.Zero; 351 352 perClockAnalyzer = new QualityPerClockAnalyzer(); 353 perEvaluationsAnalyzer = new QualityPerEvaluationsAnalyzer(); 354 355 Runs = new RunCollection { OptimizerName = Name }; 356 Initialize(); 175 RegisterEventHandlers(); 357 176 } 358 177 359 178 public override IDeepCloneable Clone(Cloner cloner) { 360 179 if (ExecutionState == ExecutionState.Started) throw new InvalidOperationException(string.Format("Clone not allowed in execution state \"{0}\".", ExecutionState)); 361 return new IndepdentRandomRestarter(this, cloner);180 return new AlgorithmIterator(this, cloner); 362 181 } 363 182 364 183 [StorableHook(HookType.AfterDeserialization)] 365 184 private void AfterDeserialization() { 366 Initialize(); 367 } 368 369 private void Initialize() { 370 if (algorithm != null) RegisterAlgorithmEvents(); 371 } 372 373 private void Reset() { 374 ExecutionTime = TimeSpan.Zero; 375 Evaluations = 0; 376 BestSoFar = double.NaN; 377 lastAlgorithmExecutionTime = TimeSpan.Zero; 378 379 CurrentRun = null; 380 } 381 382 public void Prepare() { 383 Prepare(false); 384 } 385 public void Prepare(bool clearRuns) { 386 if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused) && (ExecutionState != ExecutionState.Stopped)) 387 throw new InvalidOperationException(string.Format("Prepare not allowed in execution state \"{0}\".", ExecutionState)); 388 Reset(); 389 390 if (Algorithm != null) { 391 Algorithm.Prepare(clearRuns); 392 ExecutionState = ExecutionState.Prepared; 393 OnPrepared(); 394 } 395 } 396 public void Start() { 397 if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused)) 398 throw new InvalidOperationException(string.Format("Start not allowed in execution state \"{0}\".", ExecutionState)); 399 400 if (ExecutionState == ExecutionState.Prepared) { 401 CurrentRun = new Run(Algorithm) { 402 Name = Algorithm.Name + " IRRRun" + Runs.Count 403 }; 404 if (!CurrentRun.Results.ContainsKey(ExecutionTimeResultName)) 405 CurrentRun.Results.Add(ExecutionTimeResultName, new TimeSpanValue(TimeSpan.Zero)); 406 // use double instead of int, otherwise one might run into int.MaxValue (at least with moves) 407 CurrentRun.Results.Add(EvaluatedSolutionsResultName, new DoubleValue(0)); 408 CurrentRun.Results.Add(EvaluatedMovesResultName, new DoubleValue(0)); 409 CurrentRun.Results.Add(BestQualityResultName, new DoubleValue(Maximization ? double.MinValue : double.MaxValue)); 410 CurrentRun.Results.Add(RandomRestartsResultName, new IntValue(0)); 411 } 412 if (Algorithm.ExecutionState == ExecutionState.Stopped) 185 RegisterEventHandlers(); 186 } 187 188 #region Register Event Handlers 189 protected override void RegisterProblemEvents() { 190 var bkParam = Problem.BestKnownQualityParameter as IValueParameter<DoubleValue>; 191 if (bkParam != null) { 192 bkParam.ValueChanged += Problem_BestKnownQualityParameter_ValueChanged; 193 } 194 base.RegisterProblemEvents(); 195 } 196 protected override void DeregisterProblemEvents() { 197 base.DeregisterProblemEvents(); 198 var bkParam = Problem.BestKnownQualityParameter as IValueParameter<DoubleValue>; 199 if (bkParam != null) { 200 bkParam.ValueChanged -= Problem_BestKnownQualityParameter_ValueChanged; 201 } 202 } 203 private void RegisterAlgorithmEvents() { 204 Algorithm.ExceptionOccurred += Algorithm_ExceptionOccurred; 205 Algorithm.Paused += Algorithm_Paused; 206 Algorithm.Stopped += Algorithm_Stopped; 207 Algorithm.ProblemChanged += Algorithm_ProblemChanged; 208 } 209 private void DeregisterAlgorithmEvents() { 210 Algorithm.ExceptionOccurred -= Algorithm_ExceptionOccurred; 211 Algorithm.Paused -= Algorithm_Paused; 212 Algorithm.Stopped -= Algorithm_Stopped; 213 Algorithm.ProblemChanged -= Algorithm_ProblemChanged; 214 } 215 #endregion 216 217 private void RegisterEventHandlers() { 218 if (Algorithm != null) RegisterAlgorithmEvents(); 219 if (Problem != null) RegisterProblemEvents(); 220 AlgorithmParameter.ValueChanged += AlgorithmParameterOnValueChanged; 221 } 222 223 private void AlgorithmParameterOnValueChanged(object sender, EventArgs eventArgs) { 224 Algorithm = AlgorithmParameter.Value; 225 } 226 227 #region Prepare, Start, Pause, Stop 228 public override void Prepare() { 229 if (Problem == null || Algorithm == null) return; 230 231 Algorithm.Prepare(true); 232 233 results.Clear(); 234 OnPrepared(); 235 } 236 237 public override void Start() { 238 base.Start(); 239 OnStarted(); 240 var task = Task.Factory.StartNew(Run, null); 241 task.ContinueWith(t => { 242 try { 243 t.Wait(); 244 } catch (AggregateException ex) { 245 try { 246 ex.Flatten().Handle(x => x is OperationCanceledException); 247 } catch (AggregateException remaining) { 248 if (remaining.InnerExceptions.Count == 1) OnExceptionOccurred(remaining.InnerExceptions[0]); 249 else OnExceptionOccurred(remaining); 250 } 251 } 252 if (Algorithm.ExecutionState == ExecutionState.Paused) OnPaused(); 253 else OnStopped(); 254 }); 255 } 256 257 public override void Pause() { 258 base.Pause(); 259 Algorithm.Pause(); 260 } 261 262 public override void Stop() { 263 base.Stop(); 264 Algorithm.Stop(); 265 } 266 #endregion 267 268 private DateTime lastUpdateTime; 269 private void Run(object state) { 270 lastUpdateTime = DateTime.UtcNow; 271 System.Timers.Timer timer = new System.Timers.Timer(250); 272 timer.AutoReset = true; 273 timer.Elapsed += timer_Elapsed; 274 timer.Start(); 275 try { 276 Run(); 277 } finally { 278 timer.Elapsed -= timer_Elapsed; 279 timer.Stop(); 280 ExecutionTime += DateTime.UtcNow - lastUpdateTime; 281 } 282 } 283 284 private readonly AutoResetEvent algorithmWaitHandle = new AutoResetEvent(false); 285 private void Run() { 286 if (!Results.ContainsKey(ExecutionTimeResultName)) Results.Add(new Result(ExecutionTimeResultName, new TimeSpanValue(TimeSpan.Zero))); 287 if (!Results.ContainsKey(EvaluatedSolutionsResultName)) Results.Add(new Result(EvaluatedSolutionsResultName, new DoubleValue(0))); 288 if (!Results.ContainsKey(EvaluatedMovesResultName)) Results.Add(new Result(EvaluatedMovesResultName, new DoubleValue(0))); 289 if (!Results.ContainsKey(EvaluationsResultName)) Results.Add(new Result(EvaluationsResultName, new DoubleValue(0))); 290 if (!Results.ContainsKey(BestQualityResultName)) Results.Add(new Result(BestQualityResultName, new DoubleValue(double.NaN))); 291 292 do { 293 if (!Results.ContainsKey(RandomRestartsResultName)) Results.Add(new Result(RandomRestartsResultName, new IntValue(0))); 294 else if (Algorithm.ExecutionState == ExecutionState.Prepared) ((IntValue)Results[RandomRestartsResultName].Value).Value++; 295 296 Algorithm.Start(); 297 algorithmWaitHandle.WaitOne(); 298 299 if (Algorithm.ExecutionState == ExecutionState.Paused) return; 300 301 var execTime = ((TimeSpanValue)Results[ExecutionTimeResultName].Value); 302 var solEvals = ((DoubleValue)Results[EvaluatedSolutionsResultName].Value); 303 var movEvals = ((DoubleValue)Results[EvaluatedMovesResultName].Value); 304 var restarts = ((IntValue)Results[RandomRestartsResultName].Value); 305 var evaluations = ((DoubleValue)Results[EvaluationsResultName].Value); 306 var bestQuality = ((DoubleValue)Results[BestQualityResultName].Value); 307 var improvement = false; 308 309 IResult result; 310 if (Algorithm.Results.TryGetValue(perEvaluationsAnalyzer.EvaluatedSolutionsParameter.ActualName, out result)) { 311 var evals = ((IntValue)result.Value).Value; 312 evaluations.Value += evals; 313 solEvals.Value += evals; 314 } 315 if (Algorithm.Results.TryGetValue(perEvaluationsAnalyzer.EvaluatedMovesParameter.ActualName, out result)) { 316 var evals = ((IntValue)result.Value).Value; 317 evaluations.Value += MoveCostPerSolution * evals; 318 movEvals.Value += evals; 319 } 320 if (Algorithm.Results.TryGetValue(perEvaluationsAnalyzer.BestQualityParameter.ActualName, out result)) { 321 var newBestQuality = ((DoubleValue)result.Value).Value; 322 if (double.IsNaN(bestQuality.Value) 323 || Maximization && newBestQuality > bestQuality.Value 324 || !Maximization && newBestQuality < bestQuality.Value) { 325 bestQuality.Value = newBestQuality; 326 improvement = true; 327 } 328 } 329 if (Algorithm.Results.TryGetValue(perClockAnalyzer.QualityPerClockParameter.ResultName, out result)) { 330 UpdateQualityPerClockResult((IndexedDataTable<double>)result.Value, restarts.Value); 331 } 332 if (Algorithm.Results.TryGetValue(perEvaluationsAnalyzer.QualityPerEvaluationsParameter.ResultName, out result)) { 333 UpdateQualityPerEvaluationsResult((IndexedDataTable<double>)result.Value, restarts.Value); 334 } 335 if (StoreSolutionInRun) { 336 foreach (var r in Algorithm.Results) { 337 if (r.Name.ToLower().EndsWith("solution") && improvement) { 338 if (!Results.TryGetValue(r.Name, out result)) 339 Results.Add(new Result(r.Name, (IItem)r.Value.Clone())); 340 else result.Value = (IItem)r.Value.Clone(); 341 } 342 } 343 } 344 345 execTime.Value = ExecutionTime; 346 413 347 Algorithm.Prepare(true); 414 Algorithm.Start(); 415 ExecutionState = ExecutionState.Started; 416 OnStarted(); 417 } 418 public void Pause() { 419 if (ExecutionState != ExecutionState.Started) 420 throw new InvalidOperationException(string.Format("Pause not allowed in execution state \"{0}\".", ExecutionState)); 421 Algorithm.Pause(); 422 ExecutionState = ExecutionState.Paused; 423 OnPaused(); 424 } 425 426 private bool forceStop = false; 427 public void Stop() { 428 if ((ExecutionState != ExecutionState.Started) && (ExecutionState != ExecutionState.Paused)) 429 throw new InvalidOperationException(string.Format("Stop not allowed in execution state \"{0}\".", ExecutionState)); 430 forceStop = true; 431 try { 432 Algorithm.Stop(); 433 } catch (InvalidOperationException) { 434 // sometimes we hit the algorithm in an invalid state 435 } 436 } 437 438 private void AddAlgorithmAnalyzers() { 439 if (!Algorithm.Parameters.ContainsKey("Analyzer")) return; 440 var analyzerParam = Algorithm.Parameters["Analyzer"] as IValueParameter<MultiAnalyzer>; 441 if (analyzerParam == null) return; 442 if (!analyzerParam.Value.Operators.Contains(perClockAnalyzer)) 443 analyzerParam.Value.Operators.Add(perClockAnalyzer); 444 if (!analyzerParam.Value.Operators.Contains(perEvaluationsAnalyzer)) 445 analyzerParam.Value.Operators.Add(perEvaluationsAnalyzer); 446 } 447 448 private void RemoveAlgorithmAnalyzers() { 449 if (!Algorithm.Parameters.ContainsKey("Analyzer")) return; 450 var analyzerParam = Algorithm.Parameters["Analyzer"] as IValueParameter<MultiAnalyzer>; 451 if (analyzerParam == null) return; 452 analyzerParam.Value.Operators.Remove(perClockAnalyzer); 453 analyzerParam.Value.Operators.Remove(perEvaluationsAnalyzer); 454 } 455 456 #region Events 457 protected override void OnNameChanged() { 458 base.OnNameChanged(); 459 runs.OptimizerName = Name; 460 } 461 462 public event PropertyChangedEventHandler PropertyChanged; 463 private void OnPropertyChanged(string property) { 464 var handler = PropertyChanged; 465 if (handler != null) handler(this, new PropertyChangedEventArgs(property)); 466 } 467 468 #region IExecutable Events 469 public event EventHandler ExecutionStateChanged; 470 private void OnExecutionStateChanged() { 471 var handler = ExecutionStateChanged; 472 if (handler != null) handler(this, EventArgs.Empty); 473 } 474 public event EventHandler ExecutionTimeChanged; 475 private void OnExecutionTimeChanged() { 476 var handler = ExecutionTimeChanged; 477 if (handler != null) handler(this, EventArgs.Empty); 478 } 479 public event EventHandler Prepared; 480 private void OnPrepared() { 481 var handler = Prepared; 482 if (handler != null) handler(this, EventArgs.Empty); 483 } 484 public event EventHandler Started; 485 private void OnStarted() { 486 var handler = Started; 487 if (handler != null) handler(this, EventArgs.Empty); 488 } 489 public event EventHandler Paused; 490 private void OnPaused() { 491 var handler = Paused; 492 if (handler != null) handler(this, EventArgs.Empty); 493 } 494 public event EventHandler Stopped; 495 private void OnStopped() { 496 var handler = Stopped; 497 if (handler != null) handler(this, EventArgs.Empty); 498 } 499 public event EventHandler<EventArgs<Exception>> ExceptionOccurred; 500 private void OnExceptionOccurred(Exception exception) { 501 var handler = ExceptionOccurred; 502 if (handler != null) handler(this, new EventArgs<Exception>(exception)); 503 } 504 #endregion 505 506 #region Algorithm Events 507 private void RegisterAlgorithmEvents() { 508 algorithm.ExceptionOccurred += Algorithm_ExceptionOccurred; 509 algorithm.ExecutionTimeChanged += Algorithm_ExecutionTimeChanged; 510 algorithm.Paused += Algorithm_Paused; 511 algorithm.Prepared += Algorithm_Prepared; 512 algorithm.Stopped += Algorithm_Stopped; 513 algorithm.ProblemChanged += Algorithm_ProblemChanged; 514 Algorithm_ProblemChanged(algorithm, EventArgs.Empty); 515 } 516 private void DeregisterAlgorithmEvents() { 517 algorithm.ExceptionOccurred -= Algorithm_ExceptionOccurred; 518 algorithm.ExecutionTimeChanged -= Algorithm_ExecutionTimeChanged; 519 algorithm.Paused -= Algorithm_Paused; 520 algorithm.Prepared -= Algorithm_Prepared; 521 algorithm.Stopped -= Algorithm_Stopped; 522 algorithm.ProblemChanged -= Algorithm_ProblemChanged; 523 } 524 private void Algorithm_ExceptionOccurred(object sender, EventArgs<Exception> e) { 525 OnExceptionOccurred(e.Value); 526 } 527 private void Algorithm_ExecutionTimeChanged(object sender, EventArgs e) { 528 ExecutionTime += Algorithm.ExecutionTime - lastAlgorithmExecutionTime; 529 lastAlgorithmExecutionTime = Algorithm.ExecutionTime; 530 } 531 private void Algorithm_Paused(object sender, EventArgs e) { 532 ExecutionTime += Algorithm.ExecutionTime - lastAlgorithmExecutionTime; 533 lastAlgorithmExecutionTime = Algorithm.ExecutionTime; 534 OnPaused(); 535 } 536 private void Algorithm_Prepared(object sender, EventArgs e) { 537 lastAlgorithmExecutionTime = TimeSpan.Zero; 538 } 539 private void Algorithm_Stopped(object sender, EventArgs e) { 540 ExecutionTime += Algorithm.ExecutionTime - lastAlgorithmExecutionTime; 541 lastAlgorithmExecutionTime = Algorithm.ExecutionTime; 542 543 var execTime = ((TimeSpanValue)CurrentRun.Results[ExecutionTimeResultName]).Value; 544 var solEvals = ((DoubleValue)CurrentRun.Results[EvaluatedSolutionsResultName]).Value; 545 var movEvals = ((DoubleValue)CurrentRun.Results[EvaluatedMovesResultName]).Value; 546 var restarts = ((IntValue)CurrentRun.Results[RandomRestartsResultName]).Value; 547 var improvement = false; 548 549 IResult result; 550 if (Algorithm.Results.TryGetValue(perEvaluationsAnalyzer.EvaluatedSolutionsParameter.ActualName, out result)) { 551 var evals = ((IntValue)result.Value).Value; 552 Evaluations += evals; 553 CurrentRun.Results[EvaluatedSolutionsResultName] = new DoubleValue(solEvals + evals); 554 } 555 if (Algorithm.Results.TryGetValue(perEvaluationsAnalyzer.EvaluatedMovesParameter.ActualName, out result)) { 556 var evals = ((IntValue)result.Value).Value; 557 Evaluations += moveCostPerSolution * evals; 558 CurrentRun.Results[EvaluatedMovesResultName] = new DoubleValue(movEvals + evals); 559 } 560 if (Algorithm.Results.TryGetValue(perEvaluationsAnalyzer.BestQualityParameter.ActualName, out result)) { 561 var bestQuality = ((DoubleValue)result.Value).Value; 562 if (double.IsNaN(BestSoFar) 563 || Maximization && bestQuality > BestSoFar 564 || !Maximization && bestQuality < BestSoFar) { 565 BestSoFar = bestQuality; 566 CurrentRun.Results[BestQualityResultName] = new DoubleValue(bestQuality); 567 improvement = true; 568 } 569 } 570 if (Algorithm.Results.TryGetValue(perClockAnalyzer.QualityPerClockParameter.ResultName, out result)) { 571 UpdateQualityPerClockResult((IndexedDataTable<double>)result.Value, execTime, restarts); 572 } 573 if (Algorithm.Results.TryGetValue(perEvaluationsAnalyzer.QualityPerEvaluationsParameter.ResultName, out result)) { 574 UpdateQualityPerEvaluationsResult((IndexedDataTable<double>)result.Value, solEvals, movEvals, restarts); 575 } 576 if (StoreSolutionInRun) { 577 foreach (var r in Algorithm.Results) { 578 if (r.Name.ToLower().EndsWith("solution") && improvement) { 579 CurrentRun.Results[r.Name] = (IItem)r.Value.Clone(); 580 } 581 } 582 } 583 584 CurrentRun.Results[ExecutionTimeResultName] = new TimeSpanValue(execTime + Algorithm.ExecutionTime); 585 586 // Algorithm sets ExecutionTime to zero before firing Prepared 587 // We will thus see ExecutionTimeChanged before Prepared 588 lastAlgorithmExecutionTime = TimeSpan.Zero; 589 590 if (!forceStop && !IsFinished) { 591 CurrentRun.Results[RandomRestartsResultName] = new IntValue(restarts + 1); 592 Algorithm.Prepare(true); 593 Algorithm.Start(); 594 } else { 595 forceStop = false; 596 Runs.Add(CurrentRun); 597 Algorithm.Prepare(true); 598 ExecutionState = ExecutionState.Stopped; 599 OnStopped(); 600 } 601 } 602 603 private void UpdateQualityPerClockResult(IndexedDataTable<double> perClock, TimeSpan execTime, int restarts) { 348 } while (!IsFinished); 349 } 350 351 private void UpdateQualityPerClockResult(IndexedDataTable<double> perClock, int restarts) { 604 352 IndexedDataTable<double> dt; 605 if (! CurrentRun.Results.ContainsKey(QualityPerClockResultName)) {353 if (!Results.ContainsKey(QualityPerClockResultName)) { 606 354 dt = (IndexedDataTable<double>)perClock.Clone(); 607 355 if (!dt.Rows.ContainsKey("Restarts")) … … 614 362 foreach (var v in dt.Rows.First().Values) 615 363 dt.Rows["Restarts"].Values.Add(Tuple.Create(v.Item1, 0.0)); 616 CurrentRun.Results.Add(QualityPerClockResultName, dt);364 Results.Add(new Result(QualityPerClockResultName, dt)); 617 365 } else { 618 dt = (IndexedDataTable<double>)CurrentRun.Results[QualityPerClockResultName]; 619 var best = dt.Rows.First().Values.Last().Item2; 366 dt = (IndexedDataTable<double>)Results[QualityPerClockResultName].Value; 367 var qualityValues = dt.Rows.First().Values; 368 var restartValues = dt.Rows["Restarts"].Values; 369 var best = qualityValues.Last().Item2; 370 var execTime = qualityValues.Last().Item1; 371 var improvement = false; 620 372 foreach (var tupl in perClock.Rows.First().Values) { 621 373 if (Maximization && tupl.Item2 > best || !Maximization && tupl.Item2 < best) { 622 dt.Rows.First().Values.Add(Tuple.Create(execTime.TotalSeconds + tupl.Item1, tupl.Item2)); 623 dt.Rows["Restarts"].Values.Add(Tuple.Create(execTime.TotalSeconds + tupl.Item1, (double)restarts)); 374 if (!improvement) { 375 // the last entry always holds the same value, but with highest execution time 376 qualityValues.RemoveAt(qualityValues.Count - 1); 377 restartValues.RemoveAt(restartValues.Count - 1); 378 improvement = true; 379 } 380 qualityValues.Add(Tuple.Create(execTime + tupl.Item1, tupl.Item2)); 381 restartValues.Add(Tuple.Create(execTime + tupl.Item1, (double)restarts)); 624 382 best = tupl.Item2; 625 383 } 626 384 } 627 } 628 if (IsFinished) { 629 dt.Rows.First().Values.Add(Tuple.Create(ExecutionTime.TotalSeconds, BestSoFar)); 630 dt.Rows["Restarts"].Values.Add(Tuple.Create(ExecutionTime.TotalSeconds, (double)restarts)); 631 } 632 } 633 634 private void UpdateQualityPerEvaluationsResult(IndexedDataTable<double> perEvaluations, double solEvals, double movEvals, int restarts) { 385 if (improvement) { 386 var totalExecTime = execTime + perClock.Rows.First().Values.Last().Item1; 387 qualityValues.Add(Tuple.Create(totalExecTime, best)); 388 restartValues.Add(Tuple.Create(totalExecTime, (double)restarts)); 389 } 390 } 391 } 392 393 private void UpdateQualityPerEvaluationsResult(IndexedDataTable<double> perEvaluations, int restarts) { 635 394 IndexedDataTable<double> dt; 636 if (! CurrentRun.Results.ContainsKey(QualityPerEvaluationsResultName)) {395 if (!Results.ContainsKey(QualityPerEvaluationsResultName)) { 637 396 dt = (IndexedDataTable<double>)perEvaluations.Clone(); 638 397 if (!dt.Rows.ContainsKey("Restarts")) … … 645 404 foreach (var v in dt.Rows.First().Values) 646 405 dt.Rows["Restarts"].Values.Add(Tuple.Create(v.Item1, 0.0)); 647 CurrentRun.Results.Add(QualityPerEvaluationsResultName, dt);406 Results.Add(new Result(QualityPerEvaluationsResultName, dt)); 648 407 } else { 649 dt = (IndexedDataTable<double>)CurrentRun.Results[QualityPerEvaluationsResultName]; 650 var best = dt.Rows.First().Values.Last().Item2; 408 dt = (IndexedDataTable<double>)Results[QualityPerEvaluationsResultName].Value; 409 var qualityValues = dt.Rows.First().Values; 410 var restartValues = dt.Rows["Restarts"].Values; 411 var best = qualityValues.Last().Item2; 412 var evaluations = qualityValues.Last().Item1; 413 var improvement = false; 651 414 foreach (var tupl in perEvaluations.Rows.First().Values) { 652 415 if (Maximization && tupl.Item2 > best || !Maximization && tupl.Item2 < best) { 653 dt.Rows.First().Values.Add(Tuple.Create(solEvals + moveCostPerSolution * movEvals + tupl.Item1, tupl.Item2)); 654 dt.Rows["Restarts"].Values.Add(Tuple.Create(solEvals + moveCostPerSolution * movEvals + tupl.Item1, (double)restarts)); 416 if (!improvement) { 417 // the last entry always holds the same value, but with highest evaluations 418 qualityValues.RemoveAt(qualityValues.Count - 1); 419 restartValues.RemoveAt(restartValues.Count - 1); 420 improvement = true; 421 } 422 qualityValues.Add(Tuple.Create(evaluations + tupl.Item1, tupl.Item2)); 423 restartValues.Add(Tuple.Create(evaluations + tupl.Item1, (double)restarts)); 655 424 best = tupl.Item2; 656 425 } 657 426 } 658 } 659 if (IsFinished) { 660 dt.Rows.First().Values.Add(Tuple.Create(Evaluations, BestSoFar)); 661 dt.Rows["Restarts"].Values.Add(Tuple.Create(Evaluations, (double)restarts)); 662 } 427 if (improvement) { // add the best quality again as value with highest evaluations 428 var totalEvaluations = evaluations + perEvaluations.Rows.First().Values.Last().Item1; 429 qualityValues.Add(Tuple.Create(totalEvaluations, best)); 430 restartValues.Add(Tuple.Create(totalEvaluations, (double)restarts)); 431 } 432 } 433 } 434 435 private void UpdateTargetValueFromBestKnownQuality() { 436 var bkParam = Problem.BestKnownQualityParameter as IValueParameter<DoubleValue>; 437 if (bkParam != null && bkParam.Value != null) 438 TargetValueParameter.Value = new DoubleValue(bkParam.Value.Value); 439 else if (bkParam != null && bkParam.Value == null) 440 TargetValueParameter.Value = null; 441 } 442 443 private void AddAlgorithmAnalyzers() { 444 if (Algorithm == null) return; 445 if (!Algorithm.Parameters.ContainsKey("Analyzer")) return; 446 var analyzerParam = Algorithm.Parameters["Analyzer"] as IValueParameter<MultiAnalyzer>; 447 if (analyzerParam != null) { 448 foreach (var analyzer in analyzerParam.Value.Operators.OfType<QualityPerClockAnalyzer>().ToList()) 449 analyzerParam.Value.Operators.Remove(analyzer); 450 analyzerParam.Value.Operators.Add(perClockAnalyzer); 451 foreach (var analyzer in analyzerParam.Value.Operators.OfType<QualityPerEvaluationsAnalyzer>().ToList()) 452 analyzerParam.Value.Operators.Remove(analyzer); 453 analyzerParam.Value.Operators.Add(perEvaluationsAnalyzer); 454 } else { 455 var analyzerParam2 = Algorithm.Parameters["Analyzer"] as IValueParameter<IMultiAnalyzer>; 456 if (analyzerParam2 == null) return; 457 foreach (var analyzer in analyzerParam2.Value.Operators.OfType<QualityPerClockAnalyzer>().ToList()) 458 analyzerParam2.Value.Operators.Remove(analyzer); 459 analyzerParam2.Value.Operators.Add(perClockAnalyzer); 460 foreach (var analyzer in analyzerParam2.Value.Operators.OfType<QualityPerEvaluationsAnalyzer>().ToList()) 461 analyzerParam2.Value.Operators.Remove(analyzer); 462 analyzerParam2.Value.Operators.Add(perEvaluationsAnalyzer); 463 } 464 } 465 466 private void RemoveAlgorithmAnalyzers() { 467 if (Algorithm == null) return; 468 if (!Algorithm.Parameters.ContainsKey("Analyzer")) return; 469 var analyzerParam = Algorithm.Parameters["Analyzer"] as IValueParameter<MultiAnalyzer>; 470 if (analyzerParam != null) { 471 analyzerParam.Value.Operators.Remove(perClockAnalyzer); 472 analyzerParam.Value.Operators.Remove(perEvaluationsAnalyzer); 473 } else { 474 var analyzerParam2 = Algorithm.Parameters["Analyzer"] as IValueParameter<IMultiAnalyzer>; 475 if (analyzerParam2 != null) { 476 analyzerParam2.Value.Operators.Remove(perClockAnalyzer); 477 analyzerParam2.Value.Operators.Remove(perEvaluationsAnalyzer); 478 } 479 } 480 } 481 482 #region Event Handlers 483 private void Algorithm_ExceptionOccurred(object sender, EventArgs<Exception> e) { 484 OnExceptionOccurred(e.Value); 485 } 486 private void Algorithm_Paused(object sender, EventArgs e) { 487 algorithmWaitHandle.Set(); 488 } 489 private void Algorithm_Stopped(object sender, EventArgs e) { 490 algorithmWaitHandle.Set(); 663 491 } 664 492 665 493 private void Algorithm_ProblemChanged(object sender, EventArgs e) { 666 if (problem != null) DeregisterProblemEvents(); 667 668 problem = Algorithm.Problem as ISingleObjectiveHeuristicOptimizationProblem; 669 if (problem == null) return; 670 RegisterProblemEvents(); 671 494 if (Algorithm.Problem != Problem) Problem = (ISingleObjectiveHeuristicOptimizationProblem)Algorithm.Problem; 672 495 AddAlgorithmAnalyzers(); 673 674 var maxParam = problem.MaximizationParameter as IValueParameter<BoolValue>; 675 if (maxParam != null) 676 Maximization = maxParam.Value.Value; 677 var bkParam = problem.BestKnownQualityParameter as IValueParameter<DoubleValue>; 678 if (bkParam != null && bkParam.Value != null) 679 TargetValue = bkParam.Value.Value; 680 681 Reset(); 682 } 683 684 private void RegisterProblemEvents() { 685 problem.Reset += ProblemOnReset; 686 problem.OperatorsChanged += ProblemOnOperatorsChanged; 687 } 688 689 private void DeregisterProblemEvents() { 690 problem.Reset -= ProblemOnReset; 691 problem.OperatorsChanged -= ProblemOnOperatorsChanged; 692 } 693 694 private void ProblemOnReset(object sender, EventArgs eventArgs) { 695 AddAlgorithmAnalyzers(); 696 } 697 698 private void ProblemOnOperatorsChanged(object sender, EventArgs eventArgs) { 699 AddAlgorithmAnalyzers(); 700 } 701 #endregion 496 } 497 498 private void Problem_BestKnownQualityParameter_ValueChanged(object sender, EventArgs e) { 499 var param = sender as IValueParameter<DoubleValue>; 500 if (param != null) { 501 if (param.Value != null) param.Value.ValueChanged += Problem_BestKnownQualityParameter_Value_ValueChanged; 502 UpdateTargetValueFromBestKnownQuality(); 503 } 504 } 505 506 private void Problem_BestKnownQualityParameter_Value_ValueChanged(object sender, EventArgs e) { 507 UpdateTargetValueFromBestKnownQuality(); 508 } 509 510 protected override void Problem_Reset(object sender, EventArgs eventArgs) { 511 if (Algorithm != null) AddAlgorithmAnalyzers(); 512 } 513 514 protected override void Problem_OperatorsChanged(object sender, EventArgs eventArgs) { 515 if (Algorithm != null) AddAlgorithmAnalyzers(); 516 } 517 518 private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { 519 System.Timers.Timer timer = (System.Timers.Timer)sender; 520 timer.Enabled = false; 521 DateTime now = DateTime.UtcNow; 522 ExecutionTime += now - lastUpdateTime; 523 lastUpdateTime = now; 524 timer.Enabled = true; 525 } 702 526 #endregion 703 527 }
Note: See TracChangeset
for help on using the changeset viewer.