Index: trunk/sources/HeuristicLab.Algorithms.DataAnalysis/3.4/TSNE/TSNEAlgorithm.cs
===================================================================
--- trunk/sources/HeuristicLab.Algorithms.DataAnalysis/3.4/TSNE/TSNEAlgorithm.cs	(revision 15551)
+++ trunk/sources/HeuristicLab.Algorithms.DataAnalysis/3.4/TSNE/TSNEAlgorithm.cs	(revision 15556)
@@ -42,5 +42,5 @@
   /// </summary>
   [Item("t-Distributed Stochastic Neighbor Embedding (tSNE)", "t-Distributed Stochastic Neighbor Embedding projects the data in a low " +
-                "dimensional space to allow visual cluster identification. Implemented similar to: https://lvdmaaten.github.io/tsne/#implementations (Barnes-Hut t-SNE). Described in : https://lvdmaaten.github.io/publications/papers/JMLR_2014.pdf")]
+                                                              "dimensional space to allow visual cluster identification. Implemented similar to: https://lvdmaaten.github.io/tsne/#implementations (Barnes-Hut t-SNE). Described in : https://lvdmaaten.github.io/publications/papers/JMLR_2014.pdf")]
   [Creatable(CreatableAttribute.Categories.DataAnalysis, Priority = 100)]
   [StorableClass]
@@ -203,11 +203,7 @@
     #region Storable poperties
     [Storable]
-    private Dictionary<string, List<int>> dataRowNames;
-    [Storable]
-    private Dictionary<string, ScatterPlotDataRow> dataRows;
+    private Dictionary<string, IList<int>> dataRowIndices;
     [Storable]
     private TSNEStatic<double[]>.TSNEState state;
-    [Storable]
-    private int iter;
     #endregion
 
@@ -223,11 +219,8 @@
     }
     private TSNEAlgorithm(TSNEAlgorithm original, Cloner cloner) : base(original, cloner) {
-      if (original.dataRowNames != null)
-        dataRowNames = new Dictionary<string, List<int>>(original.dataRowNames);
-      if (original.dataRows != null)
-        dataRows = original.dataRows.ToDictionary(kvp => kvp.Key, kvp => cloner.Clone(kvp.Value));
+      if (original.dataRowIndices != null)
+        dataRowIndices = new Dictionary<string, IList<int>>(original.dataRowIndices);
       if (original.state != null)
         state = cloner.Clone(original.state);
-      iter = original.iter;
       RegisterParameterEvents();
     }
@@ -259,6 +252,5 @@
       Parameters.Add(new FixedValueParameter<BoolValue>(RandomInitializationParameterName, "Wether data points should be randomly initialized or according to the first 2 dimensions", new BoolValue(true)));
 
-      Parameters[UpdateIntervalParameterName].Hidden = true;
-
+      UpdateIntervalParameter.Hidden = true;
       MomentumSwitchIterationParameter.Hidden = true;
       InitialMomentumParameter.Hidden = true;
@@ -273,6 +265,5 @@
     public override void Prepare() {
       base.Prepare();
-      dataRowNames = null;
-      dataRows = null;
+      dataRowIndices = null;
       state = null;
     }
@@ -301,12 +292,10 @@
           col++;
         }
-
         if (Normalization) data = NormalizeInputData(data);
         state = TSNEStatic<double[]>.CreateState(data, DistanceFunction, random, NewDimensions, Perplexity, Theta, StopLyingIteration, MomentumSwitchIteration, InitialMomentum, FinalMomentum, Eta, RandomInitialization);
         SetUpResults(allindices);
-        iter = 0;
-      }
-      for (; iter < MaxIterations && !cancellationToken.IsCancellationRequested; iter++) {
-        if (iter % UpdateInterval == 0) Analyze(state);
+      }
+      while (state.iter < MaxIterations && !cancellationToken.IsCancellationRequested) {
+        if (state.iter % UpdateInterval == 0) Analyze(state);
         TSNEStatic<double[]>.Iterate(state);
       }
@@ -324,28 +313,30 @@
       base.RegisterProblemEvents();
       if (Problem == null) return;
