Index: branches/1614_GeneralizedQAP/HeuristicLab.OptimizationExpertSystem/3.3/Views/PerformanceModelingView.Designer.cs
===================================================================
--- branches/1614_GeneralizedQAP/HeuristicLab.OptimizationExpertSystem/3.3/Views/PerformanceModelingView.Designer.cs	(revision 15721)
+++ branches/1614_GeneralizedQAP/HeuristicLab.OptimizationExpertSystem/3.3/Views/PerformanceModelingView.Designer.cs	(revision 15736)
@@ -57,8 +57,10 @@
       this.topNComboBox = new System.Windows.Forms.ComboBox();
       this.topNlabel = new System.Windows.Forms.Label();
+      this.ndcgLabel = new System.Windows.Forms.Label();
       this.kendallsTauLabel = new System.Windows.Forms.Label();
       this.predictedLabel = new System.Windows.Forms.Label();
       this.absoluteLogErrorLabel = new System.Windows.Forms.Label();
       this.absoluteErrorLabel = new System.Windows.Forms.Label();
+      this.ncdgView = new HeuristicLab.Data.Views.StringConvertibleValueView();
       this.kendallsTauView = new HeuristicLab.Data.Views.StringConvertibleValueView();
       this.absoluteLogErrorView = new HeuristicLab.Data.Views.StringConvertibleValueView();
@@ -68,6 +70,6 @@
       this.minTargetView = new HeuristicLab.Data.Views.StringConvertibleValueView();
       this.minimumTargetLabel = new System.Windows.Forms.Label();
-      this.ncdgView = new HeuristicLab.Data.Views.StringConvertibleValueView();
-      this.ndcgLabel = new System.Windows.Forms.Label();
+      this.spearmansRhoView = new HeuristicLab.Data.Views.StringConvertibleValueView();
+      this.spearmansRhoLabel = new System.Windows.Forms.Label();
       this.tabControl.SuspendLayout();
       this.characteristicsTabPage.SuspendLayout();
@@ -186,4 +188,5 @@
       this.crossvalidationTabPage.Controls.Add(this.topNlabel);
       this.crossvalidationTabPage.Controls.Add(this.ndcgLabel);
+      this.crossvalidationTabPage.Controls.Add(this.spearmansRhoLabel);
       this.crossvalidationTabPage.Controls.Add(this.kendallsTauLabel);
       this.crossvalidationTabPage.Controls.Add(this.predictedLabel);
@@ -191,4 +194,5 @@
       this.crossvalidationTabPage.Controls.Add(this.absoluteErrorLabel);
       this.crossvalidationTabPage.Controls.Add(this.ncdgView);
+      this.crossvalidationTabPage.Controls.Add(this.spearmansRhoView);
       this.crossvalidationTabPage.Controls.Add(this.kendallsTauView);
       this.crossvalidationTabPage.Controls.Add(this.absoluteLogErrorView);
@@ -223,4 +227,14 @@
       this.topNlabel.Text = "Evaluate only best ranked:";
       // 
+      // ndcgLabel
+      // 
+      this.ndcgLabel.AutoSize = true;
+      this.ndcgLabel.Location = new System.Drawing.Point(519, 165);
+      this.ndcgLabel.Margin = new System.Windows.Forms.Padding(3);
+      this.ndcgLabel.Name = "ndcgLabel";
+      this.ndcgLabel.Size = new System.Drawing.Size(106, 13);
+      this.ndcgLabel.TabIndex = 3;
+      this.ndcgLabel.Text = "Mean NDCG (top N):";
+      // 
       // kendallsTauLabel
       // 
@@ -263,4 +277,15 @@
       this.absoluteErrorLabel.TabIndex = 3;
       this.absoluteErrorLabel.Text = "Mean Absolute Error (top N):";
+      // 
+      // ncdgView
+      // 
+      this.ncdgView.Caption = "StringConvertibleValue View";
+      this.ncdgView.Content = null;
+      this.ncdgView.LabelVisible = false;
+      this.ncdgView.Location = new System.Drawing.Point(503, 184);
+      this.ncdgView.Name = "ncdgView";
+      this.ncdgView.ReadOnly = true;
+      this.ncdgView.Size = new System.Drawing.Size(194, 21);
+      this.ncdgView.TabIndex = 2;
       // 
       // kendallsTauView
