Free cookie consent management tool by TermsFeed Policy Generator

source: branches/crossvalidation-2434/HeuristicLab.Algorithms.DataAnalysis/3.4/CrossValidation.cs @ 15428

Last change on this file since 15428 was 14029, checked in by gkronber, 8 years ago

#2434: merged trunk changes r12934:14026 from trunk to branch

File size: 34.9 KB
RevLine 
[5617]1#region License Information
2/* HeuristicLab
[12012]3 * Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
[5617]4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Drawing;
25using System.Linq;
[8836]26using System.Threading;
[5617]27using HeuristicLab.Collections;
28using HeuristicLab.Common;
29using HeuristicLab.Core;
30using HeuristicLab.Data;
31using HeuristicLab.Optimization;
32using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
33using HeuristicLab.Problems.DataAnalysis;
[5886]34using HeuristicLab.Problems.DataAnalysis.Symbolic;
[5617]35
36namespace HeuristicLab.Algorithms.DataAnalysis {
[14029]37  [Item("Cross Validation (CV)", "Cross-validation wrapper for data analysis algorithms.")]
[12504]38  [Creatable(CreatableAttribute.Categories.DataAnalysis, Priority = 100)]
[5617]39  [StorableClass]
40  public sealed class CrossValidation : ParameterizedNamedItem, IAlgorithm, IStorableContent {
41    public CrossValidation()
42      : base() {
43      name = ItemName;
44      description = ItemDescription;
45
46      executionState = ExecutionState.Stopped;
[8962]47      runs = new RunCollection { OptimizerName = name };
[5617]48      runsCounter = 0;
49
50      algorithm = null;
51      clonedAlgorithms = new ItemCollection<IAlgorithm>();
52      results = new ResultCollection();
53
[12800]54      folds = 2;
[5617]55      numberOfWorkers = new IntValue(1);
56      samplesStart = new IntValue(0);
57      samplesEnd = new IntValue(0);
58      storeAlgorithmInEachRun = false;
59
60      RegisterEvents();
61      if (Algorithm != null) RegisterAlgorithmEvents();
62    }
63
64    public string Filename { get; set; }
65
66    #region persistence and cloning
67    [StorableConstructor]
68    private CrossValidation(bool deserializing)
69      : base(deserializing) {
70    }
71    [StorableHook(HookType.AfterDeserialization)]
72    private void AfterDeserialization() {
73      RegisterEvents();
74      if (Algorithm != null) RegisterAlgorithmEvents();
[12800]75
76      // BackwardsCompatibility3.4
77      #region Backwards compatible code, remove with 3.6
78      if (persistenceCompatibilityFolds != null) folds = persistenceCompatibilityFolds.Value;
79      #endregion
[5617]80    }
81
82    private CrossValidation(CrossValidation original, Cloner cloner)
83      : base(original, cloner) {
84      executionState = original.executionState;
85      storeAlgorithmInEachRun = original.storeAlgorithmInEachRun;
86      runs = cloner.Clone(original.runs);
87      runsCounter = original.runsCounter;
88      algorithm = cloner.Clone(original.algorithm);
89      clonedAlgorithms = cloner.Clone(original.clonedAlgorithms);
90      results = cloner.Clone(original.results);
91
[12800]92      folds = original.folds;
[12821]93      partitionVariable = original.partitionVariable;
[5617]94      numberOfWorkers = cloner.Clone(original.numberOfWorkers);
95      samplesStart = cloner.Clone(original.samplesStart);
96      samplesEnd = cloner.Clone(original.samplesEnd);
97      RegisterEvents();
98      if (Algorithm != null) RegisterAlgorithmEvents();
99    }
100    public override IDeepCloneable Clone(Cloner cloner) {
101      return new CrossValidation(this, cloner);
102    }
103
104    #endregion
105
106    #region properties
107    [Storable]
108    private IAlgorithm algorithm;
109    public IAlgorithm Algorithm {
110      get { return algorithm; }
111      set {
112        if (ExecutionState != ExecutionState.Prepared && ExecutionState != ExecutionState.Stopped)
113          throw new InvalidOperationException("Changing the algorithm is only allowed if the CrossValidation is stopped or prepared.");
114        if (algorithm != value) {
[5659]115          if (value != null && value.Problem != null && !(value.Problem is IDataAnalysisProblem))
[5617]116            throw new ArgumentException("Only algorithms with a DataAnalysisProblem could be used for the cross validation.");
117          if (algorithm != null) DeregisterAlgorithmEvents();
118          algorithm = value;
119          Parameters.Clear();
120
121          if (algorithm != null) {
122            algorithm.StoreAlgorithmInEachRun = false;
123            RegisterAlgorithmEvents();
124            algorithm.Prepare(true);
125            Parameters.AddRange(algorithm.Parameters);
126          }
127          OnAlgorithmChanged();
128          Prepare();
129        }
130      }
131    }
132
133
134    [Storable]
[5659]135    private IDataAnalysisProblem problem;
136    public IDataAnalysisProblem Problem {
[5617]137      get {
138        if (algorithm == null)
139          return null;
[5659]140        return (IDataAnalysisProblem)algorithm.Problem;
[5617]141      }
142      set {
143        if (ExecutionState != ExecutionState.Prepared && ExecutionState != ExecutionState.Stopped)
144          throw new InvalidOperationException("Changing the problem is only allowed if the CrossValidation is stopped or prepared.");
145        if (algorithm == null) throw new ArgumentNullException("Could not set a problem before an algorithm was set.");
146        algorithm.Problem = value;
147        problem = value;
148      }
149    }
150
151    IProblem IAlgorithm.Problem {
152      get { return Problem; }
153      set {
154        if (value != null && !ProblemType.IsInstanceOfType(value))
155          throw new ArgumentException("Only DataAnalysisProblems could be used for the cross validation.");
[5659]156        Problem = (IDataAnalysisProblem)value;
[5617]157      }
158    }
159    public Type ProblemType {
[5659]160      get { return typeof(IDataAnalysisProblem); }
[5617]161    }
162
163    [Storable]
164    private ItemCollection<IAlgorithm> clonedAlgorithms;
165
166    public IEnumerable<IOptimizer> NestedOptimizers {
167      get {
168        if (Algorithm == null) yield break;
169        yield return Algorithm;
170      }
171    }
172
173    [Storable]
174    private ResultCollection results;
175    public ResultCollection Results {
176      get { return results; }
177    }
178
[12800]179    // BackwardsCompatibility3.4
180    #region Backwards compatible code, remove with 3.6
181    [Storable(Name = "folds")]
182    private IntValue persistenceCompatibilityFolds;
183    #endregion
184
185    [Storable(Name = "foldsNew")]
186    private int folds;
187    public int Folds {
[5617]188      get { return folds; }
[12800]189      set {
190        if (value != folds) {
191          folds = value;
192          partitionVariable = NoPartitionVariable; // setting folds updates the partition variable
193          OnFoldsChanged();
194        }
195      }
[5617]196    }
[12800]197
[12814]198    // folds are either specified explicitly or by a variable from the dataset
[12800]199    public const string NoPartitionVariable = "<none>";
[12821]200    [Storable]
[12800]201    private string partitionVariable = NoPartitionVariable;
202    public string PartitionVariable {
203      get { return partitionVariable; }
204      set {
205        if (value != partitionVariable) {
206          partitionVariable = value;
207          UpdateNumberOfFolds();
208        }
209      }
210    }
211
212    [Storable]
[5617]213    private IntValue samplesStart;
214    public IntValue SamplesStart {
215      get { return samplesStart; }
216    }
217    [Storable]
218    private IntValue samplesEnd;
219    public IntValue SamplesEnd {
220      get { return samplesEnd; }
221    }
222    [Storable]
223    private IntValue numberOfWorkers;
224    public IntValue NumberOfWorkers {
225      get { return numberOfWorkers; }
226    }
227
228    [Storable]
229    private bool storeAlgorithmInEachRun;
230    public bool StoreAlgorithmInEachRun {
231      get { return storeAlgorithmInEachRun; }
232      set {
233        if (storeAlgorithmInEachRun != value) {
234          storeAlgorithmInEachRun = value;
235          OnStoreAlgorithmInEachRunChanged();
236        }
237      }
238    }
239
240    [Storable]
241    private int runsCounter;
242    [Storable]
243    private RunCollection runs;
244    public RunCollection Runs {
245      get { return runs; }
246    }
247
248    [Storable]
249    private ExecutionState executionState;
250    public ExecutionState ExecutionState {
251      get { return executionState; }
252      private set {
253        if (executionState != value) {
254          executionState = value;
255          OnExecutionStateChanged();
256          OnItemImageChanged();
257        }
258      }
259    }
[7201]260    public static new Image StaticItemImage {
261      get { return HeuristicLab.Common.Resources.VSImageLibrary.Event; }
262    }
[5617]263    public override Image ItemImage {
264      get {
265        if (ExecutionState == ExecutionState.Prepared) return HeuristicLab.Common.Resources.VSImageLibrary.ExecutablePrepared;
266        else if (ExecutionState == ExecutionState.Started) return HeuristicLab.Common.Resources.VSImageLibrary.ExecutableStarted;
267        else if (ExecutionState == ExecutionState.Paused) return HeuristicLab.Common.Resources.VSImageLibrary.ExecutablePaused;
268        else if (ExecutionState == ExecutionState.Stopped) return HeuristicLab.Common.Resources.VSImageLibrary.ExecutableStopped;
[7201]269        else return base.ItemImage;
[5617]270      }
271    }
272
273    public TimeSpan ExecutionTime {
274      get {
275        if (ExecutionState != ExecutionState.Prepared)
276          return TimeSpan.FromMilliseconds(clonedAlgorithms.Select(x => x.ExecutionTime.TotalMilliseconds).Sum());
277        return TimeSpan.Zero;
278      }
279    }
[12800]280
[5617]281    #endregion
282
[8738]283    protected override void OnNameChanged() {
284      base.OnNameChanged();
[8962]285      Runs.OptimizerName = Name;
[8738]286    }
287
[5617]288    public void Prepare() {
289      if (ExecutionState == ExecutionState.Started)
290        throw new InvalidOperationException(string.Format("Prepare not allowed in execution state \"{0}\".", ExecutionState));
291      results.Clear();
292      clonedAlgorithms.Clear();
293      if (Algorithm != null) {
294        Algorithm.Prepare();
295        if (Algorithm.ExecutionState == ExecutionState.Prepared) OnPrepared();
296      }
297    }
298    public void Prepare(bool clearRuns) {
299      if (clearRuns) runs.Clear();
300      Prepare();
301    }
302
303    public void Start() {
304      if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused))
305        throw new InvalidOperationException(string.Format("Start not allowed in execution state \"{0}\".", ExecutionState));
306
[9493]307      if (Algorithm != null) {
[5617]308        //create cloned algorithms
309        if (clonedAlgorithms.Count == 0) {
[5886]310
[12800]311          for (int i = 0; i < folds; i++) {
[5617]312            IAlgorithm clonedAlgorithm = (IAlgorithm)algorithm.Clone();
313            clonedAlgorithm.Name = algorithm.Name + " Fold " + i;
[5659]314            IDataAnalysisProblem problem = clonedAlgorithm.Problem as IDataAnalysisProblem;
[5886]315            ISymbolicDataAnalysisProblem symbolicProblem = problem as ISymbolicDataAnalysisProblem;
316
[12800]317            int trainingStart, trainingEnd, testStart, testEnd;
318            // assumes that partitions are subset of subsequent rows
319            GetTrainingAndTestPartitions(i, out trainingStart, out trainingEnd, out testStart, out testEnd);
[5886]320
[7107]321            problem.ProblemData.TrainingPartition.Start = SamplesStart.Value;
322            problem.ProblemData.TrainingPartition.End = SamplesEnd.Value;
[5886]323            problem.ProblemData.TestPartition.Start = testStart;
324            problem.ProblemData.TestPartition.End = testEnd;
325            DataAnalysisProblemData problemData = problem.ProblemData as DataAnalysisProblemData;
326            if (problemData != null) {
327              problemData.TrainingPartitionParameter.Hidden = false;
328              problemData.TestPartitionParameter.Hidden = false;
329            }
330
331            if (symbolicProblem != null) {
332              symbolicProblem.FitnessCalculationPartition.Start = SamplesStart.Value;
333              symbolicProblem.FitnessCalculationPartition.End = SamplesEnd.Value;
334            }
[7738]335            clonedAlgorithm.Prepare();
[5617]336            clonedAlgorithms.Add(clonedAlgorithm);
337          }
338        }
339
340        //start prepared or paused cloned algorithms
341        int startedAlgorithms = 0;
342        foreach (IAlgorithm clonedAlgorithm in clonedAlgorithms) {
[12800]343          if (startedAlgorithms < numberOfWorkers.Value) {
[5617]344            if (clonedAlgorithm.ExecutionState == ExecutionState.Prepared ||
345                clonedAlgorithm.ExecutionState == ExecutionState.Paused) {
[8836]346
347              // start and wait until the alg is started
348              using (var signal = new ManualResetEvent(false)) {
349                EventHandler signalSetter = (sender, args) => { signal.Set(); };
350                clonedAlgorithm.Started += signalSetter;
351                clonedAlgorithm.Start();
352                signal.WaitOne();
353                clonedAlgorithm.Started -= signalSetter;
354
355                startedAlgorithms++;
356              }
[5617]357            }
358          }
359        }
360        OnStarted();
361      }
362    }
363
[12800]364    private void GetTrainingAndTestPartitions(int fold, out int trainingStart, out int trainingEnd, out int testStart, out int testEnd) {
365      if (partitionVariable == NoPartitionVariable) {
366        // uniform split
367        int testSamplesCount = (SamplesEnd.Value - SamplesStart.Value) / folds;
368        trainingStart = SamplesStart.Value;
369        trainingEnd = SamplesEnd.Value;
370        testStart = (fold * testSamplesCount) + SamplesStart.Value;
371        testEnd = (fold + 1) == folds ? SamplesEnd.Value : (fold + 1) * testSamplesCount + SamplesStart.Value;
372      } else {
373        // group rowIdx by partition
374        var partition = Problem.ProblemData.Dataset.GetReadOnlyDoubleValues(partitionVariable);
375        var g = Enumerable.Range(SamplesStart.Value, SamplesEnd.Value - SamplesStart.Value).GroupBy(r => partition[r]).OrderBy(r => r.Key).ToList();
376        trainingStart = SamplesStart.Value;
377        trainingEnd = SamplesEnd.Value;
378        testStart = g[fold].Min();
379        testEnd = g[fold].Max() + 1;
380      }
381    }
382
[5617]383    private bool pausePending;
384    public void Pause() {
385      if (ExecutionState != ExecutionState.Started)
386        throw new InvalidOperationException(string.Format("Pause not allowed in execution state \"{0}\".", ExecutionState));
387      if (!pausePending) {
388        pausePending = true;
[9493]389        PauseAllClonedAlgorithms();
[5617]390      }
391    }
392    private void PauseAllClonedAlgorithms() {
393      foreach (IAlgorithm clonedAlgorithm in clonedAlgorithms) {
394        if (clonedAlgorithm.ExecutionState == ExecutionState.Started)
395          clonedAlgorithm.Pause();
396      }
397    }
398
399    private bool stopPending;
400    public void Stop() {
401      if ((ExecutionState != ExecutionState.Started) && (ExecutionState != ExecutionState.Paused))
402        throw new InvalidOperationException(string.Format("Stop not allowed in execution state \"{0}\".",
403                                                          ExecutionState));
404      if (!stopPending) {
405        stopPending = true;
[9493]406        StopAllClonedAlgorithms();
[5617]407      }
408    }
409    private void StopAllClonedAlgorithms() {
410      foreach (IAlgorithm clonedAlgorithm in clonedAlgorithms) {
411        if (clonedAlgorithm.ExecutionState == ExecutionState.Started ||
412            clonedAlgorithm.ExecutionState == ExecutionState.Paused)
413          clonedAlgorithm.Stop();
414      }
415    }
416
417    #region collect parameters and results
418    public override void CollectParameterValues(IDictionary<string, IItem> values) {
419      values.Add("Algorithm Name", new StringValue(Name));
420      values.Add("Algorithm Type", new StringValue(GetType().GetPrettyName()));
[12800]421      values.Add("Folds", new IntValue(folds));
[5617]422
423      if (algorithm != null) {
424        values.Add("CrossValidation Algorithm Name", new StringValue(Algorithm.Name));
425        values.Add("CrossValidation Algorithm Type", new StringValue(Algorithm.GetType().GetPrettyName()));
426        base.CollectParameterValues(values);
427      }
428      if (Problem != null) {
429        values.Add("Problem Name", new StringValue(Problem.Name));
430        values.Add("Problem Type", new StringValue(Problem.GetType().GetPrettyName()));
431        Problem.CollectParameterValues(values);
432      }
433    }
434
435    public void CollectResultValues(IDictionary<string, IItem> results) {
[6566]436      var clonedResults = (ResultCollection)this.results.Clone();
437      foreach (var result in clonedResults) {
438        results.Add(result.Name, result.Value);
439      }
440    }
441
442    private void AggregateResultValues(IDictionary<string, IItem> results) {
[5617]443      IEnumerable<IRun> runs = clonedAlgorithms.Select(alg => alg.Runs.FirstOrDefault()).Where(run => run != null);
444      IEnumerable<KeyValuePair<string, IItem>> resultCollections = runs.Where(x => x != null).SelectMany(x => x.Results).ToList();
445
446      foreach (IResult result in ExtractAndAggregateResults<IntValue>(resultCollections))
447        results.Add(result.Name, result.Value);
448      foreach (IResult result in ExtractAndAggregateResults<DoubleValue>(resultCollections))
449        results.Add(result.Name, result.Value);
450      foreach (IResult result in ExtractAndAggregateResults<PercentValue>(resultCollections))
451        results.Add(result.Name, result.Value);
[6184]452      foreach (IResult result in ExtractAndAggregateRegressionSolutions(resultCollections)) {
453        results.Add(result.Name, result.Value);
454      }
[6239]455      foreach (IResult result in ExtractAndAggregateClassificationSolutions(resultCollections)) {
456        results.Add(result.Name, result.Value);
457      }
[5617]458      results.Add("Execution Time", new TimeSpanValue(this.ExecutionTime));
459      results.Add("CrossValidation Folds", new RunCollection(runs));
460    }
461
[6184]462    private IEnumerable<IResult> ExtractAndAggregateRegressionSolutions(IEnumerable<KeyValuePair<string, IItem>> resultCollections) {
463      Dictionary<string, List<IRegressionSolution>> resultSolutions = new Dictionary<string, List<IRegressionSolution>>();
464      foreach (var result in resultCollections) {
465        var regressionSolution = result.Value as IRegressionSolution;
466        if (regressionSolution != null) {
467          if (resultSolutions.ContainsKey(result.Key)) {
468            resultSolutions[result.Key].Add(regressionSolution);
469          } else {
470            resultSolutions.Add(result.Key, new List<IRegressionSolution>() { regressionSolution });
471          }
472        }
473      }
474      List<IResult> aggregatedResults = new List<IResult>();
475      foreach (KeyValuePair<string, List<IRegressionSolution>> solutions in resultSolutions) {
[6566]476        // clone manually to correctly clone references between cloned root objects
477        Cloner cloner = new Cloner();
478        var problemDataClone = (IRegressionProblemData)cloner.Clone(Problem.ProblemData);
479        // set partitions of problem data clone correctly
[6184]480        problemDataClone.TrainingPartition.Start = SamplesStart.Value; problemDataClone.TrainingPartition.End = SamplesEnd.Value;
481        problemDataClone.TestPartition.Start = SamplesStart.Value; problemDataClone.TestPartition.End = SamplesEnd.Value;
[6566]482        // clone models
[7738]483        var ensembleSolution = new RegressionEnsembleSolution(problemDataClone);
484        ensembleSolution.AddRegressionSolutions(solutions.Value);
[6184]485
[6250]486        aggregatedResults.Add(new Result(solutions.Key + " (ensemble)", ensembleSolution));
[6184]487      }
[6250]488      List<IResult> flattenedResults = new List<IResult>();
489      CollectResultsRecursively("", aggregatedResults, flattenedResults);
490      return flattenedResults;
[6184]491    }
492
[6239]493    private IEnumerable<IResult> ExtractAndAggregateClassificationSolutions(IEnumerable<KeyValuePair<string, IItem>> resultCollections) {
494      Dictionary<string, List<IClassificationSolution>> resultSolutions = new Dictionary<string, List<IClassificationSolution>>();
495      foreach (var result in resultCollections) {
496        var classificationSolution = result.Value as IClassificationSolution;
497        if (classificationSolution != null) {
498          if (resultSolutions.ContainsKey(result.Key)) {
499            resultSolutions[result.Key].Add(classificationSolution);
500          } else {
501            resultSolutions.Add(result.Key, new List<IClassificationSolution>() { classificationSolution });
502          }
503        }
504      }
[6250]505      var aggregatedResults = new List<IResult>();
[6239]506      foreach (KeyValuePair<string, List<IClassificationSolution>> solutions in resultSolutions) {
[6566]507        // clone manually to correctly clone references between cloned root objects
508        Cloner cloner = new Cloner();
509        var problemDataClone = (IClassificationProblemData)cloner.Clone(Problem.ProblemData);
510        // set partitions of problem data clone correctly
[6239]511        problemDataClone.TrainingPartition.Start = SamplesStart.Value; problemDataClone.TrainingPartition.End = SamplesEnd.Value;
512        problemDataClone.TestPartition.Start = SamplesStart.Value; problemDataClone.TestPartition.End = SamplesEnd.Value;
[6566]513        // clone models
[8528]514        var ensembleSolution = new ClassificationEnsembleSolution(problemDataClone);
515        ensembleSolution.AddClassificationSolutions(solutions.Value);
[6239]516
[6250]517        aggregatedResults.Add(new Result(solutions.Key + " (ensemble)", ensembleSolution));
[6239]518      }
[6250]519      List<IResult> flattenedResults = new List<IResult>();
520      CollectResultsRecursively("", aggregatedResults, flattenedResults);
521      return flattenedResults;
[6239]522    }
523
[6250]524    private void CollectResultsRecursively(string path, IEnumerable<IResult> results, IList<IResult> flattenedResults) {
525      foreach (IResult result in results) {
526        flattenedResults.Add(new Result(path + result.Name, result.Value));
527        ResultCollection childCollection = result.Value as ResultCollection;
528        if (childCollection != null) {
529          CollectResultsRecursively(path + result.Name + ".", childCollection, flattenedResults);
530        }
531      }
532    }
533
[5617]534    private static IEnumerable<IResult> ExtractAndAggregateResults<T>(IEnumerable<KeyValuePair<string, IItem>> results)
535  where T : class, IItem, new() {
536      Dictionary<string, List<double>> resultValues = new Dictionary<string, List<double>>();
537      foreach (var resultValue in results.Where(r => r.Value.GetType() == typeof(T))) {
538        if (!resultValues.ContainsKey(resultValue.Key))
539          resultValues[resultValue.Key] = new List<double>();
540        resultValues[resultValue.Key].Add(ConvertToDouble(resultValue.Value));
541      }
542
543      DoubleValue doubleValue;
544      if (typeof(T) == typeof(PercentValue))
545        doubleValue = new PercentValue();
546      else if (typeof(T) == typeof(DoubleValue))
547        doubleValue = new DoubleValue();
548      else if (typeof(T) == typeof(IntValue))
549        doubleValue = new DoubleValue();
550      else
551        throw new NotSupportedException();
552
553      List<IResult> aggregatedResults = new List<IResult>();
554      foreach (KeyValuePair<string, List<double>> resultValue in resultValues) {
555        doubleValue.Value = resultValue.Value.Average();
[6250]556        aggregatedResults.Add(new Result(resultValue.Key + " (average)", (IItem)doubleValue.Clone()));
[5617]557        doubleValue.Value = resultValue.Value.StandardDeviation();
[6250]558        aggregatedResults.Add(new Result(resultValue.Key + " (std.dev.)", (IItem)doubleValue.Clone()));
[5617]559      }
560      return aggregatedResults;
561    }
562
563    private static double ConvertToDouble(IItem item) {
564      if (item is DoubleValue) return ((DoubleValue)item).Value;
565      else if (item is IntValue) return ((IntValue)item).Value;
566      else throw new NotSupportedException("Could not convert any item type to double");
567    }
568    #endregion
569
570    #region events
571    private void RegisterEvents() {
[12800]572      SamplesStart.ValueChanged += (s, e) => UpdateNumberOfFolds();
573      SamplesEnd.ValueChanged += (s, e) => UpdateNumberOfFolds();
574
[5617]575      RegisterClonedAlgorithmsEvents();
576    }
[12800]577
578    private void UpdateNumberOfFolds() {
579      if (ExecutionState == ExecutionState.Paused || executionState == ExecutionState.Started)
580        throw new InvalidOperationException("Can not change number of folds if crossvalidation is paused or started.");
581
582      if (partitionVariable != NoPartitionVariable) {
583        // number of folds is the number of distinct values of the partition variable in the range [SamplesStart..SamplesEnd[
584        var ds = Problem.ProblemData.Dataset;
585        var partitionValues = ds.GetDoubleValues(PartitionVariable, Enumerable.Range(SamplesStart.Value, SamplesEnd.Value - SamplesStart.Value)).Distinct().Count();
586        folds = partitionValues;
587        OnFoldsChanged();
588      }
[5617]589    }
[7107]590
[5617]591
592    #region template algorithms events
593    public event EventHandler AlgorithmChanged;
594    private void OnAlgorithmChanged() {
595      EventHandler handler = AlgorithmChanged;
596      if (handler != null) handler(this, EventArgs.Empty);
597      OnProblemChanged();
598      if (Problem == null) ExecutionState = ExecutionState.Stopped;
599    }
600    private void RegisterAlgorithmEvents() {
601      algorithm.ProblemChanged += new EventHandler(Algorithm_ProblemChanged);
602      algorithm.ExecutionStateChanged += new EventHandler(Algorithm_ExecutionStateChanged);
[8969]603      if (Problem != null) Problem.Reset += new EventHandler(Problem_Reset);
[5617]604    }
605    private void DeregisterAlgorithmEvents() {
606      algorithm.ProblemChanged -= new EventHandler(Algorithm_ProblemChanged);
607      algorithm.ExecutionStateChanged -= new EventHandler(Algorithm_ExecutionStateChanged);
[8969]608      if (Problem != null) Problem.Reset -= new EventHandler(Problem_Reset);
[5617]609    }
610    private void Algorithm_ProblemChanged(object sender, EventArgs e) {
[5659]611      if (algorithm.Problem != null && !(algorithm.Problem is IDataAnalysisProblem)) {
[5617]612        algorithm.Problem = problem;
613        throw new ArgumentException("A cross validation algorithm can only contain DataAnalysisProblems.");
614      }
[8970]615      if (problem != null) problem.Reset -= new EventHandler(Problem_Reset);
[5659]616      problem = (IDataAnalysisProblem)algorithm.Problem;
[9517]617      if (problem != null) problem.Reset += new EventHandler(Problem_Reset);
[5617]618      OnProblemChanged();
619    }
620    public event EventHandler ProblemChanged;
621    private void OnProblemChanged() {
[12800]622      ConfigureProblem();
[5617]623      EventHandler handler = ProblemChanged;
624      if (handler != null) handler(this, EventArgs.Empty);
[8969]625    }
[5617]626
[8969]627    private void Problem_Reset(object sender, EventArgs e) {
628      ConfigureProblem();
629    }
630
[8970]631    private void ConfigureProblem() {
[12800]632      folds = 2;
633      partitionVariable = NoPartitionVariable;
[8970]634      SamplesStart.Value = 0;
[5806]635      if (Problem != null) {
[5617]636        SamplesEnd.Value = Problem.ProblemData.Dataset.Rows;
[5886]637
638        DataAnalysisProblemData problemData = Problem.ProblemData as DataAnalysisProblemData;
639        if (problemData != null) {
640          problemData.TrainingPartitionParameter.Hidden = true;
641          problemData.TestPartitionParameter.Hidden = true;
642        }
643        ISymbolicDataAnalysisProblem symbolicProblem = Problem as ISymbolicDataAnalysisProblem;
644        if (symbolicProblem != null) {
645          symbolicProblem.FitnessCalculationPartitionParameter.Hidden = true;
646          symbolicProblem.FitnessCalculationPartition.Start = SamplesStart.Value;
647          symbolicProblem.FitnessCalculationPartition.End = SamplesEnd.Value;
648          symbolicProblem.ValidationPartitionParameter.Hidden = true;
649          symbolicProblem.ValidationPartition.Start = 0;
650          symbolicProblem.ValidationPartition.End = 0;
651        }
[5806]652      } else
[5617]653        SamplesEnd.Value = 0;
654    }
655
656    private void Algorithm_ExecutionStateChanged(object sender, EventArgs e) {
657      switch (Algorithm.ExecutionState) {
658        case ExecutionState.Prepared: OnPrepared();
659          break;
660        case ExecutionState.Started: throw new InvalidOperationException("Algorithm template can not be started.");
661        case ExecutionState.Paused: throw new InvalidOperationException("Algorithm template can not be paused.");
662        case ExecutionState.Stopped: OnStopped();
663          break;
664      }
665    }
666    #endregion
667
668    #region clonedAlgorithms events
669    private void RegisterClonedAlgorithmsEvents() {
670      clonedAlgorithms.ItemsAdded += new CollectionItemsChangedEventHandler<IAlgorithm>(ClonedAlgorithms_ItemsAdded);
671      clonedAlgorithms.ItemsRemoved += new CollectionItemsChangedEventHandler<IAlgorithm>(ClonedAlgorithms_ItemsRemoved);
672      clonedAlgorithms.CollectionReset += new CollectionItemsChangedEventHandler<IAlgorithm>(ClonedAlgorithms_CollectionReset);
673      foreach (IAlgorithm algorithm in clonedAlgorithms)
674        RegisterClonedAlgorithmEvents(algorithm);
675    }
676    private void DeregisterClonedAlgorithmsEvents() {
677      clonedAlgorithms.ItemsAdded -= new CollectionItemsChangedEventHandler<IAlgorithm>(ClonedAlgorithms_ItemsAdded);
678      clonedAlgorithms.ItemsRemoved -= new CollectionItemsChangedEventHandler<IAlgorithm>(ClonedAlgorithms_ItemsRemoved);
679      clonedAlgorithms.CollectionReset -= new CollectionItemsChangedEventHandler<IAlgorithm>(ClonedAlgorithms_CollectionReset);
680      foreach (IAlgorithm algorithm in clonedAlgorithms)
681        DeregisterClonedAlgorithmEvents(algorithm);
682    }
683    private void ClonedAlgorithms_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IAlgorithm> e) {
684      foreach (IAlgorithm algorithm in e.Items)
685        RegisterClonedAlgorithmEvents(algorithm);
686    }
687    private void ClonedAlgorithms_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IAlgorithm> e) {
688      foreach (IAlgorithm algorithm in e.Items)
689        DeregisterClonedAlgorithmEvents(algorithm);
690    }
691    private void ClonedAlgorithms_CollectionReset(object sender, CollectionItemsChangedEventArgs<IAlgorithm> e) {
692      foreach (IAlgorithm algorithm in e.OldItems)
693        DeregisterClonedAlgorithmEvents(algorithm);
694      foreach (IAlgorithm algorithm in e.Items)
695        RegisterClonedAlgorithmEvents(algorithm);
696    }
697    private void RegisterClonedAlgorithmEvents(IAlgorithm algorithm) {
698      algorithm.ExceptionOccurred += new EventHandler<EventArgs<Exception>>(ClonedAlgorithm_ExceptionOccurred);
699      algorithm.ExecutionTimeChanged += new EventHandler(ClonedAlgorithm_ExecutionTimeChanged);
700      algorithm.Started += new EventHandler(ClonedAlgorithm_Started);
701      algorithm.Paused += new EventHandler(ClonedAlgorithm_Paused);
702      algorithm.Stopped += new EventHandler(ClonedAlgorithm_Stopped);
703    }
704    private void DeregisterClonedAlgorithmEvents(IAlgorithm algorithm) {
705      algorithm.ExceptionOccurred -= new EventHandler<EventArgs<Exception>>(ClonedAlgorithm_ExceptionOccurred);
706      algorithm.ExecutionTimeChanged -= new EventHandler(ClonedAlgorithm_ExecutionTimeChanged);
707      algorithm.Started -= new EventHandler(ClonedAlgorithm_Started);
708      algorithm.Paused -= new EventHandler(ClonedAlgorithm_Paused);
709      algorithm.Stopped -= new EventHandler(ClonedAlgorithm_Stopped);
710    }
711    private void ClonedAlgorithm_ExceptionOccurred(object sender, EventArgs<Exception> e) {
712      OnExceptionOccurred(e.Value);
713    }
714    private void ClonedAlgorithm_ExecutionTimeChanged(object sender, EventArgs e) {
715      OnExecutionTimeChanged();
716    }
717
718    private readonly object locker = new object();
[9504]719    private readonly object resultLocker = new object();
[5617]720    private void ClonedAlgorithm_Started(object sender, EventArgs e) {
[9493]721      IAlgorithm algorithm = sender as IAlgorithm;
[9504]722      lock (resultLocker) {
723        if (algorithm != null && !results.ContainsKey(algorithm.Name))
724          results.Add(new Result(algorithm.Name, "Contains results for the specific fold.", algorithm.Results));
725      }
[5617]726    }
727
728    private void ClonedAlgorithm_Paused(object sender, EventArgs e) {
729      lock (locker) {
730        if (pausePending && clonedAlgorithms.All(alg => alg.ExecutionState != ExecutionState.Started))
731          OnPaused();
732      }
733    }
734
735    private void ClonedAlgorithm_Stopped(object sender, EventArgs e) {
736      lock (locker) {
737        if (!stopPending && ExecutionState == ExecutionState.Started) {
[8965]738          IAlgorithm preparedAlgorithm = clonedAlgorithms.FirstOrDefault(alg => alg.ExecutionState == ExecutionState.Prepared ||
739                                                                                alg.ExecutionState == ExecutionState.Paused);
[5617]740          if (preparedAlgorithm != null) preparedAlgorithm.Start();
741        }
742        if (ExecutionState != ExecutionState.Stopped) {
743          if (clonedAlgorithms.All(alg => alg.ExecutionState == ExecutionState.Stopped))
744            OnStopped();
745          else if (stopPending &&
746                   clonedAlgorithms.All(
747                     alg => alg.ExecutionState == ExecutionState.Prepared || alg.ExecutionState == ExecutionState.Stopped))
748            OnStopped();
749        }
750      }
751    }
752    #endregion
753    #endregion
754
755    #region event firing
[12800]756
757    public event EventHandler FoldsChanged;
758    private void OnFoldsChanged() {
759      EventHandler handler = FoldsChanged;
760      if (handler != null) handler(this, EventArgs.Empty);
761    }
[5617]762    public event EventHandler ExecutionStateChanged;
763    private void OnExecutionStateChanged() {
764      EventHandler handler = ExecutionStateChanged;
765      if (handler != null) handler(this, EventArgs.Empty);
766    }
767    public event EventHandler ExecutionTimeChanged;
768    private void OnExecutionTimeChanged() {
769      EventHandler handler = ExecutionTimeChanged;
770      if (handler != null) handler(this, EventArgs.Empty);
771    }
772    public event EventHandler Prepared;
773    private void OnPrepared() {
774      ExecutionState = ExecutionState.Prepared;
775      EventHandler handler = Prepared;
776      if (handler != null) handler(this, EventArgs.Empty);
777      OnExecutionTimeChanged();
778    }
779    public event EventHandler Started;
780    private void OnStarted() {
781      ExecutionState = ExecutionState.Started;
782      EventHandler handler = Started;
783      if (handler != null) handler(this, EventArgs.Empty);
784    }
785    public event EventHandler Paused;
786    private void OnPaused() {
787      pausePending = false;
788      ExecutionState = ExecutionState.Paused;
789      EventHandler handler = Paused;
790      if (handler != null) handler(this, EventArgs.Empty);
791    }
792    public event EventHandler Stopped;
793    private void OnStopped() {
794      stopPending = false;
795      Dictionary<string, IItem> collectedResults = new Dictionary<string, IItem>();
[6566]796      AggregateResultValues(collectedResults);
[5617]797      results.AddRange(collectedResults.Select(x => new Result(x.Key, x.Value)).Cast<IResult>().ToArray());
798      runsCounter++;
799      runs.Add(new Run(string.Format("{0} Run {1}", Name, runsCounter), this));
800      ExecutionState = ExecutionState.Stopped;
801      EventHandler handler = Stopped;
802      if (handler != null) handler(this, EventArgs.Empty);
803    }
804    public event EventHandler<EventArgs<Exception>> ExceptionOccurred;
805    private void OnExceptionOccurred(Exception exception) {
806      EventHandler<EventArgs<Exception>> handler = ExceptionOccurred;
807      if (handler != null) handler(this, new EventArgs<Exception>(exception));
808    }
809    public event EventHandler StoreAlgorithmInEachRunChanged;
810    private void OnStoreAlgorithmInEachRunChanged() {
811      EventHandler handler = StoreAlgorithmInEachRunChanged;
812      if (handler != null) handler(this, EventArgs.Empty);
813    }
814    #endregion
815  }
816}
Note: See TracBrowser for help on using the repository browser.