Index: /branches/TSNE/HeuristicLab.Algorithms.DataAnalysis/3.4/HeuristicLab.Algorithms.DataAnalysis-3.4.csproj
===================================================================
--- /branches/TSNE/HeuristicLab.Algorithms.DataAnalysis/3.4/HeuristicLab.Algorithms.DataAnalysis-3.4.csproj	(revision 14858)
+++ /branches/TSNE/HeuristicLab.Algorithms.DataAnalysis/3.4/HeuristicLab.Algorithms.DataAnalysis-3.4.csproj	(revision 14859)
@@ -54,5 +54,5 @@
     <DebugType>pdbonly</DebugType>
     <Optimize>true</Optimize>
-    <OutputPath>$(SolutionDir)\bin\</OutputPath>
+    <OutputPath>..\..\..\..\trunk\sources\bin\</OutputPath>
     <DefineConstants>TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
@@ -65,5 +65,5 @@
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
     <DebugSymbols>true</DebugSymbols>
-    <OutputPath>$(SolutionDir)\bin\</OutputPath>
+    <OutputPath>..\..\..\..\trunk\sources\bin\</OutputPath>
     <DefineConstants>DEBUG;TRACE</DefineConstants>
     <DebugType>full</DebugType>
@@ -74,5 +74,5 @@
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
-    <OutputPath>$(SolutionDir)\bin\</OutputPath>
+    <OutputPath>..\..\..\..\trunk\sources\bin\</OutputPath>
     <DefineConstants>TRACE</DefineConstants>
     <DocumentationFile>
@@ -87,5 +87,5 @@
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
     <DebugSymbols>true</DebugSymbols>
-    <OutputPath>$(SolutionDir)\bin\</OutputPath>
+    <OutputPath>..\..\..\..\trunk\sources\bin\</OutputPath>
     <DefineConstants>DEBUG;TRACE</DefineConstants>
     <DebugType>full</DebugType>
@@ -96,5 +96,5 @@
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
-    <OutputPath>$(SolutionDir)\bin\</OutputPath>
+    <OutputPath>..\..\..\..\trunk\sources\bin\</OutputPath>
     <DefineConstants>TRACE</DefineConstants>
     <DocumentationFile>
Index: /branches/TSNE/HeuristicLab.Algorithms.DataAnalysis/3.4/TSNE/TSNEAlgorithm.cs
===================================================================
--- /branches/TSNE/HeuristicLab.Algorithms.DataAnalysis/3.4/TSNE/TSNEAlgorithm.cs	(revision 14858)
+++ /branches/TSNE/HeuristicLab.Algorithms.DataAnalysis/3.4/TSNE/TSNEAlgorithm.cs	(revision 14859)
@@ -71,4 +71,5 @@
     private const string ClassesParameterName = "ClassNames";
     private const string NormalizationParameterName = "Normalization";
+    private const string UpdateIntervalParameterName = "UpdateInterval";
     #endregion
 
@@ -124,4 +125,7 @@
       get { return Parameters[NormalizationParameterName] as IFixedValueParameter<BoolValue>; }
     }
+    public IFixedValueParameter<IntValue> UpdateIntervalParameter {
+      get { return Parameters[UpdateIntervalParameterName] as IFixedValueParameter<IntValue>; }
+    }
     #endregion
 
@@ -182,4 +186,9 @@
       set { NormalizationParameter.Value.Value = value; }
     }
+
+    public int UpdateInterval {
+      get { return UpdateIntervalParameter.Value.Value; }
+      set { UpdateIntervalParameter.Value.Value = value; }
+    }
     #endregion
 
@@ -191,5 +200,5 @@
       this.dataRowNames = new Dictionary<string, List<int>>(original.dataRowNames);
       this.dataRows = original.dataRows.ToDictionary(kvp => kvp.Key, kvp => cloner.Clone(kvp.Value));
-      if(original.state != null)
+      if (original.state != null)
         this.state = cloner.Clone(original.state);
       this.iter = original.iter;
@@ -217,4 +226,6 @@
       Parameters.Add(new FixedValueParameter<StringValue>(ClassesParameterName, "name of the column specifying the class lables of each data point. If the label column can not be found training/test is used as labels.", new StringValue("none")));
       Parameters.Add(new FixedValueParameter<BoolValue>(NormalizationParameterName, "Whether the data should be zero centered and have variance of 1 for each variable, so different scalings are ignored.", new BoolValue(true)));