+      Problem.ProblemDataChanged += OnProblemDataChanged;
+      if (Problem.ProblemData == null) return;
+      Problem.ProblemData.Changed += OnPerplexityChanged;
+      Problem.ProblemData.Changed += OnColumnsChanged;
+      if (Problem.ProblemData.Dataset == null) return;
+      Problem.ProblemData.Dataset.RowsChanged += OnPerplexityChanged;
+      Problem.ProblemData.Dataset.ColumnsChanged += OnColumnsChanged;
+    }
+
+    protected override void DeregisterProblemEvents() {
+      base.DeregisterProblemEvents();
+      if (Problem == null) return;
       Problem.ProblemDataChanged -= OnProblemDataChanged;
-      Problem.ProblemDataChanged += OnProblemDataChanged;
       if (Problem.ProblemData == null) return;
       Problem.ProblemData.Changed -= OnPerplexityChanged;
       Problem.ProblemData.Changed -= OnColumnsChanged;
-      Problem.ProblemData.Changed += OnPerplexityChanged;
-      Problem.ProblemData.Changed += OnColumnsChanged;
       if (Problem.ProblemData.Dataset == null) return;
       Problem.ProblemData.Dataset.RowsChanged -= OnPerplexityChanged;
       Problem.ProblemData.Dataset.ColumnsChanged -= OnColumnsChanged;
-      Problem.ProblemData.Dataset.RowsChanged += OnPerplexityChanged;
-      Problem.ProblemData.Dataset.ColumnsChanged += OnColumnsChanged;
-    }
-
-    protected override void DeregisterProblemEvents() {
-      base.DeregisterProblemEvents();
-      Problem.ProblemDataChanged -= OnProblemDataChanged;
     }
 
     protected override void OnStopped() {
       base.OnStopped();
+      //bwerth: state objects can be very large; avoid state serialization 
       state = null;
-      dataRowNames = null;
-      dataRows = null;
+      dataRowIndices = null;
     }
 
@@ -354,11 +345,7 @@
       OnPerplexityChanged(this, null);
       OnColumnsChanged(this, null);
-      Problem.ProblemData.Changed -= OnPerplexityChanged;
       Problem.ProblemData.Changed += OnPerplexityChanged;
-      Problem.ProblemData.Changed -= OnColumnsChanged;
       Problem.ProblemData.Changed += OnColumnsChanged;
       if (Problem.ProblemData.Dataset == null) return;
-      Problem.ProblemData.Dataset.RowsChanged -= OnPerplexityChanged;
-      Problem.ProblemData.Dataset.ColumnsChanged -= OnColumnsChanged;
       Problem.ProblemData.Dataset.RowsChanged += OnPerplexityChanged;
       Problem.ProblemData.Dataset.ColumnsChanged += OnColumnsChanged;
@@ -374,5 +361,4 @@
 
     private void RegisterParameterEvents() {
-      PerplexityParameter.Value.ValueChanged -= OnPerplexityChanged;
       PerplexityParameter.Value.ValueChanged += OnPerplexityChanged;
     }
@@ -380,7 +366,5 @@
     private void OnPerplexityChanged(object sender, EventArgs e) {
       if (Problem == null || Problem.ProblemData == null || Problem.ProblemData.Dataset == null || !Parameters.ContainsKey(PerplexityParameterName)) return;
-      PerplexityParameter.Value.ValueChanged -= OnPerplexityChanged;
       PerplexityParameter.Value.Value = Math.Max(1, Math.Min((Problem.ProblemData.Dataset.Rows - 1) / 3.0, Perplexity));
-      PerplexityParameter.Value.ValueChanged += OnPerplexityChanged;
     }
     #endregion
@@ -390,6 +374,5 @@
       if (Results == null) return;
       var results = Results;
-      dataRowNames = new Dictionary<string, List<int>>();
-      dataRows = new Dictionary<string, ScatterPlotDataRow>();
+      dataRowIndices = new Dictionary<string, IList<int>>();
       var problemData = Problem.ProblemData;
 
@@ -411,10 +394,11 @@
       }
 
