Index: branches/GBT-trunkintegration/Tests/Test.cs
===================================================================
--- branches/GBT-trunkintegration/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/Test.cs	(revision 12620)
+++ branches/GBT-trunkintegration/Tests/Test.cs	(revision 12632)
@@ -4,4 +4,7 @@
 using System.Linq;
 using System.Runtime.CompilerServices;
+using System.Threading;
+using HeuristicLab.Data;
+using HeuristicLab.Optimization;
 using HeuristicLab.Problems.DataAnalysis;
 using HeuristicLab.Random;
@@ -25,6 +28,6 @@
         var allVariables = new string[] { "y", "x1", "x2" };
 
-        // x1 <= 15 -> 1 
-        // x1 >  15 -> 2
+        // x1 <= 15 -> 2 
+        // x1 >  15 -> 1
         BuildTree(xy, allVariables, 10);
       }
@@ -42,6 +45,6 @@
 
         // ignore irrelevant variables
-        // x1 <= 15 -> 1 
-        // x1 >  15 -> 2
+        // x1 <= 15 -> 2 
+        // x1 >  15 -> 1
         BuildTree(xy, allVariables, 10);
       }
@@ -61,6 +64,6 @@
         // x1 <= 15 AND x2 <= 0 -> 3 
         // x1 <= 15 AND x2 >  0 -> 4 
-        // x1 >  15 AND x2 <= 0 -> 1
-        // x1 >  15 AND x2 >  0 -> 2
+        // x1 >  15 AND x2 <= 0 -> 2
+        // x1 >  15 AND x2 >  0 -> 1
         BuildTree(xy, allVariables, 10);
       }
@@ -84,6 +87,6 @@
         // x1 <= 15 AND x2 <= 0 -> 3 
         // x1 <= 15 AND x2 >  0 -> 4 
-        // x1 >  15 AND x2 <= 0 -> 1
-        // x1 >  15 AND x2 >  0 -> 2
+        // x1 >  15 AND x2 <= 0 -> 2
+        // x1 >  15 AND x2 >  0 -> 1
         BuildTree(xy, allVariables, 10);
       }
@@ -103,4 +106,5 @@
 
         // split cannot be found
+        // -> 5.50
         BuildTree(xy, allVariables, 3);
       }
@@ -116,9 +120,14 @@
 
         var allVariables = new string[] { "y", "x1", "x2" };
+        // (two possible solutions)
+        // x2 <= 1.5 -> 5.50
+        // x2 >  1.5 -> 5.55
+        BuildTree(xy, allVariables, 3);
+
         // x1 <= 1.5 AND x2 <= 1.5 -> 10 
         // x1 <= 1.5 AND x2 >  1.5 -> 1 
         // x1 >  1.5 AND x2 <= 1.5 -> 1
         // x1 >  1.5 AND x2 >  1.5 -> 10.1