+      Parameters.Add(new FixedValueParameter<IntValue>(UpdateIntervalParameterName, "", new IntValue(50)));
+      Parameters[UpdateIntervalParameterName].Hidden = true;
 
       MomentumSwitchIterationParameter.Hidden = true;
@@ -245,14 +256,14 @@
       var problemData = Problem.ProblemData;
       // set up and initialized everything if necessary
-      if(state == null) {
-        if(SetSeedRandomly) Seed = new System.Random().Next();
+      if (state == null) {
+        if (SetSeedRandomly) Seed = new System.Random().Next();
         var random = new MersenneTwister((uint)Seed);
         var dataset = problemData.Dataset;
         var allowedInputVariables = problemData.AllowedInputVariables.ToArray();
         var data = new double[dataset.Rows][];
-        for(var row = 0; row < dataset.Rows; row++)
+        for (var row = 0; row < dataset.Rows; row++)
           data[row] = allowedInputVariables.Select(col => dataset.GetDoubleValue(col, row)).ToArray();
 
-        if(Normalization) data = NormalizeData(data);
+        if (Normalization) data = NormalizeData(data);
 
         state = TSNEStatic<double[]>.CreateState(data, Distance, random, NewDimensions, Perplexity, Theta,
@@ -262,12 +273,14 @@
         iter = 0;
       }
-      for(; iter < MaxIterations && !cancellationToken.IsCancellationRequested; iter++) {
+      for (; iter < MaxIterations && !cancellationToken.IsCancellationRequested; iter++) {
+        if (iter % UpdateInterval == 0)
+          Analyze(state);
         TSNEStatic<double[]>.Iterate(state);
-        Analyze(state);
-      }
+      }
+      Analyze(state);
     }
 
     private void SetUpResults(IReadOnlyCollection<double[]> data) {
-      if(Results == null) return;
+      if (Results == null) return;
       var results = Results;
       dataRowNames = new Dictionary<string, List<int>>();
@@ -276,17 +289,17 @@
 
       //color datapoints acording to classes variable (be it double or string)
-      if(problemData.Dataset.VariableNames.Contains(Classes)) {
-        if((problemData.Dataset as Dataset).VariableHasType<string>(Classes)) {
+      if (problemData.Dataset.VariableNames.Contains(Classes)) {
+        if ((problemData.Dataset as Dataset).VariableHasType<string>(Classes)) {
           var classes = problemData.Dataset.GetStringValues(Classes).ToArray();
-          for(var i = 0; i < classes.Length; i++) {
-            if(!dataRowNames.ContainsKey(classes[i])) dataRowNames.Add(classes[i], new List<int>());
+          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);
           }
-        } else if((problemData.Dataset as Dataset).VariableHasType<double>(Classes)) {
+        } else if ((problemData.Dataset as Dataset).VariableHasType<double>(Classes)) {
           var classValues = problemData.Dataset.GetDoubleValues(Classes).ToArray();
-          var max = classValues.Max() + 0.1;    
+          var max = classValues.Max() + 0.1;
           var min = classValues.Min() - 0.1;
           const int contours = 8;
-          for(var i = 0; i < contours; i++) {
+          for (var i = 0; i < contours; i++) {
             var contourname = GetContourName(i, min, max, contours);
             dataRowNames.Add(contourname, new List<int>());
@@ -295,5 +308,5 @@
             dataRows[contourname].VisualProperties.PointSize = i + 3;
           }
-          for(var i = 0; i < classValues.Length; i++) {
+          for (var i = 0; i < classValues.Length; i++) {
             dataRowNames[GetContourName(classValues[i], min, max, contours)].Add(i);
           }
@@ -304,18 +317,19 @@
       }
 
-      if(!results.ContainsKey(IterationResultName)) results.Add(new Result(IterationResultName, new IntValue(0)));
+      if (!results.ContainsKey(IterationResultName)) results.Add(new Result(IterationResultName, new IntValue(0)));
       else ((IntValue)results[IterationResultName].Value).Value = 0;
 
-      if(!results.ContainsKey(ErrorResultName)) results.Add(new Result(ErrorResultName, new DoubleValue(0)));
+      if (!results.ContainsKey(ErrorResultName)) results.Add(new Result(ErrorResultName, new DoubleValue(0)));
       else ((DoubleValue)results[ErrorResultName].Value).Value = 0;
 
-      if(!results.ContainsKey(ErrorPlotResultName)) results.Add(new Result(ErrorPlotResultName, new DataTable(ErrorPlotResultName, "Development of errors during gradient descent")));
+      if (!results.ContainsKey(ErrorPlotResultName)) results.Add(new Result(ErrorPlotResultName, new DataTable(ErrorPlotResultName, "Development of errors during gradient descent")));
       else results[ErrorPlotResultName].Value = new DataTable(ErrorPlotResultName, "Development of errors during gradient descent");
 
       var plot = results[ErrorPlotResultName].Value as DataTable;
-      if(plot == null) throw new ArgumentException("could not create/access error data table in results collection");
-
-      if(!plot.Rows.ContainsKey("errors")) plot.Rows.Add(new DataRow("errors"));
+      if (plot == null) throw new ArgumentException("could not create/access error data table in results collection");
+
+      if (!plot.Rows.ContainsKey("errors")) plot.Rows.Add(new DataRow("errors"));
       plot.Rows["errors"].Values.Clear();
+      plot.Rows["errors"].VisualProperties.StartIndexZero = true;
 
       results.Add(new Result(ScatterPlotResultName, "Plot of the projected data", new ScatterPlot(DataResultName, "")));
@@ -324,8 +338,8 @@
 
     private void Analyze(TSNEStatic<double[]>.TSNEState tsneState) {
-      if(Results == null) return;
+      if (Results == null) return;
       var results = Results;
       var plot = results[ErrorPlotResultName].Value as DataTable;
-      if(plot == null) throw new ArgumentException("Could not create/access error data table in results collection.");
+      if (plot == null) throw new ArgumentException("Could not create/access error data table in results collection.");
       var errors = plot.Rows["errors"].Values;
       var c = tsneState.EvaluateError();
@@ -341,6 +355,6 @@
 
     private void FillScatterPlot(double[,] lowDimData, ScatterPlot plot) {
-      foreach(var rowName in dataRowNames.Keys) {
-        if(!plot.Rows.ContainsKey(rowName))
+      foreach (var rowName in dataRowNames.Keys) {
+        if (!plot.Rows.ContainsKey(rowName))
           plot.Rows.Add(dataRows.ContainsKey(rowName) ? dataRows[rowName] : new ScatterPlotDataRow(rowName, "", new List<Point2D<double>>()));
         plot.Rows[rowName].Points.Replace(dataRowNames[rowName].Select(i => new Point2D<double>(lowDimData[i, 0], lowDimData[i, 1])));
@@ -352,13 +366,13 @@
       var min = new double[data.GetLength(1)];
       var res = new double[data.GetLength(0), data.GetLength(1)];
-      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++) {
+      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 i = 0; i < data.GetLength(0); i++) {
-        for(var j = 0; j < data.GetLength(1); j++) {
+      for (var i = 0; i < data.GetLength(0); i++) {
+        for (var j = 0; j < data.GetLength(1); j++) {
           res[i, j] = (data[i, j] - (max[j] + min[j]) / 2) / (max[j] - min[j]);
         }
@@ -368,16 +382,16 @@
 
     private static double[][] NormalizeData(IReadOnlyList<double[]> data) {
+      // as in tSNE implementation by van der Maaten
       var n = data[0].Length;
       var mean = new double[n];
-      var sd = new double[n];
+      var max = new double[n];
       var nData = new double[data.Count][];
-      for(var i = 0; i < n; i++) {
-        var i1 = i;
-        sd[i] = Enumerable.Range(0, data.Count).Select(x => data[x][i1]).StandardDeviation();
-        mean[i] = Enumerable.Range(0, data.Count).Select(x => data[x][i1]).Average();
-      }
-      for(var i = 0; i < data.Count; i++) {
+      for (var i = 0; i < n; i++) {
+        mean[i] = Enumerable.Range(0, data.Count).Select(x => data[x][i]).Average();
+        max[i] = Enumerable.Range(0, data.Count).Max(x => Math.Abs(data[x][i]));
+      }
+      for (var i = 0; i < data.Count; i++) {
         nData[i] = new double[n];
-        for(var j = 0; j < n; j++) nData[i][j] = (data[i][j] - mean[j]) / sd[j];
+        for (var j = 0; j < n; j++) nData[i][j] = (data[i][j] - mean[j]) / max[j];
       }
       return nData;
