Index: branches/HiveStatistics/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/MenuItems/CreateEnsembleMenuItem.cs
===================================================================
--- branches/HiveStatistics/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/MenuItems/CreateEnsembleMenuItem.cs	(revision 11203)
+++ branches/HiveStatistics/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/MenuItems/CreateEnsembleMenuItem.cs	(revision 11205)
@@ -1,5 +1,5 @@
 ﻿#region License Information
 /* HeuristicLab
- * Copyright (C) 2002-2012 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ * Copyright (C) 2002-2014 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
  *
  * This file is part of HeuristicLab.
@@ -23,6 +23,5 @@
 using System.Collections.Generic;
 using System.Linq;
-using System.Windows.Forms;
-using HeuristicLab.Core;
+using HeuristicLab.Common;
 using HeuristicLab.MainForm;
 using HeuristicLab.MainForm.WindowsForms;
@@ -31,5 +30,5 @@
 using HeuristicLab.Optimizer;
 
-namespace HeuristicLab.Problems.DataAnalysis.MenuItems {
+namespace HeuristicLab.Problems.DataAnalysis.Views {
   internal class CreateEnsembleMenuItem : HeuristicLab.MainForm.WindowsForms.MenuItem, IOptimizerUserInterfaceItemProvider {
     public override string Name {
@@ -37,8 +36,8 @@
     }
     public override IEnumerable<string> Structure {
-      get { return new string[] { "&Edit" }; }
+      get { return new string[] { "&Edit", "&Data Analysis" }; }
     }
     public override int Position {
-      get { return 2500; }
+      get { return 5100; }
     }
     public override string ToolTipText {
@@ -68,9 +67,7 @@
         if (group.All(s => s is IRegressionSolution)) {
           // show all regression ensembles
-          // clone problemdata (N.B. this assumes all solutions are based on the same problem data!)
-          var problemData = (RegressionProblemData)group
-            .OfType<IRegressionSolution>()
-            .First()
-            .ProblemData.Clone();
+          // N.B. this assumes all solutions are based on the same problem data!
+          // the problem data is not cloned because the individual solutions were already cloned
+          var problemData = group.OfType<IRegressionSolution>().First().ProblemData;
           var ensemble = new RegressionEnsembleSolution(problemData);
           ensemble.Name = group.Key + " ensemble";
@@ -81,8 +78,7 @@
         } else if (group.All(s => s is IClassificationSolution)) {
           // show all classification ensembles
-          var problemData = (ClassificationProblemData)group
-            .OfType<IClassificationSolution>()
-            .First()
-            .ProblemData.Clone();
+          // N.B. this assumes all solutions are based on the same problem data!
+          // the problem data is not cloned because the individual solutions were already cloned
+          var problemData = (ClassificationProblemData)group.OfType<IClassificationSolution>().First().ProblemData;
           var ensemble = new ClassificationEnsembleSolution(Enumerable.Empty<IClassificationModel>(), problemData);
           ensemble.Name = group.Key + " ensemble";
@@ -95,6 +91,6 @@
     }
 
-    private IEnumerable<KeyValuePair<string, IItem>> GetDataAnalysisResults(IContentView view) {
-      var empty = Enumerable.Empty<KeyValuePair<string, IItem>>();
+    private IEnumerable<KeyValuePair<string, IDataAnalysisSolution>> GetDataAnalysisResults(IContentView view) {
+      var empty = Enumerable.Empty<KeyValuePair<string, IDataAnalysisSolution>>();
       if (view == null) return empty;
       if (view.Content == null) return empty;
@@ -114,12 +110,15 @@
     }
 
-    private IEnumerable<KeyValuePair<string, IItem>> GetDataAnalysisResults(IEnumerable<IRun> runs) {
+    private IEnumerable<KeyValuePair<string, IDataAnalysisSolution>> GetDataAnalysisResults(IEnumerable<IRun> runs) {
+      var cloner = new Cloner();
       var allResults = from r in runs
+                       where r.Visible
                        select r.Results;
       return from r in allResults
              from result in r
-             let s = result.Value as IDataAnalysisSolution
-             where s != null
-             select result;
+             let solution = result.Value as IDataAnalysisSolution
+             where solution != null
+             let s = (IDataAnalysisSolution)cloner.Clone(result.Value)
+             select new KeyValuePair<string, IDataAnalysisSolution>(result.Key, s);
     }
   }
Index: branches/HiveStatistics/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/MenuItems/ShrinkDataAnalysisRunsMenuItem.cs
===================================================================
--- branches/HiveStatistics/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/MenuItems/ShrinkDataAnalysisRunsMenuItem.cs	(revision 11205)
+++ branches/HiveStatistics/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/MenuItems/ShrinkDataAnalysisRunsMenuItem.cs	(revision 11205)
@@ -0,0 +1,137 @@
+﻿#region License Information
+
+/* HeuristicLab
+ * Copyright (C) 2002-2014 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;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using HeuristicLab.Common;
+using HeuristicLab.MainForm;
+using HeuristicLab.Optimization;
+using HeuristicLab.Optimizer;
+
+using MenuItem = HeuristicLab.MainForm.WindowsForms.MenuItem;
+using ValuesType = System.Collections.Generic.Dictionary<string, System.Collections.IList>;
+
+namespace HeuristicLab.Problems.DataAnalysis.Views {
+  internal sealed class ShrinkDataAnalysisRunsMenuItem : MenuItem, IOptimizerUserInterfaceItemProvider {
+    public override string Name {
+      get { return "Remove Duplicate Datasets"; }
+    }
+    public override IEnumerable<string> Structure {
+      get { return new string[] { "&Edit", "&Data Analysis" }; }
+    }
+    public override int Position {
+      get { return 5300; }
+    }
+    public override string ToolTipText {
+      get { return "This command shrinks the memory usage of data analysis optimizers by checking and unifying duplicate datasets."; }
+    }
+
+    protected override void OnToolStripItemSet(EventArgs e) {
+      ToolStripItem.Enabled = false;
+    }
+
+    protected override void OnActiveViewChanged(object sender, EventArgs e) {
+      IContentView activeView = MainFormManager.MainForm.ActiveView as IContentView;
+      if (activeView == null) {
+        ToolStripItem.Enabled = false;
+        return;
+      }
+
+      var content = activeView.Content;
+      RunCollection runCollection = content as RunCollection;
+      if (runCollection == null && content is IOptimizer)
+        runCollection = ((IOptimizer)content).Runs;
+
+      if (runCollection == null) {
+        ToolStripItem.Enabled = false;
+        return;
+      }
+
+      ToolStripItem.Enabled = runCollection.Any(run => run.Parameters.Any(p => p.Value is IDataAnalysisProblemData));
+    }
+
+    public override void Execute() {
+      IContentView activeView = (IContentView)MainFormManager.MainForm.ActiveView;
+      var mainForm = (MainForm.WindowsForms.MainForm)MainFormManager.MainForm;
+      mainForm.AddOperationProgressToContent(activeView.Content, "Removing duplicate datasets.");
+
+      Action<IContentView> action = (view) => {
+        var variableValuesMapping = new Dictionary<ValuesType, ValuesType>();
+        foreach (var problemData in view.Content.GetObjectGraphObjects(excludeStaticMembers: true).OfType<IDataAnalysisProblemData>()) {
+          var originalValues = variableValuesGetter(problemData.Dataset);
+          var matchingValues = GetEqualValues(originalValues, variableValuesMapping);
+          variableValuesSetter(problemData.Dataset, matchingValues);
+        }
+      };
+
+      action.BeginInvoke(activeView, delegate(IAsyncResult result) {
+        action.EndInvoke(result);
+        mainForm.RemoveOperationProgressFromContent(activeView.Content);
+      }, null);
+    }
+
+    private static ValuesType GetEqualValues(ValuesType originalValues, Dictionary<ValuesType, ValuesType> variableValuesMapping) {
+      if (variableValuesMapping.ContainsKey(originalValues)) return variableValuesMapping[originalValues];
+
+      var matchingValues = variableValuesMapping.FirstOrDefault(kv => kv.Key == kv.Value && EqualVariableValues(originalValues, kv.Key)).Key ?? originalValues;
+      variableValuesMapping[originalValues] = matchingValues;
+      return matchingValues;
+    }
+
+    private static bool EqualVariableValues(ValuesType values1, ValuesType values2) {
+      //compare variable names for equality
+      if (!values1.Keys.SequenceEqual(values2.Keys)) return false;
+      foreach (var key in values1.Keys) {
+        var v1 = values1[key];
+        var v2 = values2[key];
+        if (v1.Count != v2.Count) return false;
+        for (int i = 0; i < v1.Count; i++) {
+          if (!v1[i].Equals(v2[i])) return false;
+        }
+      }
+      return true;
+    }
+
+    private static readonly Action<Dataset, Dictionary<string, IList>> variableValuesSetter;
+    private static readonly Func<Dataset, Dictionary<string, IList>> variableValuesGetter;
+    /// <summary>
+    /// The static initializer is used to create expressions for getting and setting the private variableValues field in the dataset.
+    /// This is done by expressions because the field is private and compiled expression calls are much faster compared to standad reflection calls.
+    /// </summary>
+    static ShrinkDataAnalysisRunsMenuItem() {
+      var dataset = Expression.Parameter(typeof(Dataset));
+      var variableValues = Expression.Parameter(typeof(ValuesType));
+      var valuesExpression = Expression.Field(dataset, "variableValues");
+      var assignExpression = Expression.Assign(valuesExpression, variableValues);
+
+      var variableValuesSetExpression = Expression.Lambda<Action<Dataset, ValuesType>>(assignExpression, dataset, variableValues);
+      variableValuesSetter = variableValuesSetExpression.Compile();
+
+      var variableValuesGetExpression = Expression.Lambda<Func<Dataset, ValuesType>>(valuesExpression, dataset);
+      variableValuesGetter = variableValuesGetExpression.Compile();
+    }
+  }
+}
