Index: /trunk/sources/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/ClassificationEnsembleSolution.cs
===================================================================
--- /trunk/sources/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/ClassificationEnsembleSolution.cs	(revision 8152)
+++ /trunk/sources/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Classification/ClassificationEnsembleSolution.cs	(revision 8153)
@@ -37,4 +37,8 @@
   [Creatable("Data Analysis - Ensembles")]
   public sealed class ClassificationEnsembleSolution : ClassificationSolution, IClassificationEnsembleSolution {
+    private readonly Dictionary<int, double> trainingEstimatedValuesCache = new Dictionary<int, double>();
+    private readonly Dictionary<int, double> testEstimatedValuesCache = new Dictionary<int, double>();
+    private readonly Dictionary<int, double> estimatedValuesCache = new Dictionary<int, double>();
+
     public new IClassificationEnsembleModel Model {
       get { return (IClassificationEnsembleModel)base.Model; }
@@ -149,17 +153,13 @@
       get {
         var rows = ProblemData.TrainingIndices;
-        var estimatedValuesEnumerators = (from model in Model.Models
-                                          select new { Model = model, EstimatedValuesEnumerator = model.GetEstimatedClassValues(ProblemData.Dataset, rows).GetEnumerator() })
-                                         .ToList();
-        var rowsEnumerator = rows.GetEnumerator();
-        // aggregate to make sure that MoveNext is called for all enumerators 
-        while (rowsEnumerator.MoveNext() & estimatedValuesEnumerators.Select(en => en.EstimatedValuesEnumerator.MoveNext()).Aggregate(true, (acc, b) => acc & b)) {
-          int currentRow = rowsEnumerator.Current;
-
-          var selectedEnumerators = from pair in estimatedValuesEnumerators
-                                    where RowIsTrainingForModel(currentRow, pair.Model) && !RowIsTestForModel(currentRow, pair.Model)
-                                    select pair.EstimatedValuesEnumerator;
-          yield return AggregateEstimatedClassValues(selectedEnumerators.Select(x => x.Current));
+        var rowsToEvaluate = rows.Except(trainingEstimatedValuesCache.Keys);
+        var rowsEnumerator = rowsToEvaluate.GetEnumerator();
+        var valuesEnumerator = GetEstimatedValues(rowsToEvaluate, (r, m) => RowIsTrainingForModel(r, m) && !RowIsTestForModel(r, m)).GetEnumerator();
+
+        while (rowsEnumerator.MoveNext() & valuesEnumerator.MoveNext()) {
+          trainingEstimatedValuesCache.Add(rowsEnumerator.Current, valuesEnumerator.Current);
         }
+
+        return rows.Select(row => trainingEstimatedValuesCache[row]);
       }
     }
@@ -168,18 +168,30 @@
       get {
         var rows = ProblemData.TestIndices;
-        var estimatedValuesEnumerators = (from model in Model.Models
-                                          select new { Model = model, EstimatedValuesEnumerator = model.GetEstimatedClassValues(ProblemData.Dataset, rows).GetEnumerator() })
-                                         .ToList();
-        var rowsEnumerator = ProblemData.TestIndices.GetEnumerator();
-        // aggregate to make sure that MoveNext is called for all enumerators 
-        while (rowsEnumerator.MoveNext() & estimatedValuesEnumerators.Select(en => en.EstimatedValuesEnumerator.MoveNext()).Aggregate(true, (acc, b) => acc & b)) {
-          int currentRow = rowsEnumerator.Current;
-
-          var selectedEnumerators = from pair in estimatedValuesEnumerators
-                                    where RowIsTestForModel(currentRow, pair.Model)
-                                    select pair.EstimatedValuesEnumerator;
-
-          yield return AggregateEstimatedClassValues(selectedEnumerators.Select(x => x.Current));
+        var rowsToEvaluate = rows.Except(testEstimatedValuesCache.Keys);
+        var rowsEnumerator = rowsToEvaluate.GetEnumerator();
+        var valuesEnumerator = GetEstimatedValues(rowsToEvaluate, RowIsTestForModel).GetEnumerator();
+
+        while (rowsEnumerator.MoveNext() & valuesEnumerator.MoveNext()) {
+          testEstimatedValuesCache.Add(rowsEnumerator.Current, valuesEnumerator.Current);
         }
+
+        return rows.Select(row => testEstimatedValuesCache[row]);
+      }
+    }
+
+    private IEnumerable<double> GetEstimatedValues(IEnumerable<int> rows, Func<int, IClassificationModel, bool> modelSelectionPredicate) {
+      var estimatedValuesEnumerators = (from model in Model.Models
+                                        select new { Model = model, EstimatedValuesEnumerator = model.GetEstimatedClassValues(ProblemData.Dataset, rows).GetEnumerator() })
+                                       .ToList();
+      var rowsEnumerator = rows.GetEnumerator();
+      // aggregate to make sure that MoveNext is called for all enumerators 
+      while (rowsEnumerator.MoveNext() & estimatedValuesEnumerators.Select(en => en.EstimatedValuesEnumerator.MoveNext()).Aggregate(true, (acc, b) => acc & b)) {
+        int currentRow = rowsEnumerator.Current;
+
+        var selectedEnumerators = from pair in estimatedValuesEnumerators
+                                  where modelSelectionPredicate(currentRow, pair.Model)
+                                  select pair.EstimatedValuesEnumerator;
+
+        yield return AggregateEstimatedClassValues(selectedEnumerators.Select(x => x.Current));
       }
     }
@@ -196,6 +208,15 @@
 
     public override IEnumerable<double> GetEstimatedClassValues(IEnumerable<int> rows) {
-      return from xs in GetEstimatedClassValueVectors(ProblemData.Dataset, rows)
-             select AggregateEstimatedClassValues(xs);
+      var rowsToEvaluate = rows.Except(estimatedValuesCache.Keys);
+      var rowsEnumerator = rowsToEvaluate.GetEnumerator();
+      var valuesEnumerator = (from xs in GetEstimatedClassValueVectors(ProblemData.Dataset, rowsToEvaluate)
+                              select AggregateEstimatedClassValues(xs))
+                             .GetEnumerator();
+
+      while (rowsEnumerator.MoveNext() & valuesEnumerator.MoveNext()) {
+        estimatedValuesCache.Add(rowsEnumerator.Current, valuesEnumerator.Current);
+      }
+
+      return rows.Select(row => estimatedValuesCache[row]);
     }
 
@@ -223,4 +244,8 @@
 
     protected override void OnProblemDataChanged() {
+      trainingEstimatedValuesCache.Clear();
+      testEstimatedValuesCache.Clear();
+      estimatedValuesCache.Clear();
+
       IClassificationProblemData problemData = new ClassificationProblemData(ProblemData.Dataset,
                                                                      ProblemData.AllowedInputVariables,
@@ -251,7 +276,15 @@
     public void AddClassificationSolutions(IEnumerable<IClassificationSolution> solutions) {
       classificationSolutions.AddRange(solutions);
+
+      trainingEstimatedValuesCache.Clear();
+      testEstimatedValuesCache.Clear();
+      estimatedValuesCache.Clear();
     }
     public void RemoveClassificationSolutions(IEnumerable<IClassificationSolution> solutions) {
       classificationSolutions.RemoveRange(solutions);
+
+      trainingEstimatedValuesCache.Clear();
+      testEstimatedValuesCache.Clear();
+      estimatedValuesCache.Clear();
     }
 
@@ -275,4 +308,8 @@
       trainingPartitions[solution.Model] = solution.ProblemData.TrainingPartition;
       testPartitions[solution.Model] = solution.ProblemData.TestPartition;
+
+      trainingEstimatedValuesCache.Clear();
+      testEstimatedValuesCache.Clear();
+      estimatedValuesCache.Clear();
     }
 
@@ -282,4 +319,8 @@
       trainingPartitions.Remove(solution.Model);
       testPartitions.Remove(solution.Model);
+
+      trainingEstimatedValuesCache.Clear();
+      testEstimatedValuesCache.Clear();
+      estimatedValuesCache.Clear();
     }
   }