-      //color datapoints acording to classes variable (be it double or string)
+      //color datapoints acording to classes variable (be it double, datetime or string)
       if (!problemData.Dataset.VariableNames.Contains(ClassesName)) {
-        dataRowNames.Add("Training", problemData.TrainingIndices.ToList());
-        dataRowNames.Add("Test", problemData.TestIndices.ToList());
+        dataRowIndices.Add("Training", problemData.TrainingIndices.ToList());
+        dataRowIndices.Add("Test", problemData.TestIndices.ToList());
         return;
       }
+
       var classificationData = problemData as ClassificationProblemData;
       if (classificationData != null && classificationData.TargetVariable.Equals(ClassesName)) {
@@ -422,12 +406,12 @@
         var classes = classificationData.Dataset.GetDoubleValues(classificationData.TargetVariable, allIndices).Select(v => classNames[v]).ToArray();
         for (var i = 0; i < classes.Length; i++) {
-          if (!dataRowNames.ContainsKey(classes[i])) dataRowNames.Add(classes[i], new List<int>());
-          dataRowNames[classes[i]].Add(i);
+          if (!dataRowIndices.ContainsKey(classes[i])) dataRowIndices.Add(classes[i], new List<int>());
+          dataRowIndices[classes[i]].Add(i);
         }
       } else if (((Dataset)problemData.Dataset).VariableHasType<string>(ClassesName)) {
         var classes = problemData.Dataset.GetStringValues(ClassesName, allIndices).ToArray();
         for (var i = 0; i < classes.Length; i++) {
-          if (!dataRowNames.ContainsKey(classes[i])) dataRowNames.Add(classes[i], new List<int>());
-          dataRowNames[classes[i]].Add(i);
+          if (!dataRowIndices.ContainsKey(classes[i])) dataRowIndices.Add(classes[i], new List<int>());
+          dataRowIndices[classes[i]].Add(i);
         }
       } else if (((Dataset)problemData.Dataset).VariableHasType<double>(ClassesName)) {
@@ -442,10 +426,10 @@
           var c = contourorder[i];
           var contourname = contourMap[c];
-          dataRowNames.Add(contourname, new List<int>());
-          dataRows.Add(contourname, new ScatterPlotDataRow(contourname, "", new List<Point2D<double>>()));
-          dataRows[contourname].VisualProperties.Color = GetHeatMapColor(i, contours);
+          dataRowIndices.Add(contourname, new List<int>());
+          var row = new ScatterPlotDataRow(contourname, "", new List<Point2D<double>>()) {VisualProperties = {Color = GetHeatMapColor(i, contours), PointSize = 8}};
+          ((ScatterPlot)results[ScatterPlotResultName].Value).Rows.Add(row);
         }
         var allClusters = clusterModel.GetClusterValues(clusterdata, Enumerable.Range(0, clusterdata.Rows)).ToArray();
-        for (var i = 0; i < clusterdata.Rows; i++) dataRowNames[contourMap[allClusters[i] - 1]].Add(i);
+        for (var i = 0; i < clusterdata.Rows; i++) dataRowIndices[contourMap[allClusters[i] - 1]].Add(i);
       } else if (((Dataset)problemData.Dataset).VariableHasType<DateTime>(ClassesName)) {
         var clusterdata = new Dataset(problemData.Dataset.DateTimeVariables, problemData.Dataset.DateTimeVariables.Select(v => problemData.Dataset.GetDoubleValues(v, allIndices).ToList()));
@@ -459,13 +443,14 @@
           var c = contourorder[i];
           var contourname = contourMap[c];
-          dataRowNames.Add(contourname, new List<int>());
-          dataRows.Add(contourname, new ScatterPlotDataRow(contourname, "", new List<Point2D<double>>()));
-          dataRows[contourname].VisualProperties.Color = GetHeatMapColor(i, contours);
+          dataRowIndices.Add(contourname, new List<int>());
+          var row = new ScatterPlotDataRow(contourname, "", new List<Point2D<double>>()) {VisualProperties = {Color = GetHeatMapColor(i, contours), PointSize = 8}};
+          row.VisualProperties.PointSize = 8;
+          ((ScatterPlot)results[ScatterPlotResultName].Value).Rows.Add(row);
         }
         var allClusters = clusterModel.GetClusterValues(clusterdata, Enumerable.Range(0, clusterdata.Rows)).ToArray();