-        BuildTree(xy, allVariables, 3);
+        BuildTree(xy, allVariables, 7);
       }
       {
@@ -136,9 +145,116 @@
         // x1 >  1.5 AND x2 <= 1.5 -> 0.9
         // x1 >  1.5 AND x2 >  1.5 -> 1.1
-        BuildTree(xy, allVariables, 3);
-      }
-
-    }
-
+        BuildTree(xy, allVariables, 10);
+      }
+
+      {
+        // unbalanced split
+        var xy = new double[,]
+        {
+          {-1, 1, 1},
+          {-1, 1, 2},
+          {-1, 2, 1},
+          { 1, 2, 2},
+        };
+
+        var allVariables = new string[] { "y", "x1", "x2" };
+        // (two possible solutions)
+        // x2 <= 1.5 -> -1.0 
+        // x2 >  1.5 AND x1 <= 1.5 -> -1.0
+        // x2 >  1.5 AND x1 >  1.5 ->  1.0
+        BuildTree(xy, allVariables, 10);
+      }
+    }
+
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "long")]
+    public void GradientBoostingTestTowerSquaredError() {
+      var gbt = new GradientBoostedTreesAlgorithm();
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.RegressionRealWorldInstanceProvider();
+      var instance = provider.GetDataDescriptors().Single(x => x.Name.Contains("Tower"));
+      var regProblem = new RegressionProblem();
+      regProblem.Load(provider.LoadData(instance));
+
+      #region Algorithm Configuration
+      gbt.Problem = regProblem;
+      gbt.Seed = 0;
+      gbt.SetSeedRandomly = false;
+      gbt.Iterations = 5000;
+      gbt.MaxSize = 20;
+      #endregion
+
+      RunAlgorithm(gbt);
+
+      Assert.AreEqual(267.68704241153921, ((DoubleValue)gbt.Results["Loss (train)"].Value).Value, 1E-6);
+      Assert.AreEqual(393.84704062205469, ((DoubleValue)gbt.Results["Loss (test)"].Value).Value, 1E-6);
+    }
+
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "long")]
+    public void GradientBoostingTestTowerAbsoluteError() {
+      var gbt = new GradientBoostedTreesAlgorithm();
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.RegressionRealWorldInstanceProvider();
+      var instance = provider.GetDataDescriptors().Single(x => x.Name.Contains("Tower"));
+      var regProblem = new RegressionProblem();
+      regProblem.Load(provider.LoadData(instance));
+
+      #region Algorithm Configuration
+      gbt.Problem = regProblem;
+      gbt.Seed = 0;
+      gbt.SetSeedRandomly = false;
+      gbt.Iterations = 1000;
+      gbt.MaxSize = 20;
+      gbt.Nu = 0.02;
+      gbt.LossFunctionParameter.Value = gbt.LossFunctionParameter.ValidValues.First(l => l.ToString().Contains("Absolute"));
+      #endregion
+
+      RunAlgorithm(gbt);
+
+      Assert.AreEqual(10.551385044666661, ((DoubleValue)gbt.Results["Loss (train)"].Value).Value, 1E-6);
+      Assert.AreEqual(12.918001745581172, ((DoubleValue)gbt.Results["Loss (test)"].Value).Value, 1E-6);
+    }
+
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "long")]
+    public void GradientBoostingTestTowerRelativeError() {
+      var gbt = new GradientBoostedTreesAlgorithm();
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.RegressionRealWorldInstanceProvider();
+      var instance = provider.GetDataDescriptors().Single(x => x.Name.Contains("Tower"));
+      var regProblem = new RegressionProblem();
+      regProblem.Load(provider.LoadData(instance));
+
+      #region Algorithm Configuration
+      gbt.Problem = regProblem;
+      gbt.Seed = 0;
+      gbt.SetSeedRandomly = false;
+      gbt.Iterations = 3000;
+      gbt.MaxSize = 20;
+      gbt.Nu = 0.005;
+      gbt.LossFunctionParameter.Value = gbt.LossFunctionParameter.ValidValues.First(l => l.ToString().Contains("Relative"));
+      #endregion
+
+      RunAlgorithm(gbt);
+
+      Assert.AreEqual(0.061954221604374943, ((DoubleValue)gbt.Results["Loss (train)"].Value).Value, 1E-6);
+      Assert.AreEqual(0.06316303473499961, ((DoubleValue)gbt.Results["Loss (test)"].Value).Value, 1E-6);
+    }
+
+    // same as in SamplesUtil
+    private void RunAlgorithm(IAlgorithm a) {
+      var trigger = new EventWaitHandle(false, EventResetMode.ManualReset);
+      Exception ex = null;
+      a.Stopped += (src, e) => { trigger.Set(); };
+      a.ExceptionOccurred += (src, e) => { ex = e.Value; trigger.Set(); };
+      a.Prepare();
+      a.Start();
+      trigger.WaitOne();
+
+      Assert.AreEqual(ex, null);
+    }
+
+    #region helper
     private void BuildTree(double[,] xy, string[] allVariables, int maxDepth) {
       int nRows = xy.GetLength(0);
@@ -161,21 +277,22 @@
     private void WriteTree(RegressionTreeModel.TreeNode[] tree, int idx, string partialRule, double offset) {
       var n = tree[idx];
-      if (n.varName == RegressionTreeModel.TreeNode.NO_VARIABLE) {
-        Console.WriteLine("{0} -> {1:F}", partialRule, n.val + offset);
+      if (n.VarName == RegressionTreeModel.TreeNode.NO_VARIABLE) {
+        Console.WriteLine("{0} -> {1:F}", partialRule, n.Val + offset);
       } else {
-        WriteTree(tree, n.leftIdx,
+        WriteTree(tree, n.LeftIdx,
           string.Format(CultureInfo.InvariantCulture, "{0}{1}{2} <= {3:F}",
           partialRule,
           string.IsNullOrEmpty(partialRule) ? "" : " and ",
-          n.varName,
-          n.val), offset);
-        WriteTree(tree, n.rightIdx,
+          n.VarName,
+          n.Val), offset);
+        WriteTree(tree, n.RightIdx,
           string.Format(CultureInfo.InvariantCulture, "{0}{1}{2} >  {3:F}",
           partialRule,
           string.IsNullOrEmpty(partialRule) ? "" : " and ",
-          n.varName,
-          n.val), offset);
-      }
-    }
+          n.VarName,
+          n.Val), offset);
+      }
+    }
+    #endregion
   }
 }
