Index: branches/TSNE/HeuristicLab.Algorithms.DataAnalysis/3.4/TSNE/TSNEAnalysis.cs
===================================================================
--- branches/TSNE/HeuristicLab.Algorithms.DataAnalysis/3.4/TSNE/TSNEAnalysis.cs	(revision 14414)
+++ branches/TSNE/HeuristicLab.Algorithms.DataAnalysis/3.4/TSNE/TSNEAnalysis.cs	(revision 14512)
@@ -20,5 +20,6 @@
 #endregion
 
-using System;
+using System.Collections.Generic;
+using System.Drawing;
 using System.Linq;
 using HeuristicLab.Analysis;
@@ -27,5 +28,4 @@
 using HeuristicLab.Data;
 using HeuristicLab.Encodings.RealVectorEncoding;
-using HeuristicLab.Optimization;
 using HeuristicLab.Parameters;
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
@@ -60,4 +60,5 @@
     private const string SetSeedRandomlyParameterName = "SetSeedRandomly";
     private const string SeedParameterName = "Seed";
+    private const string ClassesParameterName = "ClassNames";
     #endregion
 
@@ -67,7 +68,7 @@
       get { return Parameters[PerplexityParameterName] as IFixedValueParameter<DoubleValue>; }
     }
-    public IFixedValueParameter<DoubleValue> ThetaParameter
-    {
-      get { return Parameters[ThetaParameterName] as IFixedValueParameter<DoubleValue>; }
+    public OptionalValueParameter<DoubleValue> ThetaParameter
+    {
+      get { return Parameters[ThetaParameterName] as OptionalValueParameter<DoubleValue>; }
     }
     public IFixedValueParameter<IntValue> NewDimensionsParameter
@@ -110,4 +111,8 @@
     {
       get { return Parameters[SeedParameterName] as IFixedValueParameter<IntValue>; }
+    }
+    public IFixedValueParameter<StringValue> ClassesParameter
+    {
+      get { return Parameters[ClassesParameterName] as IFixedValueParameter<StringValue>; }
     }
     #endregion
@@ -124,5 +129,5 @@
     public double Theta
     {
-      get { return ThetaParameter.Value.Value; }
+      get { return ThetaParameter.Value == null ? 0 : ThetaParameter.Value.Value; }
     }
     public int NewDimensions
@@ -152,5 +157,8 @@
     public double Eta
     {
-      get { return EtaParameter.Value.Value; }
+      get
+      {
+        return EtaParameter.Value == null ? 0 : EtaParameter.Value.Value;
+      }
     }
     public bool SetSeedRandomly
@@ -162,4 +170,11 @@
       get { return (uint)SeedParameter.Value.Value; }
     }
+    public string Classes
+    {
+      get { return ClassesParameter.Value.Value; }
+    }
+
+    [Storable]
+    public TSNE<RealVector> tsne;
     #endregion
 
@@ -172,6 +187,6 @@
       Problem = new RegressionProblem();
       Parameters.Add(new ValueParameter<IDistance<RealVector>>(DistanceParameterName, "The distance function used to differentiate similar from non-similar points", new EuclidianDistance()));
-      Parameters.Add(new FixedValueParameter<DoubleValue>(PerplexityParameterName, "Perplexity-Parameter of TSNE. Comparable to k in a k-nearest neighbour algorithm", new DoubleValue(25)));
-      Parameters.Add(new FixedValueParameter<DoubleValue>(ThetaParameterName, "Value describing how much appoximated gradients my differ from exact gradients. Set to 0 for exact calculation", new DoubleValue(0.1)));
+      Parameters.Add(new FixedValueParameter<DoubleValue>(PerplexityParameterName, "Perplexity-Parameter of TSNE. Comparable to k in a k-nearest neighbour algorithm. Recommended Value is Floor(number of points /3) or lower", new DoubleValue(25)));
+      Parameters.Add(new OptionalValueParameter<DoubleValue>(ThetaParameterName, "Value describing how much appoximated gradients my differ from exact gradients. Set to 0 for exact calculation and in [0,1] otherwise \n CAUTION: exact calculation of forces requires building a non-sparse N*N matrix where N is the number of data points\n This may exceed memory limitations", new DoubleValue(0.1)));
       Parameters.Add(new FixedValueParameter<IntValue>(NewDimensionsParameterName, "Dimensionality of projected space (usually 2 for easy visual analysis", new IntValue(2)));
       Parameters.Add(new FixedValueParameter<IntValue>(MaxIterationsParameterName, "Maximum number of iterations for gradient descent", new IntValue(1000)));
@@ -183,24 +198,55 @@
       Parameters.Add(new FixedValueParameter<BoolValue>(SetSeedRandomlyParameterName, "If the seed should be random", new BoolValue(true)));
       Parameters.Add(new FixedValueParameter<IntValue>(SeedParameterName, "The seed used if it should not be random", new IntValue(0)));