@@ -339,24 +364,24 @@
       this.minimumTargetLabel.Text = "Target:";
       // 
-      // ncdgView
-      // 
-      this.ncdgView.Caption = "StringConvertibleValue View";
-      this.ncdgView.Content = null;
-      this.ncdgView.LabelVisible = false;
-      this.ncdgView.Location = new System.Drawing.Point(503, 184);
-      this.ncdgView.Name = "ncdgView";
-      this.ncdgView.ReadOnly = true;
-      this.ncdgView.Size = new System.Drawing.Size(194, 21);
-      this.ncdgView.TabIndex = 2;
-      // 
-      // ndcgLabel
-      // 
-      this.ndcgLabel.AutoSize = true;
-      this.ndcgLabel.Location = new System.Drawing.Point(519, 165);
-      this.ndcgLabel.Margin = new System.Windows.Forms.Padding(3);
-      this.ndcgLabel.Name = "ndcgLabel";
-      this.ndcgLabel.Size = new System.Drawing.Size(106, 13);
-      this.ndcgLabel.TabIndex = 3;
-      this.ndcgLabel.Text = "Mean NDCG (top N):";
+      // spearmansRhoView
+      // 
+      this.spearmansRhoView.Caption = "StringConvertibleValue View";
+      this.spearmansRhoView.Content = null;
+      this.spearmansRhoView.LabelVisible = false;
+      this.spearmansRhoView.Location = new System.Drawing.Point(503, 303);
+      this.spearmansRhoView.Name = "spearmansRhoView";
+      this.spearmansRhoView.ReadOnly = true;
+      this.spearmansRhoView.Size = new System.Drawing.Size(194, 21);
+      this.spearmansRhoView.TabIndex = 2;
+      // 
+      // spearmansRhoLabel
+      // 
+      this.spearmansRhoLabel.AutoSize = true;
+      this.spearmansRhoLabel.Location = new System.Drawing.Point(519, 284);
+      this.spearmansRhoLabel.Margin = new System.Windows.Forms.Padding(3);
+      this.spearmansRhoLabel.Name = "spearmansRhoLabel";
+      this.spearmansRhoLabel.Size = new System.Drawing.Size(118, 13);
+      this.spearmansRhoLabel.TabIndex = 3;
+      this.spearmansRhoLabel.Text = "Mean Spearman\'s Rho:";
       // 
       // PerformanceModelingView
@@ -409,4 +434,6 @@
     private System.Windows.Forms.Label ndcgLabel;
     private Data.Views.StringConvertibleValueView ncdgView;
+    private System.Windows.Forms.Label spearmansRhoLabel;
+    private Data.Views.StringConvertibleValueView spearmansRhoView;
   }
 }
Index: branches/1614_GeneralizedQAP/HeuristicLab.OptimizationExpertSystem/3.3/Views/PerformanceModelingView.cs
===================================================================
--- branches/1614_GeneralizedQAP/HeuristicLab.OptimizationExpertSystem/3.3/Views/PerformanceModelingView.cs	(revision 15721)
+++ branches/1614_GeneralizedQAP/HeuristicLab.OptimizationExpertSystem/3.3/Views/PerformanceModelingView.cs	(revision 15736)
@@ -20,4 +20,8 @@
 #endregion
 
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
 using HeuristicLab.Analysis;
 using HeuristicLab.Common.Resources;
@@ -28,8 +32,4 @@
 using HeuristicLab.OptimizationExpertSystem.Common;
 using HeuristicLab.PluginInfrastructure;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
 
 namespace HeuristicLab.OptimizationExpertSystem {
@@ -181,4 +181,6 @@
         var tau = 0.0;
         var tauCnt = 0;
+        var rho = 0.0;
+        var rhoCnt = 0;
         var ndcg = 0.0;
         var ndcgCnt = 0;
@@ -207,7 +209,12 @@
           }
           var kt = KendallsTau(observed, predicted);
-          if (!double.IsNaN(kt)) {
+          if (!double.IsNaN(kt) && !double.IsInfinity(kt)) {
             tau += kt;
             tauCnt++;
+          }
+          var sr = SpearmansRho(observed, predicted);
+          if (!double.IsNaN(sr)) {
+            rho += sr;
+            rhoCnt++;
           }
           var gain = NDCG(observed, model.GetRanking(pi).Take(topN).Select(x => x.Key).ToList());
@@ -216,6 +223,4 @@
             ndcgCnt++;
           }