-        for (var i = 0; i < clusterdata.Rows; i++) dataRowNames[contourMap[allClusters[i] - 1]].Add(i);
+        for (var i = 0; i < clusterdata.Rows; i++) dataRowIndices[contourMap[allClusters[i] - 1]].Add(i);
       } else {
-        dataRowNames.Add("Training", problemData.TrainingIndices.ToList());
-        dataRowNames.Add("Test", problemData.TestIndices.ToList());
+        dataRowIndices.Add("Training", problemData.TrainingIndices.ToList());
+        dataRowIndices.Add("Test", problemData.TestIndices.ToList());
       }
     }
@@ -485,14 +470,14 @@
       results[DataResultName].Value = new DoubleMatrix(ndata);
       var splot = results[ScatterPlotResultName].Value as ScatterPlot;
-      FillScatterPlot(ndata, splot);
-    }
-
-    private void FillScatterPlot(double[,] lowDimData, ScatterPlot plot) {
-      foreach (var rowName in dataRowNames.Keys) {
+      FillScatterPlot(ndata, splot, dataRowIndices);
+    }
+
+    private static void FillScatterPlot(double[,] lowDimData, ScatterPlot plot, Dictionary<string, IList<int>> dataRowIndices) {
+      foreach (var rowName in dataRowIndices.Keys) {
         if (!plot.Rows.ContainsKey(rowName)) {
-          plot.Rows.Add(dataRows.ContainsKey(rowName) ? dataRows[rowName] : new ScatterPlotDataRow(rowName, "", new List<Point2D<double>>()));
+          plot.Rows.Add(new ScatterPlotDataRow(rowName, "", new List<Point2D<double>>()));
           plot.Rows[rowName].VisualProperties.PointSize = 8;
         }
-        plot.Rows[rowName].Points.Replace(dataRowNames[rowName].Select(i => new Point2D<double>(lowDimData[i, 0], lowDimData[i, 1])));
+        plot.Rows[rowName].Points.Replace(dataRowIndices[rowName].Select(i => new Point2D<double>(lowDimData[i, 0], lowDimData[i, 1])));
       }
     }
@@ -504,9 +489,9 @@
       for (var i = 0; i < max.Length; i++) max[i] = min[i] = data[0, i];
       for (var i = 0; i < data.GetLength(0); i++)
-        for (var j = 0; j < data.GetLength(1); j++) {
-          var v = data[i, j];
-          max[j] = Math.Max(max[j], v);
-          min[j] = Math.Min(min[j], v);
-        }
+      for (var j = 0; j < data.GetLength(1); j++) {
+        var v = data[i, j];
+        max[j] = Math.Max(max[j], v);
+        min[j] = Math.Min(min[j], v);
+      }
       for (var i = 0; i < data.GetLength(0); i++) {
         for (var j = 0; j < data.GetLength(1); j++) {
@@ -532,5 +517,6 @@
       for (var i = 0; i < data.Count; i++) {
         nData[i] = new double[n];
-        for (var j = 0; j < n; j++) nData[i][j] = max[j].IsAlmost(0) ? data[i][j] - mean[j] : (data[i][j] - mean[j]) / max[j];
+        for (var j = 0; j < n; j++)
+          nData[i][j] = max[j].IsAlmost(0) ? data[i][j] - mean[j] : (data[i][j] - mean[j]) / max[j];
       }
       return nData;
@@ -542,8 +528,8 @@
 
     private static void CreateClusters(IDataset data, string target, int contours, out IClusteringModel contourCluster, out Dictionary<int, string> contourNames, out double[][] borders) {
-      var cpd = new ClusteringProblemData((Dataset)data, new[] { target });
+      var cpd = new ClusteringProblemData((Dataset)data, new[] {target});
       contourCluster = KMeansClustering.CreateKMeansSolution(cpd, contours, 3).Model;
 
-      borders = Enumerable.Range(0, contours).Select(x => new[] { double.MaxValue, double.MinValue }).ToArray();
+      borders = Enumerable.Range(0, contours).Select(x => new[] {double.MaxValue, double.MinValue}).ToArray();
       var clusters = contourCluster.GetClusterValues(cpd.Dataset, cpd.AllIndices).ToArray();
       var targetvalues = cpd.Dataset.GetDoubleValues(target).ToArray();
