Index: branches/GBT/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/LossFunctions/AbsoluteErrorLoss.cs
===================================================================
--- branches/GBT/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/LossFunctions/AbsoluteErrorLoss.cs	(revision 12374)
+++ branches/GBT/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/LossFunctions/AbsoluteErrorLoss.cs	(revision 12374)
@@ -0,0 +1,102 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+
+namespace GradientBoostedTrees {
+  public class AbsoluteErrorLoss : ILossFunction {
+    public double GetLoss(IEnumerable<double> target, IEnumerable<double> pred, IEnumerable<double> weight) {
+      var targetEnum = target.GetEnumerator();
+      var predEnum = pred.GetEnumerator();
+      var weightEnum = weight.GetEnumerator();
+
+      double s = 0;
+      while (targetEnum.MoveNext() & predEnum.MoveNext() & weightEnum.MoveNext()) {
+        double res = targetEnum.Current - predEnum.Current;
+        s += weightEnum.Current * Math.Abs(res);
+      }
+      if (targetEnum.MoveNext() | predEnum.MoveNext() | weightEnum.MoveNext())
+        throw new ArgumentException("target, pred and weight have differing lengths");
+
+      return s;
+    }
+
+    public IEnumerable<double> GetLossGradient(IEnumerable<double> target, IEnumerable<double> pred, IEnumerable<double> weight) {
+      var targetEnum = target.GetEnumerator();
+      var predEnum = pred.GetEnumerator();
+      var weightEnum = weight.GetEnumerator();
+
+      while (targetEnum.MoveNext() & predEnum.MoveNext() & weightEnum.MoveNext()) {
+        var res = targetEnum.Current - predEnum.Current;
+        if (res > 0) yield return weightEnum.Current;
+        else if (res < 0) yield return -weightEnum.Current;
+        else yield return 0.0;
+      }
+      if (targetEnum.MoveNext() | predEnum.MoveNext() | weightEnum.MoveNext())
+        throw new ArgumentException("target, pred and weight have differing lengths");
+    }
+
+    public LineSearchFunc GetLineSearchFunc(IEnumerable<double> target, IEnumerable<double> pred, IEnumerable<double> weight) {
+      var targetArr = target.ToArray();
+      var predArr = pred.ToArray();
+      var weightArr = weight.ToArray();
+      // weights are not supported yet
+      // when weights are supported we need to calculate a weighted median
+      Debug.Assert(weightArr.All(w => w.IsAlmost(1.0)));
+
+      if (targetArr.Length != predArr.Length || predArr.Length != weightArr.Length)
+        throw new ArgumentException("target, pred and weight have differing lengths");
+
+      // line search for abs error
+      LineSearchFunc lineSearch = (idx, startIdx, endIdx) => {
+        int nRows = endIdx - startIdx + 1;
+        var res = from offset in Enumerable.Range(0, nRows)
+                  let i = startIdx + offset
+                  let row = idx[i]
+                  select (targetArr[row] - predArr[row]);
+        return res.Median();
+
+        // old code for weighted median calculation
+        // int nRows = endIdx - startIdx + 1; // startIdx and endIdx are inclusive
+        // if (nRows == 1) return targetArr[idx[startIdx]] - predArr[idx[startIdx]];
+        // else if (nRows == 2) {
+        //   if (weightArr[idx[startIdx]] > weightArr[idx[endIdx]]) {
+        //     return targetArr[idx[startIdx]] - predArr[idx[startIdx]];
+        //   } else if (weightArr[idx[startIdx]] < weightArr[idx[endIdx]]) {
+        //     return targetArr[idx[endIdx]] - predArr[idx[endIdx]];
+        //   } else {
+        //     // same weight
+        //     return ((targetArr[idx[startIdx]] - predArr[idx[startIdx]]) +
+        //        (targetArr[idx[endIdx]] - predArr[idx[endIdx]])) / 2;
+        //   }
+        // } else {
+        //   var ts = from offset in Enumerable.Range(0, nRows)
+        //            let i = startIdx + offset
+        //            select new { res = targetArr[idx[i]] - predArr[idx[i]], weight = weightArr[idx[i]] };
+        //   ts = ts.OrderBy(t => t.res);
+        //   var totalWeight = ts.Sum(t => t.weight);
+        //   var tsEnumerator = ts.GetEnumerator();
+        //   tsEnumerator.MoveNext();
+        // 
+        //   double aggWeight = tsEnumerator.Current.weight; // weight of first
+        // 
+        //   while (aggWeight < totalWeight / 2) {
+        //     tsEnumerator.MoveNext();
+        //     aggWeight += tsEnumerator.Current.weight;
+        //   }
+        //   return tsEnumerator.Current.res;
+        // }
+      };
+      return lineSearch;
+
+    }
+
+    public override string ToString() {
+      return "Absolute error loss";
+    }
+  }
+}
Index: branches/GBT/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/LossFunctions/RelativeErrorLoss.cs
===================================================================
--- branches/GBT/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/LossFunctions/RelativeErrorLoss.cs	(revision 12374)
+++ branches/GBT/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/LossFunctions/RelativeErrorLoss.cs	(revision 12374)
@@ -0,0 +1,93 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+
+namespace GradientBoostedTrees {
+  public class RelativeErrorLoss : ILossFunction {
+    public double GetLoss(IEnumerable<double> target, IEnumerable<double> pred, IEnumerable<double> weight) {
+      var targetEnum = target.GetEnumerator();
+      var predEnum = pred.GetEnumerator();
+      var weightEnum = weight.GetEnumerator();
+
+      double s = 0;
+      while (targetEnum.MoveNext() & predEnum.MoveNext() & weightEnum.MoveNext()) {
+        double res = targetEnum.Current - predEnum.Current;
+        s += weightEnum.Current * Math.Abs(res) * Math.Abs(1.0 / targetEnum.Current);
+      }
+      if (targetEnum.MoveNext() | predEnum.MoveNext() | weightEnum.MoveNext())
+        throw new ArgumentException("target, pred and weight have differing lengths");
+
+      return s;
+    }
+
+    public IEnumerable<double> GetLossGradient(IEnumerable<double> target, IEnumerable<double> pred, IEnumerable<double> weight) {
+      var targetEnum = target.GetEnumerator();
+      var predEnum = pred.GetEnumerator();
+      var weightEnum = weight.GetEnumerator();
+
+      while (targetEnum.MoveNext() & predEnum.MoveNext() & weightEnum.MoveNext()) {
+        var res = targetEnum.Current - predEnum.Current;
+        if (res > 0) yield return weightEnum.Current * 1.0 / Math.Abs(targetEnum.Current);
+        else if (res < 0) yield return -weightEnum.Current * 1.0 / Math.Abs(targetEnum.Current);
+        else yield return 0.0;
+      }
+      if (targetEnum.MoveNext() | predEnum.MoveNext() | weightEnum.MoveNext())
+        throw new ArgumentException("target, pred and weight have differing lengths");
+    }
+
+    public LineSearchFunc GetLineSearchFunc(IEnumerable<double> target, IEnumerable<double> pred, IEnumerable<double> weight) {
+      var targetArr = target.ToArray();
+      var predArr = pred.ToArray();
+      var weightArr = weight.ToArray();
+      Debug.Assert(weightArr.All(w => w.IsAlmost(1.0)));
+
+      if (targetArr.Length != predArr.Length || predArr.Length != weightArr.Length)
+        throw new ArgumentException("target, pred and weight have differing lengths");
+
+      // line search for abs error
+      LineSearchFunc lineSearch = (idx, startIdx, endIdx) => {
+        // weighted median calculation
+        int nRows = endIdx - startIdx + 1; // startIdx and endIdx are inclusive
+        if (nRows == 1) return targetArr[idx[startIdx]] - predArr[idx[startIdx]];
+        else if (nRows == 2) {
+          var w0 = weightArr[idx[startIdx]] * Math.Abs(1.0 / targetArr[idx[startIdx]]);
+          var w1 = weightArr[idx[endIdx]] * Math.Abs(1.0 / targetArr[idx[endIdx]]);
+          if (w0 > w1) {
+            return targetArr[idx[startIdx]] - predArr[idx[startIdx]];
+          } else if (w0 < w1) {
+            return targetArr[idx[endIdx]] - predArr[idx[endIdx]];
+          } else {
+            // same weight
+            return ((targetArr[idx[startIdx]] - predArr[idx[startIdx]]) + (targetArr[idx[endIdx]] - predArr[idx[endIdx]])) / 2;
+          }
+        } else {
+          var ts = from offset in Enumerable.Range(0, nRows)
+                   let i = startIdx + offset
+                   select new { res = targetArr[idx[i]] - predArr[idx[i]], weight = weightArr[idx[i]] * Math.Abs(1.0 / targetArr[idx[i]]) };
+          ts = ts.OrderBy(t => t.res);
+          var totalWeight = ts.Sum(t => t.weight);
+          var tsEnumerator = ts.GetEnumerator();
+          tsEnumerator.MoveNext();
+
+          double aggWeight = tsEnumerator.Current.weight; // weight of first
+
+          while (aggWeight < totalWeight / 2) {
+            tsEnumerator.MoveNext();
+            aggWeight += tsEnumerator.Current.weight;
+          }
+          return tsEnumerator.Current.res;
+        }
+      };
+      return lineSearch;
+    }
+
+    public override string ToString() {
+      return "Relative error loss";
+    }
+  }
+}
Index: branches/GBT/HeuristicLab.Algorithms.DataAnalysis/3.4/HeuristicLab.Algorithms.DataAnalysis-3.4.csproj
===================================================================
--- branches/GBT/HeuristicLab.Algorithms.DataAnalysis/3.4/HeuristicLab.Algorithms.DataAnalysis-3.4.csproj	(revision 12373)
+++ branches/GBT/HeuristicLab.Algorithms.DataAnalysis/3.4/HeuristicLab.Algorithms.DataAnalysis-3.4.csproj	(revision 12374)
@@ -198,4 +198,6 @@
     <Compile Include="GradientBoostedTrees\GradientBoostedTreesAlgorithmStatic.cs" />
     <Compile Include="GradientBoostedTrees\GradientBoostedTreesModel.cs" />
+    <Compile Include="GradientBoostedTrees\LossFunctions\AbsoluteErrorLoss.cs" />
+    <Compile Include="GradientBoostedTrees\LossFunctions\RelativeErrorLoss.cs" />
     <Compile Include="GradientBoostedTrees\LossFunctions\ILossFunction.cs" />
     <Compile Include="GradientBoostedTrees\LossFunctions\SquaredErrorLoss.cs" />