-          // mean reciprocal rank
-          // optional: expected reciprocal rank
           progress.ProgressValue = ++count / (double)trainingSet.Length;
         }
@@ -223,4 +228,5 @@
         absLogErr /= absLogErrCnt;
         tau /= tauCnt;
+        rho /= rhoCnt;
         ndcg /= ndcgCnt;
 
@@ -230,4 +236,5 @@
         confusionMatrixView.Content = new IntMatrix(confMatrix) {ColumnNames = description};
         kendallsTauView.Content = new DoubleValue(tau);
+        spearmansRhoView.Content = new DoubleValue(rho);
         ncdgView.Content = new DoubleValue(ndcg);
       } finally { progress.Finish(); }
@@ -272,4 +279,19 @@
     }
 
+    private static double SpearmansRho(Dictionary<IAlgorithm, double> performance, Dictionary<IAlgorithm, double> ranking) {
+      var commonKeys = performance.Keys.Intersect(ranking.Keys).ToList();
+      var n = commonKeys.Count;
+      if (n == 0) return double.NaN;
+
+      var actualRanks = commonKeys.Select((x, i) => new { Alg = x, Index = i, Perf = performance[x] })
+                                  .OrderBy(x => x.Perf).Select((x, i) => new { Index = x.Index, Rank = double.IsPositiveInfinity(x.Perf) ? int.MaxValue : i })
+                                  .OrderBy(x => x.Index).Select(x => (double)x.Rank).ToArray();
+      var predictedRanks = commonKeys.Select((x, i) => new { Alg = x, Index = i, Perf = ranking[x] })
+                                     .OrderBy(x => x.Perf).Select((x, i) => new { Index = x.Index, Rank = double.IsPositiveInfinity(x.Perf) ? int.MaxValue : i })
+                                     .OrderBy(x => x.Index).Select(x => (double)x.Rank).ToArray();
+
+      return alglib.spearmancorr2(actualRanks, predictedRanks);
+    }
+
     private static double KendallsTau(Dictionary<IAlgorithm, double> performance, Dictionary<IAlgorithm, double> ranking) {
       var commonKeys = performance.Keys.Intersect(ranking.Keys).ToList();
@@ -278,8 +300,8 @@
 
       var actualRanks = commonKeys.Select((x, i) => new { Alg = x, Index = i, Perf = performance[x] })
-                                  .OrderBy(x => x.Perf).Select((x, i) => new { Index = x.Index, Rank = i })
+                                  .OrderBy(x => x.Perf).Select((x, i) => new { Index = x.Index, Rank = double.IsPositiveInfinity(x.Perf) ? int.MaxValue : i })
                                   .OrderBy(x => x.Index).Select(x => x.Rank).ToList();
       var predictedRanks = commonKeys.Select((x, i) => new { Alg = x, Index = i, Perf = ranking[x] })
-                                     .OrderBy(x => x.Perf).Select((x, i) => new { Index = x.Index, Rank = i })
+                                     .OrderBy(x => x.Perf).Select((x, i) => new { Index = x.Index, Rank = double.IsPositiveInfinity(x.Perf) ? int.MaxValue : i })
                                      .OrderBy(x => x.Index).Select(x => x.Rank).ToList();
 
@@ -292,7 +314,12 @@
             || paired[i].Actual < paired[j].Actual && paired[i].Predicted < paired[j].Predicted)
             concordant++;
-          else discordant++;
+          else if (paired[i].Actual > paired[j].Actual && paired[i].Predicted < paired[j].Predicted
+            || paired[i].Actual < paired[j].Actual && paired[i].Predicted > paired[j].Predicted)
+            discordant++;
         }
-      return (2.0 * (concordant - discordant)) / (n * (n - 1));
+
+      var ti = performance.GroupBy(x => x.Value).Sum(x => x.Count() * (x.Count() - 1));
+      var uj = ranking.GroupBy(x => x.Value).Sum(x => x.Count() * (x.Count() - 1));
+      return (2.0 * (concordant - discordant)) / Math.Sqrt( (n * (n - 1) - ti * (ti - 1)) * (n * (n - 1) - uj * (uj - 1)) );
     }
 