+      Parameters.Add(new FixedValueParameter<StringValue>(ClassesParameterName, "name of the column specifying the class lables of each data point. \n if the lable column can not be found Training/Test is used as labels", new StringValue("none")));
     }
     #endregion
 
     protected override void Run() {
-      var lowDimData = new DoubleMatrix(GetProjectedData(Problem.ProblemData));
-      Results.Add(new Result(ScatterPlotResultName, "Plot of the projected data", CreateScatterPlot(lowDimData, Problem.ProblemData)));
-      Results.Add(new Result(DataResultName, "Projected Data", lowDimData));
-    }
-
-    private ScatterPlot CreateScatterPlot(DoubleMatrix lowDimData, IDataAnalysisProblemData problemData) {
-      var plot = new ScatterPlot(DataResultName, "");
-      Normalize(lowDimData);
-      plot.Rows.Add(new ScatterPlotDataRow("Training", "Points of the training set", problemData.TrainingIndices.Select(i => new Point2D<double>(lowDimData[i, 0], lowDimData[i, 1]))));
-      plot.Rows.Add(new ScatterPlotDataRow("Test", "Points of the test set", problemData.TestIndices.Select(i => new Point2D<double>(lowDimData[i, 0], lowDimData[i, 1]))));
-      return plot;
-    }
-
-    private double[,] GetProjectedData(IDataAnalysisProblemData problemData) {
+      var data = CalculateProjectedData(Problem.ProblemData);
+      var lowDimData = new DoubleMatrix(data);
+    }
+
+    public override void Stop() {
+      base.Stop();
+      if (tsne != null) tsne.Running = false;
+    }
+
+    private double[,] CalculateProjectedData(IDataAnalysisProblemData problemData) {
+      var DataRowNames = new Dictionary<string, List<int>>();
+      var rows = new Dictionary<string, ScatterPlotDataRow>();
+
+      if (problemData.Dataset.VariableNames.Contains(Classes)) {
+        if ((problemData.Dataset as Dataset).VariableHasType<string>(Classes)) {
+          var classes = problemData.Dataset.GetStringValues(Classes).ToArray();
+          for (int i = 0; i < classes.Length; i++) {
+            if (!DataRowNames.ContainsKey(classes[i])) DataRowNames.Add(classes[i], new List<int>());
+            DataRowNames[classes[i]].Add(i); //always succeeds
+          }
+        } else if ((problemData.Dataset as Dataset).VariableHasType<double>(Classes)) {
+          var classValues = problemData.Dataset.GetDoubleValues(Classes).ToArray();
+          var max = classValues.Max() + 0.1;
+          var min = classValues.Min() - 0.1;
+          var contours = 8;
+          for (int i = 0; i < contours; i++) {
+            var name = GetContourName(i, min, max, contours);
+            DataRowNames.Add(name, new List<int>());
+            rows.Add(name, new ScatterPlotDataRow(name, "", new List<Point2D<double>>()));
+            rows[name].VisualProperties.Color = GetHeatMapColor(i, contours);
+            rows[name].VisualProperties.PointSize = i+3;
+          }
+          for (int i = 0; i < classValues.Length; i++) {
+            DataRowNames[GetContourName(classValues[i], min, max, contours)].Add(i); //always succeeds
+          }
+
+        }
+
+
+      } else {
+        DataRowNames.Add("Training", problemData.TrainingIndices.ToList());
+        DataRowNames.Add("Test", problemData.TestIndices.ToList());
+      }
+
       var random = SetSeedRandomly ? new MersenneTwister() : new MersenneTwister(Seed);
-      var tsne = new TSNE<RealVector>(Distance, random, Results, MaxIterations, StopLyingIteration, MomentumSwitchIteration, InitialMomentum, FinalMomentum, Eta);
+      tsne = new TSNE<RealVector>(Distance, random, Results, MaxIterations, StopLyingIteration, MomentumSwitchIteration, InitialMomentum, FinalMomentum, Eta, DataRowNames, rows);
       var dataset = problemData.Dataset;
       var allowedInputVariables = problemData.AllowedInputVariables.ToArray();
@@ -210,21 +256,19 @@
     }
 
-    private static void Normalize(DoubleMatrix data) {
-      var max = new double[data.Columns];
-      var min = new double[data.Columns];
-      for (var i = 0; i < max.Length; i++) max[i] = min[i] = data[0, i];
-      for (var i = 0; i < data.Rows; i++)
-        for (var j = 0; j < data.Columns; 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.Rows; i++) {
-        for (var j = 0; j < data.Columns; j++) {
-          data[i, j] = (data[i, j] - (max[j] + min[j]) / 2) / (max[j] - min[j]);
-        }
-      }
-
-    }
+    private static Color GetHeatMapColor(int contourNr, int noContours) {
+      var q = (double)contourNr / noContours;  // q in [0,1]
+      var c = q < 0.5 ? Color.FromArgb((int)(q * 2 * 255), 255, 0) : Color.FromArgb(255, (int)((1 - q) * 2 * 255), 0);
+      return c;
+    }
+    private static string GetContourName(double value, double min, double max, int noContours) {
+      var size = (max - min) / noContours;
+      var contourNr = (int)((value - min) / size);
+      return GetContourName(contourNr, min, max, noContours);
+    }
+    private static string GetContourName(int i, double min, double max, int noContours) {
+      var size = (max - min) / noContours;
+      return "[" + (min + i * size) + ";" + (min + (i + 1) * size) + ")";
+    }
+
   }
 }
