Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/BinaryMemPR.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/BinaryMemPR.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/BinaryMemPR.cs	(revision 14666)
@@ -0,0 +1,244 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Encodings.BinaryVectorEncoding;
+using HeuristicLab.Optimization;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.PluginInfrastructure;
+using HeuristicLab.Random;
+
+namespace HeuristicLab.Algorithms.MemPR.Binary {
+  [Item("MemPR (binary)", "MemPR implementation for binary vectors.")]
+  [StorableClass]
+  [Creatable(CreatableAttribute.Categories.PopulationBasedAlgorithms, Priority = 999)]
+  public class BinaryMemPR : MemPRAlgorithm<ISingleObjectiveHeuristicOptimizationProblem, BinaryVector, BinaryMemPRPopulationContext, BinaryMemPRSolutionContext> {
+    [StorableConstructor]
+    protected BinaryMemPR(bool deserializing) : base(deserializing) { }
+    protected BinaryMemPR(BinaryMemPR original, Cloner cloner) : base(original, cloner) { }
+    public BinaryMemPR() {
+      foreach (var trainer in ApplicationManager.Manager.GetInstances<ISolutionModelTrainer<BinaryMemPRPopulationContext>>())
+        SolutionModelTrainerParameter.ValidValues.Add(trainer);
+
+      if (SolutionModelTrainerParameter.ValidValues.Count > 0) {
+        var unbiased = SolutionModelTrainerParameter.ValidValues.FirstOrDefault(x => !x.Bias);
+        if (unbiased != null) SolutionModelTrainerParameter.Value = unbiased;
+      }
+
+      foreach (var localSearch in ApplicationManager.Manager.GetInstances<ILocalSearch<BinaryMemPRSolutionContext>>()) {
+        LocalSearchParameter.ValidValues.Add(localSearch);
+      }
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new BinaryMemPR(this, cloner);
+    }
+
+    protected override bool Eq(BinaryVector a, BinaryVector b) {
+      var len = a.Length;
+      for (var i = 0; i < len; i++)
+        if (a[i] != b[i]) return false;
+      return true;
+    }
+
+    protected override double Dist(ISingleObjectiveSolutionScope<BinaryVector> a, ISingleObjectiveSolutionScope<BinaryVector> b) {
+      return 1.0 - HammingSimilarityCalculator.CalculateSimilarity(a.Solution, b.Solution);
+    }
+
+    protected override ISolutionSubspace<BinaryVector> CalculateSubspace(IEnumerable<BinaryVector> solutions, bool inverse = false) {
+      var pop = solutions.ToList();
+      var N = pop[0].Length;
+      var subspace = new bool[N];
+      for (var i = 0; i < N; i++) {
+        var val = pop[0][i];
+        if (inverse) subspace[i] = true;
+        for (var p = 1; p < pop.Count; p++) {
+          if (pop[p][i] != val) subspace[i] = !inverse;
+        }
+      }
+      return new BinarySolutionSubspace(subspace);
+    }
+
+    protected override void AdaptiveWalk(ISingleObjectiveSolutionScope<BinaryVector> scope, int maxEvals, CancellationToken token, ISolutionSubspace<BinaryVector> subspace = null) {
+      var evaluations = 0;
+      var subset = subspace != null ? ((BinarySolutionSubspace)subspace).Subspace : null;
+      if (double.IsNaN(scope.Fitness)) {
+        Context.Evaluate(scope, token);
+        evaluations++;
+      }
+      SingleObjectiveSolutionScope<BinaryVector> bestOfTheWalk = null;
+      var currentScope = (SingleObjectiveSolutionScope<BinaryVector>)scope.Clone();
+      var current = currentScope.Solution;
+      var N = current.Length;
+
+      var subN = subset != null ? subset.Count(x => x) : N;
+      if (subN == 0) return;
+      var order = Enumerable.Range(0, N).Where(x => subset == null || subset[x]).Shuffle(Context.Random).ToArray();
+
+      var bound = Context.Maximization ? Context.Population.Max(x => x.Fitness) : Context.Population.Min(x => x.Fitness);
+      var range = Math.Abs(bound - Context.LocalOptimaLevel);
+      if (range.IsAlmost(0)) range = Math.Abs(bound * 0.05);
+      if (range.IsAlmost(0)) { // because bound = localoptimalevel = 0
+        Context.IncrementEvaluatedSolutions(evaluations);
+        return;
+      }
+      
+      var temp = -range / Math.Log(1.0 / maxEvals);
+      var endtemp = -range / Math.Log(0.1 / maxEvals);
+      var annealFactor = Math.Pow(endtemp / temp, 1.0 / maxEvals);
+      for (var iter = 0; iter < int.MaxValue; iter++) {
+        var moved = false;
+
+        for (var i = 0; i < subN; i++) {
+          var idx = order[i];
+          var before = currentScope.Fitness;
+          current[idx] = !current[idx];
+          Context.Evaluate(currentScope, token);
+          evaluations++;
+          var after = currentScope.Fitness;
+
+          if (Context.IsBetter(after, before) && (bestOfTheWalk == null || Context.IsBetter(after, bestOfTheWalk.Fitness))) {
+            bestOfTheWalk = (SingleObjectiveSolutionScope<BinaryVector>)currentScope.Clone();
+            if (Context.IsBetter(bestOfTheWalk, scope)) {
+              moved = false;
+              break;
+            }
+          }
+          var diff = Context.Maximization ? after - before : before - after;
+          if (diff > 0) moved = true;
+          else {
+            var prob = Math.Exp(diff / temp);
+            if (Context.Random.NextDouble() >= prob) {
+              // the move is not good enough -> undo the move
+              current[idx] = !current[idx];
+              currentScope.Fitness = before;
+            }
+          }
+          temp *= annealFactor;
+          if (evaluations >= maxEvals) break;
+        }
+        if (!moved) break;
+        if (evaluations >= maxEvals) break;
+      }
+
+      Context.IncrementEvaluatedSolutions(evaluations);
+      scope.Adopt(bestOfTheWalk ?? currentScope);
+    }
+
+    protected override ISingleObjectiveSolutionScope<BinaryVector> Breed(ISingleObjectiveSolutionScope<BinaryVector> p1, ISingleObjectiveSolutionScope<BinaryVector> p2, CancellationToken token) {
+      var evaluations = 0;
+      var N = p1.Solution.Length;
+
+      var probe = Context.ToScope((BinaryVector)p1.Solution.Clone());
+
+      var cache = new HashSet<BinaryVector>(new BinaryVectorEqualityComparer());
+      cache.Add(p1.Solution);
+      cache.Add(p2.Solution);
+
+      var cacheHits = new Dictionary<int, int>() { { 0, 0 }, { 1, 0 }, { 2, 0 } };
+      ISingleObjectiveSolutionScope<BinaryVector> offspring = null;
+      
+      while (evaluations < N) {
+        BinaryVector c = null;
+        var xochoice = cacheHits.SampleRandom(Context.Random).Key;
+        switch (xochoice) {
+          case 0: c = NPointCrossover.Apply(Context.Random, p1.Solution, p2.Solution, new IntValue(1)); break;
+          case 1: c = NPointCrossover.Apply(Context.Random, p1.Solution, p2.Solution, new IntValue(2)); break;
+          case 2: c = UniformCrossover.Apply(Context.Random, p1.Solution, p2.Solution); break;
+        }
+        if (cache.Contains(c)) {
+          cacheHits[xochoice]++;
+          if (cacheHits[xochoice] > 10) {
+            cacheHits.Remove(xochoice);
+            if (cacheHits.Count == 0) break;
+          }
+          continue;
+        }
+        probe.Solution = c;
+        Context.Evaluate(probe, token);
+        evaluations++;
+        cache.Add(c);
+        if (offspring == null || Context.IsBetter(probe, offspring)) {
+          offspring = probe;
+          if (Context.IsBetter(offspring, p1) && Context.IsBetter(offspring, p2))
+            break;
+        }
+      }
+      Context.IncrementEvaluatedSolutions(evaluations);
+      return offspring ?? probe;
+    }
+
+    protected override ISingleObjectiveSolutionScope<BinaryVector> Link(ISingleObjectiveSolutionScope<BinaryVector> a, ISingleObjectiveSolutionScope<BinaryVector> b, CancellationToken token, bool delink = false) {
+      var evaluations = 0;
+      var childScope = (ISingleObjectiveSolutionScope<BinaryVector>)a.Clone();
+      var child = childScope.Solution;
+      ISingleObjectiveSolutionScope<BinaryVector> best = null;
+      var cF = a.Fitness;
+      var bF = double.NaN;
+      var order = Enumerable.Range(0, child.Length)
+        .Where(x => !delink && child[x] != b.Solution[x] || delink && child[x] == b.Solution[x])
+        .Shuffle(Context.Random).ToList();
+      if (order.Count == 0) return childScope;
+
+      while (true) {
+        var bestS = double.NaN;
+        var bestI = -1;
+        for (var i = 0; i < order.Count; i++) {
+          var idx = order[i];
+          child[idx] = !child[idx]; // move
+          Context.Evaluate(childScope, token);
+          evaluations++;
+          var s = childScope.Fitness;
+          childScope.Fitness = cF;
+          child[idx] = !child[idx]; // undo move
+          if (Context.IsBetter(s, cF)) {
+            bestS = s;
+            bestI = i;
+            break; // first-improvement
+          }
+          if (Context.IsBetter(s, bestS)) {
+            // least-degrading
+            bestS = s;
+            bestI = i;
+          }
+        }
+        child[order[bestI]] = !child[order[bestI]];
+        order.RemoveAt(bestI);
+        cF = bestS;
+        childScope.Fitness = cF;
+        if (Context.IsBetter(cF, bF)) {
+          bF = cF;
+          best = (ISingleObjectiveSolutionScope<BinaryVector>)childScope.Clone();
+        }
+        if (order.Count == 0) break;
+      }
+      Context.IncrementEvaluatedSolutions(evaluations);
+      return best ?? childScope;
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/BinaryMemPRContext.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/BinaryMemPRContext.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/BinaryMemPRContext.cs	(revision 14666)
@@ -0,0 +1,89 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.BinaryVectorEncoding;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Algorithms.MemPR.Binary {
+  [Item("MemPR Population Context (binary)", "MemPR population context for binary encoded problems.")]
+  [StorableClass]
+  public sealed class BinaryMemPRPopulationContext : MemPRPopulationContext<ISingleObjectiveHeuristicOptimizationProblem, BinaryVector, BinaryMemPRPopulationContext, BinaryMemPRSolutionContext> {
+
+    [StorableConstructor]
+    private BinaryMemPRPopulationContext(bool deserializing) : base(deserializing) { }
+    private BinaryMemPRPopulationContext(BinaryMemPRPopulationContext original, Cloner cloner)
+      : base(original, cloner) { }
+    public BinaryMemPRPopulationContext() : base("BinaryMemPRPopulationContext") { }
+    public BinaryMemPRPopulationContext(string name) : base(name) { }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new BinaryMemPRPopulationContext(this, cloner);
+    }
+
+    public override BinaryMemPRSolutionContext CreateSingleSolutionContext(ISingleObjectiveSolutionScope<BinaryVector> solution) {
+      return new BinaryMemPRSolutionContext(this, solution);
+    }
+
+    public override ISingleObjectiveSolutionScope<BinaryVector> ToScope(BinaryVector code, double fitness = double.NaN) {
+      var creator = Problem.SolutionCreator as IBinaryVectorCreator;
+      if (creator == null) throw new InvalidOperationException("MemPR (binary) context expects a problem with an IBinaryVectorCreator as solution creator.");
+      return new SingleObjectiveSolutionScope<BinaryVector>(code, creator.BinaryVectorParameter.ActualName, fitness, Problem.Evaluator.QualityParameter.ActualName) {
+        Parent = Scope
+      };
+    }
+  }
+
+  [Item("MemPR Solution Context (binary)", "MemPR solution context for binary encoded problems.")]
+  [StorableClass]
+  public sealed class BinaryMemPRSolutionContext : MemPRSolutionContext<ISingleObjectiveHeuristicOptimizationProblem, BinaryVector, BinaryMemPRPopulationContext, BinaryMemPRSolutionContext>, IBinaryVectorSubspaceContext {
+
+    [Storable]
+    private IValueParameter<BinarySolutionSubspace> subspace;
+    public BinarySolutionSubspace Subspace {
+      get { return subspace.Value; }
+    }
+    ISolutionSubspace<BinaryVector> ISolutionSubspaceContext<BinaryVector>.Subspace {
+      get { return Subspace; }
+    }
+
+    [StorableConstructor]
+    private BinaryMemPRSolutionContext(bool deserializing) : base(deserializing) { }
+    private BinaryMemPRSolutionContext(BinaryMemPRSolutionContext original, Cloner cloner)
+      : base(original, cloner) {
+
+    }
+    public BinaryMemPRSolutionContext(BinaryMemPRPopulationContext baseContext, ISingleObjectiveSolutionScope<BinaryVector> solution)
+      : base(baseContext, solution) {
+
+      Parameters.Add(subspace = new ValueParameter<BinarySolutionSubspace>("Subspace", new BinarySolutionSubspace(null)));
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new BinaryMemPRSolutionContext(this, cloner);
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/BinarySolutionSubspace.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/BinarySolutionSubspace.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/BinarySolutionSubspace.cs	(revision 14666)
@@ -0,0 +1,51 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.BinaryVectorEncoding;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Algorithms.MemPR.Binary {
+  [Item("Solution subspace (binary)", "")]
+  [StorableClass]
+  public sealed class BinarySolutionSubspace : Item, ISolutionSubspace<BinaryVector> {
+
+    [Storable]
+    private bool[] subspace;
+    public bool[] Subspace { get { return subspace; } }
+
+    [StorableConstructor]
+    private BinarySolutionSubspace(bool deserializing) : base(deserializing) { }
+    private BinarySolutionSubspace(BinarySolutionSubspace original, Cloner cloner)
+      : base(original, cloner) {
+      subspace = (bool[])original.subspace.Clone();
+    }
+    public BinarySolutionSubspace(bool[] subspace) {
+      this.subspace = subspace;
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new BinarySolutionSubspace(this, cloner);
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/LocalSearch/ExhaustiveBitflip.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/LocalSearch/ExhaustiveBitflip.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/LocalSearch/ExhaustiveBitflip.cs	(revision 14666)
@@ -0,0 +1,63 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Threading;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Algorithms.MemPR.Util;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.Binary.LocalSearch;
+using HeuristicLab.Encodings.BinaryVectorEncoding;
+using HeuristicLab.Optimization;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Algorithms.MemPR.Binary.LocalSearch {
+  [Item("Exhaustive Bitflip Local Search (binary)", "", ExcludeGenericTypeInfo = true)]
+  [StorableClass]
+  public class ExhaustiveBitflip<TContext> : NamedItem, ILocalSearch<TContext>
+    where TContext : ISingleSolutionHeuristicAlgorithmContext<ISingleObjectiveHeuristicOptimizationProblem, BinaryVector>,
+                     IEvaluationServiceContext<BinaryVector> {
+    
+    [StorableConstructor]
+    protected ExhaustiveBitflip(bool deserializing) : base(deserializing) { }
+    protected ExhaustiveBitflip(ExhaustiveBitflip<TContext> original, Cloner cloner) : base(original, cloner) { }
+    public ExhaustiveBitflip() {
+      Name = ItemName;
+      Description = ItemDescription;
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new ExhaustiveBitflip<TContext>(this, cloner);
+    }
+
+    public void Optimize(TContext context) {
+      var quality = context.Solution.Fitness;
+      try {
+        var result = ExhaustiveBitflip.Optimize(context.Random, context.Solution.Solution, ref quality,
+          context.Maximization, context.Evaluate, CancellationToken.None);
+        context.IncrementEvaluatedSolutions(result.Item1);
+        context.Iterations = result.Item2;
+      } finally {
+        context.Solution.Fitness = quality;
+      }
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/LocalSearch/ExhaustiveBitflipSubspace.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/LocalSearch/ExhaustiveBitflipSubspace.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/LocalSearch/ExhaustiveBitflipSubspace.cs	(revision 14666)
@@ -0,0 +1,63 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Threading;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Algorithms.MemPR.Util;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.Binary.LocalSearch;
+using HeuristicLab.Encodings.BinaryVectorEncoding;
+using HeuristicLab.Optimization;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Algorithms.MemPR.Binary.LocalSearch {
+  [Item("Exhaustive Bitflip Local (Subspace) Search (binary)", "", ExcludeGenericTypeInfo = true)]
+  [StorableClass]
+  public class ExhaustiveBitflipSubspace<TContext> : NamedItem, ILocalSearch<TContext>
+      where TContext : ISingleSolutionHeuristicAlgorithmContext<SingleObjectiveBasicProblem<BinaryVectorEncoding>, BinaryVector>,
+                       IBinaryVectorSubspaceContext, IEvaluationServiceContext<BinaryVector> {
+
+    [StorableConstructor]
+    protected ExhaustiveBitflipSubspace(bool deserializing) : base(deserializing) { }
+    protected ExhaustiveBitflipSubspace(ExhaustiveBitflipSubspace<TContext> original, Cloner cloner) : base(original, cloner) { }
+    public ExhaustiveBitflipSubspace() {
+      Name = ItemName;
+      Description = ItemDescription;
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new ExhaustiveBitflipSubspace<TContext>(this, cloner);
+    }
+
+    public void Optimize(TContext context) {
+      var quality = context.Solution.Fitness;
+      try {
+        var result = ExhaustiveBitflip.Optimize(context.Random, context.Solution.Solution, ref quality,
+          context.Problem.Maximization, context.Evaluate, CancellationToken.None, context.Subspace.Subspace);
+        context.IncrementEvaluatedSolutions(result.Item1);
+        context.Iterations = result.Item2;
+      } finally {
+        context.Solution.Fitness = quality;
+      }
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/LocalSearch/StaticAPI/ExhaustiveBitflip.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/LocalSearch/StaticAPI/ExhaustiveBitflip.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/LocalSearch/StaticAPI/ExhaustiveBitflip.cs	(revision 14666)
@@ -0,0 +1,67 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Linq;
+using System.Threading;
+using HeuristicLab.Algorithms.MemPR.Util;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.BinaryVectorEncoding;
+using HeuristicLab.Random;
+
+namespace HeuristicLab.Encodings.Binary.LocalSearch {
+  public static class ExhaustiveBitflip {
+    public static Tuple<int, int> Optimize(IRandom random, BinaryVector solution, ref double quality, bool maximization, Func<BinaryVector, CancellationToken, double> evalFunc, CancellationToken token, bool[] subspace = null) {
+      if (double.IsNaN(quality)) quality = evalFunc(solution, token);
+      var improved = false;
+      var order = Enumerable.Range(0, solution.Length).Shuffle(random).ToArray();
+      var lastImp = -1;
+      var steps = 0;
+      var evaluations = 0;
+      do {
+        improved = false;
+        for (var i = 0; i < solution.Length; i++) {
+          // in case we didn't make an improvement this round and arrived at the index of the last improvement
+          // break means we don't need to try the remaining moves again as they have brought no improvement
+          if (!improved && lastImp == i) break;
+          var idx = order[i];
+          if (subspace != null && !subspace[idx]) continue;
+          // bitflip the solution
+          solution[idx] = !solution[idx];
+          var after = evalFunc(solution, token);
+          evaluations++;
+          if (FitnessComparer.IsBetter(maximization, after, quality)) {
+            steps++;
+            quality = after;
+            lastImp = i;
+            improved = true;
+          } else {
+            // undo the bitflip in case no improvement was made
+            solution[idx] = !solution[idx];
+          }
+          token.ThrowIfCancellationRequested();
+        }
+      } while (improved);
+
+      return Tuple.Create(evaluations, steps);
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/SolutionModel/Univariate/BiasedModelTrainer.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/SolutionModel/Univariate/BiasedModelTrainer.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/SolutionModel/Univariate/BiasedModelTrainer.cs	(revision 14666)
@@ -0,0 +1,65 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Linq;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Encodings.BinaryVectorEncoding;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Algorithms.MemPR.Binary.SolutionModel.Univariate {
+  [Item("Biased Univariate Model Trainer (binary)", "", ExcludeGenericTypeInfo = true)]
+  [StorableClass]
+  public class BiasedModelTrainer<TContext> : ParameterizedNamedItem, ISolutionModelTrainer<TContext>
+    where TContext : IPopulationBasedHeuristicAlgorithmContext<ISingleObjectiveHeuristicOptimizationProblem, BinaryVector>, ISolutionModelContext<BinaryVector> {
+    
+    public bool Bias { get { return true; } }
+
+    [Storable]
+    private IValueParameter<EnumValue<ModelBiasOptions>> modelBiasParameter;
+    public ModelBiasOptions ModelBias {
+      get { return modelBiasParameter.Value.Value; }
+      set { modelBiasParameter.Value.Value = value; }
+    }
+
+    [StorableConstructor]
+    protected BiasedModelTrainer(bool deserializing) : base(deserializing) { }
+    protected BiasedModelTrainer(BiasedModelTrainer<TContext> original, Cloner cloner)
+      : base(original, cloner) {
+      modelBiasParameter = cloner.Clone(original.modelBiasParameter);
+    }
+    public BiasedModelTrainer() {
+      Parameters.Add(modelBiasParameter = new ValueParameter<EnumValue<ModelBiasOptions>>("Model Bias", "What kind of bias towards better individuals is chosen."));
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new BiasedModelTrainer<TContext>(this, cloner);
+    }
+
+    public void TrainModel(TContext context) {
+      context.Model = Trainer.TrainBiased(ModelBias, context.Random, context.Maximization, context.Population.Select(x => x.Solution), context.Population.Select(x => x.Fitness));
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/SolutionModel/Univariate/StaticAPI/Trainer.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/SolutionModel/Univariate/StaticAPI/Trainer.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/SolutionModel/Univariate/StaticAPI/Trainer.cs	(revision 14666)
@@ -0,0 +1,47 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.BinaryVectorEncoding;
+
+namespace HeuristicLab.Algorithms.MemPR.Binary.SolutionModel.Univariate {
+  public enum ModelBiasOptions { Rank, Fitness }
+
+  public static class Trainer {
+    public static ISolutionModel<BinaryVector> TrainBiased(ModelBiasOptions modelBias, IRandom random, bool maximization, IEnumerable<BinaryVector> population, IEnumerable<double> qualities) {
+      switch (modelBias) {
+        case ModelBiasOptions.Rank:
+          return UnivariateModel.CreateWithRankBias(random, maximization, population, qualities);
+        case ModelBiasOptions.Fitness:
+          return UnivariateModel.CreateWithFitnessBias(random, maximization, population, qualities);
+        default:
+          throw new InvalidOperationException(string.Format("Unknown bias option {0}", modelBias));
+      }
+    }
+
+    public static ISolutionModel<BinaryVector> TrainUnbiased(IRandom random, IEnumerable<BinaryVector> population) {
+      return UnivariateModel.CreateWithoutBias(random, population);
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/SolutionModel/Univariate/UnbiasedModelTrainer.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/SolutionModel/Univariate/UnbiasedModelTrainer.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/SolutionModel/Univariate/UnbiasedModelTrainer.cs	(revision 14666)
@@ -0,0 +1,54 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Linq;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.BinaryVectorEncoding;
+using HeuristicLab.Optimization;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Algorithms.MemPR.Binary.SolutionModel.Univariate {
+  [Item("Unbiased Univariate Model Trainer (binary)", "", ExcludeGenericTypeInfo = true)]
+  [StorableClass]
+  public class UniasedModelTrainer<TContext> : NamedItem, ISolutionModelTrainer<TContext>
+    where TContext : IPopulationBasedHeuristicAlgorithmContext<ISingleObjectiveHeuristicOptimizationProblem, BinaryVector>, ISolutionModelContext<BinaryVector> {
+
+    public bool Bias { get { return false; } }
+
+    [StorableConstructor]
+    protected UniasedModelTrainer(bool deserializing) : base(deserializing) { }
+    protected UniasedModelTrainer(UniasedModelTrainer<TContext> original, Cloner cloner) : base(original, cloner) { }
+    public UniasedModelTrainer() {
+      Name = ItemName;
+      Description = ItemDescription;
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new UniasedModelTrainer<TContext>(this, cloner);
+    }
+
+    public void TrainModel(TContext context) {
+      context.Model = Trainer.TrainUnbiased(context.Random, context.Population.Select(x => x.Solution));
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/SolutionModel/Univariate/UnivariateSolutionModel.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/SolutionModel/Univariate/UnivariateSolutionModel.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Binary/SolutionModel/Univariate/UnivariateSolutionModel.cs	(revision 14666)
@@ -0,0 +1,125 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Encodings.BinaryVectorEncoding;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Random;
+
+namespace HeuristicLab.Algorithms.MemPR.Binary.SolutionModel.Univariate {
+  [Item("Univariate solution model (binary)", "")]
+  [StorableClass]
+  public sealed class UnivariateModel : Item, ISolutionModel<BinaryVector> {
+    [Storable]
+    public DoubleArray Probabilities { get; set; }
+    [Storable]
+    public IRandom Random { get; set; }
+
+    [StorableConstructor]
+    private UnivariateModel(bool deserializing) : base(deserializing) { }
+    private UnivariateModel(UnivariateModel original, Cloner cloner)
+      : base(original, cloner) {
+      Probabilities = cloner.Clone(original.Probabilities);
+      Random = cloner.Clone(original.Random);
+    }
+    public UnivariateModel(IRandom random, int N) : this(random, Enumerable.Range(0, N).Select(x => 0.5).ToArray()) { }
+    public UnivariateModel(IRandom random, double[] probabilities) {
+      Probabilities = new DoubleArray(probabilities);
+      Random = random;
+    }
+    public UnivariateModel(IRandom random, DoubleArray probabilties) {
+      Probabilities = probabilties;
+      Random = random;
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new UnivariateModel(this, cloner);
+    }
+
+    public BinaryVector Sample() {
+      var vec = new BinaryVector(Probabilities.Length);
+      for (var i = 0; i < Probabilities.Length; i++)
+        vec[i] = Random.NextDouble() < Probabilities[i];
+      return vec;
+    }
+
+    public static ISolutionModel<BinaryVector> CreateWithoutBias(IRandom random, IEnumerable<BinaryVector> population) {
+      double[] model = null;
+      var popSize = 0;
+      foreach (var p in population) {
+        popSize++;
+        if (model == null) model = new double[p.Length];
+        for (var x = 0; x < model.Length; x++) {
+          if (p[x]) model[x]++;
+        }
+      }
+      if (model == null) throw new ArgumentException("Cannot train model from empty population.");
+      // normalize to [0;1]
+      var factor = 1.0 / popSize;
+      for (var x = 0; x < model.Length; x++) {
+        model[x] *= factor;
+      }
+      return new UnivariateModel(random, model);
+    }
+
+    public static ISolutionModel<BinaryVector> CreateWithRankBias(IRandom random, bool maximization, IEnumerable<BinaryVector> population, IEnumerable<double> qualities) {
+      var popSize = 0;
+
+      double[] model = null;
+      var pop = population.Zip(qualities, (b, q) => new { Solution = b, Fitness = q });
+      foreach (var ind in maximization ? pop.OrderBy(x => x.Fitness) : pop.OrderByDescending(x => x.Fitness)) {
+        // from worst to best, worst solution has 1 vote, best solution N votes
+        popSize++;
+        if (model == null) model = new double[ind.Solution.Length];
+        for (var x = 0; x < model.Length; x++) {
+          if (ind.Solution[x]) model[x] += popSize;
+        }
+      }
+      if (model == null) throw new ArgumentException("Cannot train model from empty population.");
+      // normalize to [0;1]
+      var factor = 2.0 / (popSize + 1);
+      for (var i = 0; i < model.Length; i++) {
+        model[i] *= factor / popSize;
+      }
+      return new UnivariateModel(random, model);
+    }
+
+    public static ISolutionModel<BinaryVector> CreateWithFitnessBias(IRandom random, bool maximization, IEnumerable<BinaryVector> population, IEnumerable<double> qualities) {
+      var proportions = Util.Auxiliary.PrepareProportional(qualities, true, !maximization);
+      var factor = 1.0 / proportions.Sum();
+      double[] model = null;
+      foreach (var ind in population.Zip(proportions, (p, q) => new { Solution = p, Proportion = q })) {
+        if (model == null) model = new double[ind.Solution.Length];
+        for (var x = 0; x < model.Length; x++) {
+          if (ind.Solution[x]) model[x] += ind.Proportion * factor;
+        }
+      }
+      if (model == null) throw new ArgumentException("Cannot train model from empty population.");
+      return new UnivariateModel(random, model);
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/HeuristicLab.Algorithms.MemPR-3.3.csproj
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/HeuristicLab.Algorithms.MemPR-3.3.csproj	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/HeuristicLab.Algorithms.MemPR-3.3.csproj	(revision 14666)
@@ -0,0 +1,230 @@
+﻿<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{9D274421-6332-4FBC-AAE4-467ACE27C368}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>HeuristicLab.Algorithms.MemPR</RootNamespace>
+    <AssemblyName>HeuristicLab.Algorithms.MemPR-3.3</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <TargetFrameworkProfile />
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>..\..\..\..\trunk\sources\bin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>..\..\..\..\trunk\sources\bin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup>
+    <SignAssembly>true</SignAssembly>
+  </PropertyGroup>
+  <PropertyGroup>
+    <AssemblyOriginatorKeyFile>HeuristicLab.snk</AssemblyOriginatorKeyFile>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>..\..\..\..\trunk\sources\bin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>x64</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+    <OutputPath>..\..\..\..\trunk\sources\bin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>x64</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>..\..\..\..\trunk\sources\bin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
+    <OutputPath>..\..\..\..\trunk\sources\bin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="ALGLIB-3.7.0">
+      <HintPath>..\..\..\..\trunk\sources\bin\ALGLIB-3.7.0.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Algorithms.DataAnalysis-3.4">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Algorithms.DataAnalysis-3.4.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Collections-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Collections-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Common-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Common-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Common.Resources-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Common.Resources-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Core-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Core-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Data-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Data-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Encodings.BinaryVectorEncoding-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Encodings.BinaryVectorEncoding-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Encodings.LinearLinkageEncoding-3.4">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Encodings.LinearLinkageEncoding-3.4.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Operators-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Operators-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Optimization.Operators-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Optimization.Operators-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Parameters-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Parameters-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Persistence-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Persistence-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.PluginInfrastructure-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.PluginInfrastructure-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Problems.DataAnalysis-3.4">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Problems.DataAnalysis-3.4.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Random-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Random-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Binary\BinaryMemPR.cs" />
+    <Compile Include="Binary\BinaryMemPRContext.cs" />
+    <Compile Include="Binary\BinarySolutionSubspace.cs" />
+    <Compile Include="Binary\LocalSearch\ExhaustiveBitflipSubspace.cs" />
+    <Compile Include="Binary\LocalSearch\ExhaustiveBitflip.cs" />
+    <Compile Include="Binary\LocalSearch\StaticAPI\ExhaustiveBitflip.cs" />
+    <Compile Include="Binary\SolutionModel\Univariate\StaticAPI\Trainer.cs" />
+    <Compile Include="Binary\SolutionModel\Univariate\UnivariateSolutionModel.cs" />
+    <Compile Include="Binary\SolutionModel\Univariate\BiasedModelTrainer.cs" />
+    <Compile Include="Binary\SolutionModel\Univariate\UnbiasedModelTrainer.cs" />
+    <Compile Include="Interfaces\Interfaces.cs" />
+    <Compile Include="LinearLinkage\LinearLinkageMemPR.cs" />
+    <Compile Include="LinearLinkage\LinearLinkageSolutionSubspace.cs" />
+    <Compile Include="LinearLinkage\LinearLinkageMemPRContext.cs" />
+    <Compile Include="LinearLinkage\LocalSearch\ExhaustiveSubspace.cs" />
+    <Compile Include="LinearLinkage\LocalSearch\StaticAPI\ExhaustiveLocalSearch.cs" />
+    <Compile Include="LinearLinkage\SolutionModel\Univariate\StaticAPI\Trainer.cs" />
+    <Compile Include="LinearLinkage\SolutionModel\Univariate\UnbiasedModelTrainer.cs" />
+    <Compile Include="LinearLinkage\SolutionModel\Univariate\UnivariateSolutionModel.cs" />
+    <Compile Include="MemPRAlgorithm.cs" />
+    <Compile Include="Permutation\PermutationMemPR.cs" />
+    <Compile Include="Permutation\PermutationMemPRContext.cs" />
+    <Compile Include="Permutation\LocalSearch\ExhaustiveHillClimbSubspace.cs" />
+    <Compile Include="Permutation\LocalSearch\ExhaustiveHillClimb.cs" />
+    <Compile Include="Permutation\PermutationSolutionSubspace.cs" />
+    <Compile Include="Permutation\LocalSearch\StaticAPI\Exhaustive.cs" />
+    <Compile Include="Permutation\LocalSearch\StaticAPI\Exhaustive1Shift.cs" />
+    <Compile Include="Permutation\LocalSearch\StaticAPI\Exhaustive2Opt.cs" />
+    <Compile Include="Permutation\LocalSearch\StaticAPI\ExhaustiveSwap2.cs" />
+    <Compile Include="Permutation\SolutionModel\Univariate\BiasedModelTrainer.cs" />
+    <Compile Include="Permutation\SolutionModel\Univariate\StaticAPI\Trainer.cs" />
+    <Compile Include="Permutation\SolutionModel\Univariate\UnbiasedModelTrainer.cs" />
+    <Compile Include="Permutation\SolutionModel\Univariate\UnivariateRelativeModel.cs" />
+    <Compile Include="Permutation\SolutionModel\Univariate\UnivariateAbsoluteModel.cs" />
+    <Compile Include="SingleObjectiveSolutionScope.cs" />
+    <Compile Include="MemPRContext.cs" />
+    <Compile Include="Plugin.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Util\Auxiliary.cs" />
+    <Compile Include="Util\CkMeans1D.cs" />
+    <Compile Include="Util\FitnessComparer.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\HeuristicLab.Analysis\3.3\HeuristicLab.Analysis-3.3.csproj">
+      <Project>{887425b4-4348-49ed-a457-b7d2c26ddbf9}</Project>
+      <Name>HeuristicLab.Analysis-3.3</Name>
+      <Private>False</Private>
+    </ProjectReference>
+    <ProjectReference Include="..\..\HeuristicLab.Encodings.PermutationEncoding\3.3\HeuristicLab.Encodings.PermutationEncoding-3.3.csproj">
+      <Project>{dbecb8b0-b166-4133-baf1-ed67c3fd7fca}</Project>
+      <Name>HeuristicLab.Encodings.PermutationEncoding-3.3</Name>
+      <Private>False</Private>
+    </ProjectReference>
+    <ProjectReference Include="..\..\HeuristicLab.Optimization\3.3\HeuristicLab.Optimization-3.3.csproj">
+      <Project>{14ab8d24-25bc-400c-a846-4627aa945192}</Project>
+      <Name>HeuristicLab.Optimization-3.3</Name>
+      <Private>False</Private>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="HeuristicLab.snk" />
+  </ItemGroup>
+  <ItemGroup />
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <PropertyGroup>
+    <PreBuildEvent>set Path=%25Path%25;$(ProjectDir);$(SolutionDir)
+set ProjectDir=$(ProjectDir)
+set SolutionDir=$(SolutionDir)
+set Outdir=$(Outdir)
+
+call PreBuildEvent.cmd</PreBuildEvent>
+  </PropertyGroup>
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Interfaces/Interfaces.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Interfaces/Interfaces.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Interfaces/Interfaces.cs	(revision 14666)
@@ -0,0 +1,123 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+ 
+using System.Collections.Generic;
+using System.Threading;
+using HeuristicLab.Algorithms.MemPR.Binary;
+using HeuristicLab.Algorithms.MemPR.Grouping;
+using HeuristicLab.Algorithms.MemPR.Permutation;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.BinaryVectorEncoding;
+using HeuristicLab.Optimization;
+
+namespace HeuristicLab.Algorithms.MemPR.Interfaces {
+
+  /*************************************************
+   * ********************************************* *
+   *                    DATA                       *
+   * ********************************************* *
+   *************************************************/
+
+  public interface ISolutionModel<TSolution> : IItem {
+    TSolution Sample();
+  }
+
+  public interface ISolutionSubspace<TSolution> : IItem { }
+
+  /*************************************************
+   * ********************************************* *
+   *                  OPERATORS                    *
+   * ********************************************* *
+   *************************************************/
+   
+  public interface ISolutionModelTrainer<TContext> : IItem {
+    bool Bias { get; }
+    void TrainModel(TContext context);
+  }
+
+  public interface ILocalSearch<TContext> : IItem {
+    void Optimize(TContext context);
+  }
+  
+  /*************************************************
+   * ********************************************* *
+   *                  CONTEXTS                     *
+   * ********************************************* *
+   *************************************************/
+
+  public interface IHeuristicAlgorithmContext<TProblem, TSolution> : IExecutionContext
+      where TProblem : class, ISingleObjectiveHeuristicOptimizationProblem {
+    TProblem Problem { get; }
+    bool Maximization { get; }
+    IRandom Random { get; }
+    int Iterations { get; set; }
+    int EvaluatedSolutions { get; }
+    void IncrementEvaluatedSolutions(int byEvaluations);
+    double BestQuality { get; set; }
+    TSolution BestSolution { get; set; }
+  }
+
+  public interface IEvaluationServiceContext<TSolution> : IExecutionContext {
+    double Evaluate(TSolution solution, CancellationToken token);
+    void Evaluate(ISingleObjectiveSolutionScope<TSolution> scope, CancellationToken token);
+  }
+
+  public interface IPopulationBasedHeuristicAlgorithmContext<TProblem, TSolution> : IHeuristicAlgorithmContext<TProblem, TSolution>
+      where TProblem : class, ISingleObjectiveHeuristicOptimizationProblem {
+    IEnumerable<ISingleObjectiveSolutionScope<TSolution>> Population { get; }
+  }
+
+  public interface ISingleSolutionHeuristicAlgorithmContext<TProblem, TSolution> : IHeuristicAlgorithmContext<TProblem, TSolution>
+      where TProblem : class, ISingleObjectiveHeuristicOptimizationProblem {
+    ISingleObjectiveSolutionScope<TSolution> Solution { get; }
+  }
+
+  public interface ISolutionModelContext<TSolution> : IExecutionContext {
+    ISolutionModel<TSolution> Model { get; set; }
+  }
+
+  public interface ISolutionSubspaceContext<TSolution> : IExecutionContext {
+    ISolutionSubspace<TSolution> Subspace { get; }
+  }
+  public interface IBinaryVectorSubspaceContext : ISolutionSubspaceContext<BinaryVector> {
+    new BinarySolutionSubspace Subspace { get; }
+  }
+  public interface IPermutationSubspaceContext : ISolutionSubspaceContext<Encodings.PermutationEncoding.Permutation> {
+    new PermutationSolutionSubspace Subspace { get; }
+  }
+  public interface ILinearLinkageSubspaceContext : ISolutionSubspaceContext<Encodings.LinearLinkageEncoding.LinearLinkage> {
+    new LinearLinkageSolutionSubspace Subspace { get; }
+  }
+
+
+  /*************************************************
+   * ********************************************* *
+   *                   SCOPES                      *
+   * ********************************************* *
+   *************************************************/
+
+  public interface ISingleObjectiveSolutionScope<TSolution> : IScope {
+    TSolution Solution { get; set; }
+    double Fitness { get; set; }
+
+    void Adopt(ISingleObjectiveSolutionScope<TSolution> orphan);
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LinearLinkageMemPR.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LinearLinkageMemPR.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LinearLinkageMemPR.cs	(revision 14666)
@@ -0,0 +1,326 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Algorithms.MemPR.Util;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.LinearLinkageEncoding;
+using HeuristicLab.Optimization;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.PluginInfrastructure;
+using HeuristicLab.Random;
+
+namespace HeuristicLab.Algorithms.MemPR.Grouping {
+  [Item("MemPR (linear linkage)", "MemPR implementation for linear linkage vectors.")]
+  [StorableClass]
+  [Creatable(CreatableAttribute.Categories.PopulationBasedAlgorithms, Priority = 999)]
+  public class LinearLinkageMemPR : MemPRAlgorithm<ISingleObjectiveHeuristicOptimizationProblem, LinearLinkage, LinearLinkageMemPRPopulationContext, LinearLinkageMemPRSolutionContext> {
+    [StorableConstructor]
+    protected LinearLinkageMemPR(bool deserializing) : base(deserializing) { }
+    protected LinearLinkageMemPR(LinearLinkageMemPR original, Cloner cloner) : base(original, cloner) { }
+    public LinearLinkageMemPR() {
+      foreach (var trainer in ApplicationManager.Manager.GetInstances<ISolutionModelTrainer<LinearLinkageMemPRPopulationContext>>())
+        SolutionModelTrainerParameter.ValidValues.Add(trainer);
+
+      if (SolutionModelTrainerParameter.ValidValues.Count > 0) {
+        var unbiased = SolutionModelTrainerParameter.ValidValues.FirstOrDefault(x => !x.Bias);
+        if (unbiased != null) SolutionModelTrainerParameter.Value = unbiased;
+      }
+
+      foreach (var localSearch in ApplicationManager.Manager.GetInstances<ILocalSearch<LinearLinkageMemPRSolutionContext>>()) {
+        LocalSearchParameter.ValidValues.Add(localSearch);
+      }
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new LinearLinkageMemPR(this, cloner);
+    }
+
+    protected override bool Eq(LinearLinkage a, LinearLinkage b) {
+      if (a.Length != b.Length) return false;
+      for (var i = 0; i < a.Length; i++)
+        if (a[i] != b[i]) return false;
+      return true;
+    }
+
+    protected override double Dist(ISingleObjectiveSolutionScope<LinearLinkage> a, ISingleObjectiveSolutionScope<LinearLinkage> b) {
+      return Dist(a.Solution, b.Solution);
+    }
+
+    private double Dist(LinearLinkage a, LinearLinkage b) {
+      return 1.0 - HammingSimilarityCalculator.CalculateSimilarity(a, b);
+    }
+
+    protected override ISolutionSubspace<LinearLinkage> CalculateSubspace(IEnumerable<LinearLinkage> solutions, bool inverse = false) {
+      var pop = solutions.ToList();
+      var N = pop[0].Length;
+      var subspace = new bool[N];
+      for (var i = 0; i < N; i++) {
+        var val = pop[0][i];
+        if (inverse) subspace[i] = true;
+        for (var p = 1; p < pop.Count; p++) {
+          if (pop[p][i] != val) subspace[i] = !inverse;
+        }
+      }
+      return new LinearLinkageSolutionSubspace(subspace);
+    }
+
+    protected override void AdaptiveWalk(
+        ISingleObjectiveSolutionScope<LinearLinkage> scope,
+        int maxEvals, CancellationToken token,
+        ISolutionSubspace<LinearLinkage> sub_space = null) {
+      var maximization = Context.Maximization;
+      var subspace = sub_space is LinearLinkageSolutionSubspace ? ((LinearLinkageSolutionSubspace)sub_space).Subspace : null;
+      var evaluations = 0;
+      var quality = scope.Fitness;
+      if (double.IsNaN(quality)) {
+        Context.Evaluate(scope, token);
+        quality = scope.Fitness;
+        evaluations++;
+        if (evaluations >= maxEvals) return;
+      }
+      var bestQuality = quality;
+      var currentScope = (ISingleObjectiveSolutionScope<LinearLinkage>)scope.Clone();
+      var current = currentScope.Solution;
+      LinearLinkage bestOfTheWalk = null;
+      var bestOfTheWalkF = double.NaN;
+
+      var tabu = new double[current.Length, current.Length];
+      for (var i = 0; i < current.Length; i++) {
+        for (var j = i; j < current.Length; j++) {
+          tabu[i, j] = tabu[j, i] = maximization ? double.MinValue : double.MaxValue;
+        }
+        tabu[i, current[i]] = quality;
+      }
+
+      // this dictionary holds the last relevant links
+      var groupItems = new List<int>();
+      var lleb = current.ToBackLinks();
+      Move bestOfTheRest = null;
+      var bestOfTheRestF = double.NaN;
+      var lastAppliedMove = -1;
+      for (var iter = 0; iter < int.MaxValue; iter++) {
+        // clear the dictionary before a new pass through the array is made
+        groupItems.Clear();
+        for (var i = 0; i < current.Length; i++) {
+          if (subspace != null && !subspace[i]) {
+            if (lleb[i] != i)
+              groupItems.Remove(lleb[i]);
+            groupItems.Add(i);
+            continue;
+          }
+
+          var next = current[i];
+
+          if (lastAppliedMove == i) {
+            if (bestOfTheRest != null) {
+              bestOfTheRest.Apply(current);
+              bestOfTheRest.ApplyToLLEb(lleb);
+              currentScope.Fitness = bestOfTheRestF;
+              quality = bestOfTheRestF;
+              if (maximization) {
+                tabu[i, next] = Math.Max(tabu[i, next], bestOfTheRestF);
+                tabu[i, current[i]] = Math.Max(tabu[i, current[i]], bestOfTheRestF);
+              } else {
+                tabu[i, next] = Math.Min(tabu[i, next], bestOfTheRestF);
+                tabu[i, current[i]] = Math.Min(tabu[i, current[i]], bestOfTheRestF);
+              }
+              if (FitnessComparer.IsBetter(maximization, bestOfTheRestF, bestOfTheWalkF)) {
+                bestOfTheWalk = (LinearLinkage)current.Clone();
+                bestOfTheWalkF = bestOfTheRestF;
+              }
+              bestOfTheRest = null;
+              bestOfTheRestF = double.NaN;
+              lastAppliedMove = i;
+            } else {
+              lastAppliedMove = -1;
+            }
+            break;
+          } else {
+            foreach (var move in MoveGenerator.GenerateForItem(i, groupItems, current, lleb)) {
+              // we intend to break link i -> next
+              var qualityToBreak = tabu[i, next];
+              move.Apply(current);
+              var qualityToRestore = tabu[i, current[i]]; // current[i] is new next
+              Context.Evaluate(currentScope, token);
+              evaluations++;
+              var moveF = currentScope.Fitness;
+              var isNotTabu = FitnessComparer.IsBetter(maximization, moveF, qualityToBreak)
+                              && FitnessComparer.IsBetter(maximization, moveF, qualityToRestore);
+              var isImprovement = FitnessComparer.IsBetter(maximization, moveF, quality);
+              var isAspired = FitnessComparer.IsBetter(maximization, moveF, bestQuality);
+              if ((isNotTabu && isImprovement) || isAspired) {
+                if (maximization) {
+                  tabu[i, next] = Math.Max(tabu[i, next], moveF);
+                  tabu[i, current[i]] = Math.Max(tabu[i, current[i]], moveF);
+                } else {
+                  tabu[i, next] = Math.Min(tabu[i, next], moveF);
+                  tabu[i, current[i]] = Math.Min(tabu[i, current[i]], moveF);
+                }
+                quality = moveF;
+                if (isAspired) bestQuality = quality;
+
+                move.ApplyToLLEb(lleb);
+
+                if (FitnessComparer.IsBetter(maximization, moveF, bestOfTheWalkF)) {
+                  bestOfTheWalk = (LinearLinkage)current.Clone();
+                  bestOfTheWalkF = moveF;
+                }
+
+                bestOfTheRest = null;
+                bestOfTheRestF = double.NaN;
+                lastAppliedMove = i;
+                break;
+              } else {
+                if (isNotTabu) {
+                  if (FitnessComparer.IsBetter(maximization, moveF, bestOfTheRestF)) {
+                    bestOfTheRest = move;
+                    bestOfTheRestF = moveF;
+                  }
+                }
+                move.Undo(current);
+                currentScope.Fitness = quality;
+              }
+              if (evaluations >= maxEvals) break;
+            }
+          }
+          if (lleb[i] != i)
+            groupItems.Remove(lleb[i]);
+          groupItems.Add(i);
+          if (evaluations >= maxEvals) break;
+          if (token.IsCancellationRequested) break;
+        }
+        if (lastAppliedMove == -1) { // no move has been applied
+          if (bestOfTheRest != null) {
+            var i = bestOfTheRest.Item;
+            var next = current[i];
+            bestOfTheRest.Apply(current);
+            currentScope.Fitness = bestOfTheRestF;
+            quality = bestOfTheRestF;
+            if (maximization) {
+              tabu[i, next] = Math.Max(tabu[i, next], bestOfTheRestF);
+              tabu[i, current[i]] = Math.Max(tabu[i, current[i]], bestOfTheRestF);
+            } else {
+              tabu[i, next] = Math.Min(tabu[i, next], bestOfTheRestF);
+              tabu[i, current[i]] = Math.Min(tabu[i, current[i]], bestOfTheRestF);
+            }
+            if (FitnessComparer.IsBetter(maximization, bestOfTheRestF, bestOfTheWalkF)) {
+              bestOfTheWalk = (LinearLinkage)current.Clone();
+              bestOfTheWalkF = bestOfTheRestF;
+            }
+
+            bestOfTheRest = null;
+            bestOfTheRestF = double.NaN;
+          } else break;
+        }
+        if (evaluations >= maxEvals) break;
+        if (token.IsCancellationRequested) break;
+      }
+      if (bestOfTheWalk != null) {
+        scope.Solution = bestOfTheWalk;
+        scope.Fitness = bestOfTheWalkF;
+      }
+    }
+
+    protected override ISingleObjectiveSolutionScope<LinearLinkage> Breed(ISingleObjectiveSolutionScope<LinearLinkage> p1, ISingleObjectiveSolutionScope<LinearLinkage> p2, CancellationToken token) {
+      var cache = new HashSet<LinearLinkage>(new LinearLinkageEqualityComparer());
+      cache.Add(p1.Solution);
+      cache.Add(p2.Solution);
+
+      var cacheHits = new Dictionary<int, int>() { { 0, 0 }, { 1, 0 } };
+      var evaluations = 0;
+      var probe = Context.ToScope((LinearLinkage)p1.Solution.Clone());
+      ISingleObjectiveSolutionScope<LinearLinkage> offspring = null;
+      while (evaluations < p1.Solution.Length) {
+        LinearLinkage c = null;
+        var xochoice = cacheHits.SampleRandom(Context.Random).Key;
+        switch (xochoice) {
+          case 0: c = GroupCrossover.Apply(Context.Random, p1.Solution, p2.Solution); break;
+          case 1: c = SinglePointCrossover.Apply(Context.Random, p1.Solution, p2.Solution); break;
+        }
+        if (cache.Contains(c)) {
+          cacheHits[xochoice]++;
+          if (cacheHits[xochoice] > 10) {
+            cacheHits.Remove(xochoice);
+            if (cacheHits.Count == 0) break;
+          }
+          continue;
+        }
+        probe.Solution = c;
+        Context.Evaluate(probe, token);
+        evaluations++;
+        cache.Add(c);
+        if (offspring == null || Context.IsBetter(probe, offspring)) {
+          offspring = probe;
+          if (Context.IsBetter(offspring, p1) && Context.IsBetter(offspring, p2))
+            break;
+        }
+      }
+      Context.IncrementEvaluatedSolutions(evaluations);
+      return offspring ?? probe;
+    }
+
+    protected override ISingleObjectiveSolutionScope<LinearLinkage> Link(ISingleObjectiveSolutionScope<LinearLinkage> a, ISingleObjectiveSolutionScope<LinearLinkage> b, CancellationToken token, bool delink = false) {
+      var evaluations = 0;
+      var probe = (ISingleObjectiveSolutionScope<LinearLinkage>)a.Clone();
+      ISingleObjectiveSolutionScope<LinearLinkage> best = null;
+      while (true) {
+        Move bestMove = null;
+        var bestMoveQ = double.NaN;
+        // this approach may not fully relink the two solutions
+        foreach (var m in MoveGenerator.Generate(probe.Solution)) {
+          var distBefore = Dist(probe, b);
+          m.Apply(probe.Solution);
+          var distAfter = Dist(probe, b);
+          // consider all moves that would increase the distance between probe and b
+          // or decrease it depending on whether we do delinking or relinking
+          if (delink && distAfter > distBefore || !delink && distAfter < distBefore) {
+            var beforeQ = probe.Fitness;
+            Context.Evaluate(probe, token);
+            evaluations++;
+            var q = probe.Fitness;
+            m.Undo(probe.Solution);
+            probe.Fitness = beforeQ;
+
+            if (Context.IsBetter(q, bestMoveQ)) {
+              bestMove = m;
+              bestMoveQ = q;
+            }
+            if (Context.IsBetter(q, beforeQ)) break;
+          } else m.Undo(probe.Solution);
+        }
+        if (bestMove == null) break;
+        bestMove.Apply(probe.Solution);
+        probe.Fitness = bestMoveQ;
+        if (best == null || Context.IsBetter(probe, best))
+          best = (ISingleObjectiveSolutionScope<LinearLinkage>)probe.Clone();
+      }
+      Context.IncrementEvaluatedSolutions(evaluations);
+
+      return best ?? probe;
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LinearLinkageMemPRContext.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LinearLinkageMemPRContext.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LinearLinkageMemPRContext.cs	(revision 14666)
@@ -0,0 +1,90 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Runtime.Remoting.Contexts;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.LinearLinkageEncoding;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Algorithms.MemPR.Grouping {
+  [Item("MemPR Population Context (linear linkage)", "MemPR population context for linear linkage encoded problems.")]
+  [StorableClass]
+  public sealed class LinearLinkageMemPRPopulationContext : MemPRPopulationContext<ISingleObjectiveHeuristicOptimizationProblem, LinearLinkage, LinearLinkageMemPRPopulationContext, LinearLinkageMemPRSolutionContext> {
+
+    [StorableConstructor]
+    private LinearLinkageMemPRPopulationContext(bool deserializing) : base(deserializing) { }
+    private LinearLinkageMemPRPopulationContext(LinearLinkageMemPRPopulationContext original, Cloner cloner)
+      : base(original, cloner) { }
+    public LinearLinkageMemPRPopulationContext() : base("LinearLinkageMemPRPopulationContext") { }
+    public LinearLinkageMemPRPopulationContext(string name) : base(name) { }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new LinearLinkageMemPRPopulationContext(this, cloner);
+    }
+
+    public override LinearLinkageMemPRSolutionContext CreateSingleSolutionContext(ISingleObjectiveSolutionScope<LinearLinkage> solution) {
+      return new LinearLinkageMemPRSolutionContext(this, solution);
+    }
+
+    public override ISingleObjectiveSolutionScope<LinearLinkage> ToScope(LinearLinkage code, double fitness = double.NaN) {
+      var creator = Problem.SolutionCreator as ILinearLinkageCreator;
+      if (creator == null) throw new InvalidOperationException("Can only solve linear linkage encoded problems with MemPR (linear linkage)");
+      return new SingleObjectiveSolutionScope<LinearLinkage>(code, creator.LLEParameter.ActualName, fitness, Problem.Evaluator.QualityParameter.ActualName) {
+        Parent = Scope
+      };
+    }
+  }
+
+  [Item("MemPR Solution Context (linear linkage)", "MemPR solution context for linear linkage encoded problems.")]
+  [StorableClass]
+  public sealed class LinearLinkageMemPRSolutionContext : MemPRSolutionContext<ISingleObjectiveHeuristicOptimizationProblem, LinearLinkage, LinearLinkageMemPRPopulationContext, LinearLinkageMemPRSolutionContext>, ILinearLinkageSubspaceContext {
+
+    [Storable]
+    private IValueParameter<LinearLinkageSolutionSubspace> subspace;
+    public LinearLinkageSolutionSubspace Subspace {
+      get { return subspace.Value; }
+    }
+    ISolutionSubspace<LinearLinkage> ISolutionSubspaceContext<LinearLinkage>.Subspace {
+      get { return Subspace; }
+    }
+
+    [StorableConstructor]
+    private LinearLinkageMemPRSolutionContext(bool deserializing) : base(deserializing) { }
+    private LinearLinkageMemPRSolutionContext(LinearLinkageMemPRSolutionContext original, Cloner cloner)
+      : base(original, cloner) {
+
+    }
+    public LinearLinkageMemPRSolutionContext(LinearLinkageMemPRPopulationContext baseContext, ISingleObjectiveSolutionScope<LinearLinkage> solution)
+      : base(baseContext, solution) {
+
+      Parameters.Add(subspace = new ValueParameter<LinearLinkageSolutionSubspace>("Subspace", new LinearLinkageSolutionSubspace(null)));
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new LinearLinkageMemPRSolutionContext(this, cloner);
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LinearLinkageSolutionSubspace.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LinearLinkageSolutionSubspace.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LinearLinkageSolutionSubspace.cs	(revision 14666)
@@ -0,0 +1,51 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.LinearLinkageEncoding;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Algorithms.MemPR.Grouping {
+  [Item("Solution subspace (linear linkage)", "")]
+  [StorableClass]
+  public sealed class LinearLinkageSolutionSubspace : Item, ISolutionSubspace<LinearLinkage> {
+
+    [Storable]
+    private bool[] subspace;
+    public bool[] Subspace { get { return subspace; } }
+
+    [StorableConstructor]
+    private LinearLinkageSolutionSubspace(bool deserializing) : base(deserializing) { }
+    private LinearLinkageSolutionSubspace(LinearLinkageSolutionSubspace original, Cloner cloner)
+      : base(original, cloner) {
+      subspace = (bool[])original.subspace.Clone();
+    }
+    public LinearLinkageSolutionSubspace(bool[] subspace) {
+      this.subspace = subspace;
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new LinearLinkageSolutionSubspace(this, cloner);
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LocalSearch/ExhaustiveSubspace.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LocalSearch/ExhaustiveSubspace.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LocalSearch/ExhaustiveSubspace.cs	(revision 14666)
@@ -0,0 +1,61 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Threading;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.LinearLinkageEncoding;
+using HeuristicLab.Optimization;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Algorithms.MemPR.Grouping.LocalSearch {
+  [Item("Exhaustive Local (Subspace) Search (linear linkage)", "", ExcludeGenericTypeInfo = true)]
+  [StorableClass]
+  public class ExhaustiveSubspace<TContext> : NamedItem, ILocalSearch<TContext>
+      where TContext : ISingleSolutionHeuristicAlgorithmContext<ISingleObjectiveHeuristicOptimizationProblem, LinearLinkage>,
+                       ILinearLinkageSubspaceContext, IEvaluationServiceContext<LinearLinkage> {
+
+    [StorableConstructor]
+    protected ExhaustiveSubspace(bool deserializing) : base(deserializing) { }
+    protected ExhaustiveSubspace(ExhaustiveSubspace<TContext> original, Cloner cloner) : base(original, cloner) { }
+    public ExhaustiveSubspace() {
+      Name = ItemName;
+      Description = ItemDescription;
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new ExhaustiveSubspace<TContext>(this, cloner);
+    }
+
+    public void Optimize(TContext context) {
+      var quality = context.Solution.Fitness;
+      try {
+        var result = ExhaustiveLocalSearch.Optimize(context.Random, context.Solution.Solution, ref quality,
+          context.Maximization, context.Evaluate, CancellationToken.None, context.Subspace.Subspace);
+        context.IncrementEvaluatedSolutions(result.Item1);
+        context.Iterations = result.Item2;
+      } finally {
+        context.Solution.Fitness = quality;
+      }
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LocalSearch/StaticAPI/ExhaustiveLocalSearch.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LocalSearch/StaticAPI/ExhaustiveLocalSearch.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LocalSearch/StaticAPI/ExhaustiveLocalSearch.cs	(revision 14666)
@@ -0,0 +1,126 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using HeuristicLab.Algorithms.MemPR.Util;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.LinearLinkageEncoding;
+
+namespace HeuristicLab.Algorithms.MemPR.Grouping.LocalSearch {
+  public static class ExhaustiveLocalSearch {
+    public static Tuple<int, int> Optimize(IRandom random, LinearLinkage solution, ref double quality, bool maximization, Func<LinearLinkage, CancellationToken, double> eval, CancellationToken token, bool[] subspace = null) {
+      var evaluations = 0;
+      if (double.IsNaN(quality)) {
+        quality = eval(solution, token);
+        evaluations++;
+      }
+      var steps = 0;
+      var lleb = solution.ToBackLinks();
+      for (var iter = 0; iter < int.MaxValue; iter++) {
+        var change = false;
+        var groupItems = new List<int>();
+        for (var i = 0; i < solution.Length; i++) {
+          foreach (var move in MoveGenerator.GenerateForItem(i, groupItems, solution, lleb).ToList()) {
+            move.Apply(solution);
+            var moveF = eval(solution, token);
+            evaluations++;
+            if (FitnessComparer.IsBetter(maximization, moveF, quality)) {
+              move.ApplyToLLEb(lleb);
+              steps++;
+              quality = moveF;
+              change = true;
+              break;
+            } else {
+              move.Undo(solution);
+            }
+            if (token.IsCancellationRequested) break;
+          }
+          if (lleb[i] != i)
+            groupItems.Remove(lleb[i]);
+          groupItems.Add(i);
+          if (token.IsCancellationRequested) break;
+        }
+        if (!change || token.IsCancellationRequested) break;
+      }
+
+      return Tuple.Create(evaluations, steps);
+    }
+
+    public static Tuple<int, int> OptimizeSwap(IRandom random, LinearLinkage solution, ref double quality, bool maximization, Func<LinearLinkage, IRandom, double> eval, CancellationToken token) {
+      var evaluations = 0;
+      var current = solution;
+      if (double.IsNaN(quality)) {
+        quality = eval(current, random);
+        evaluations++;
+      }
+      var groups = current.GetGroups().ToList();
+      var exclude = new bool[groups.Count, groups.Count];
+      var steps = 0;
+      for (var iter = 0; iter < int.MaxValue; iter++) {
+        var change = false;
+        for (var i = 0; i < groups.Count - 1; i++) {
+          for (var j = i + 1; j < groups.Count; j++) {
+            if (exclude[i, j]) continue;
+            var groupsChanged = false;
+            if (groups[i].Count == 1 && groups[j].Count == 1) continue; // groups are anonymous, swap has no effect
+            for (var x = 0; x < groups[i].Count; x++) {
+              for (var y = 0; y < groups[j].Count; y++) {
+                var a = groups[i][x];
+                var b = groups[j][y];
+                groups[i][x] = b;
+                groups[j][y] = a;
+                current.SetGroups(groups);
+                var moveF = eval(current, random);
+                evaluations++;
+                if (FitnessComparer.IsBetter(maximization, moveF, quality)) {
+                  steps++;
+                  quality = moveF;
+                  groupsChanged = true;
+                } else {
+                  // undo
+                  groups[i][x] = a;
+                  groups[j][y] = b;
+                  current.SetGroups(groups);
+                }
+              }
+            }
+            if (!groupsChanged) {
+              exclude[i, j] = true;
+            } else {
+              change = true;
+              for (var k = 0; k < groups.Count; k++) {
+                exclude[k, j] = false;
+                exclude[i, k] = false;
+              }
+            }
+            if (token.IsCancellationRequested) break;
+          }
+          if (token.IsCancellationRequested) break;
+        }
+        if (!change || token.IsCancellationRequested) break;
+      }
+      return Tuple.Create(evaluations, steps);
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/SolutionModel/Univariate/StaticAPI/Trainer.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/SolutionModel/Univariate/StaticAPI/Trainer.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/SolutionModel/Univariate/StaticAPI/Trainer.cs	(revision 14666)
@@ -0,0 +1,35 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Collections.Generic;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.LinearLinkageEncoding;
+
+namespace HeuristicLab.Algorithms.MemPR.Grouping.SolutionModel.Univariate {
+  public static class Trainer {
+
+    public static ISolutionModel<LinearLinkage> Train(IRandom random,
+        IEnumerable<LinearLinkage> pop) {
+      return UnivariateModel.Create(random, pop);
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/SolutionModel/Univariate/UnbiasedModelTrainer.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/SolutionModel/Univariate/UnbiasedModelTrainer.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/SolutionModel/Univariate/UnbiasedModelTrainer.cs	(revision 14666)
@@ -0,0 +1,54 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Linq;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.LinearLinkageEncoding;
+using HeuristicLab.Optimization;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Algorithms.MemPR.Grouping.SolutionModel.Univariate {
+  [Item("Unbiased Univariate Model Trainer (linear linkage)", "", ExcludeGenericTypeInfo = true)]
+  [StorableClass]
+  public class UniasedModelTrainer<TContext> : NamedItem, ISolutionModelTrainer<TContext>
+    where TContext : IPopulationBasedHeuristicAlgorithmContext<ISingleObjectiveHeuristicOptimizationProblem, LinearLinkage>, ISolutionModelContext<LinearLinkage> {
+
+    public bool Bias { get { return false; } }
+
+    [StorableConstructor]
+    protected UniasedModelTrainer(bool deserializing) : base(deserializing) { }
+    protected UniasedModelTrainer(UniasedModelTrainer<TContext> original, Cloner cloner) : base(original, cloner) { }
+    public UniasedModelTrainer() {
+      Name = ItemName;
+      Description = ItemDescription;
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new UniasedModelTrainer<TContext>(this, cloner);
+    }
+
+    public void TrainModel(TContext context) {
+      context.Model = Trainer.Train(context.Random, context.Population.Select(x => x.Solution));
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/SolutionModel/Univariate/UnivariateSolutionModel.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/SolutionModel/Univariate/UnivariateSolutionModel.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/SolutionModel/Univariate/UnivariateSolutionModel.cs	(revision 14666)
@@ -0,0 +1,110 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Encodings.LinearLinkageEncoding;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Algorithms.MemPR.Grouping.SolutionModel.Univariate {
+  [Item("Univariate solution model (linear linkage)", "")]
+  [StorableClass]
+  public sealed class UnivariateModel : Item, ISolutionModel<LinearLinkage> {
+    [Storable]
+    public IntMatrix Frequencies { get; set; }
+    [Storable]
+    public IRandom Random { get; set; }
+    [Storable]
+    public IntValue Maximum { get; set; }
+
+    [StorableConstructor]
+    private UnivariateModel(bool deserializing) : base(deserializing) { }
+    private UnivariateModel(UnivariateModel original, Cloner cloner)
+      : base(original, cloner) {
+      Frequencies = cloner.Clone(original.Frequencies);
+      Random = cloner.Clone(original.Random);
+    }
+    public UnivariateModel(IRandom random, int[,] frequencies, int max) {
+      Frequencies = new IntMatrix(frequencies);
+      Random = random;
+      Maximum = new IntValue(max);
+    }
+    public UnivariateModel(IRandom random, IntMatrix frequencies, int max) {
+      Frequencies = frequencies;
+      Random = random;
+      Maximum = new IntValue(max);
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new UnivariateModel(this, cloner);
+    }
+
+    public LinearLinkage Sample() {
+      var N = Frequencies.Rows;
+      var centroid = LinearLinkage.SingleElementGroups(N);
+      var dict = new Dictionary<int, int>();
+      for (var i = N - 1; i >= 0; i--) {
+        centroid[i] = i; // default be a cluster of your own
+        for (var j = i + 1; j < N; j++) {
+          // try to find a suitable link
+          if (Maximum.Value * Random.NextDouble() < Frequencies[i, j]) {
+            int pred;
+            if (dict.TryGetValue(j, out pred)) {
+              int tmp, k = pred;
+              while (dict.TryGetValue(k, out tmp)) {
+                if (k == tmp) break;
+                k = tmp;
+              }
+              centroid[i] = k;
+            } else centroid[i] = j;
+            dict[centroid[i]] = i;
+            break;
+          }
+        }
+      }
+      return centroid;
+    }
+
+    public static ISolutionModel<LinearLinkage> Create(IRandom random, IEnumerable<LinearLinkage> population) {
+      var iter = population.GetEnumerator();
+      if (!iter.MoveNext()) throw new ArgumentException("Cannot create solution model from empty population.");
+      var popSize = 1;
+      var N = iter.Current.Length;
+      var freq = new int[N, N];
+      do {
+        var current = iter.Current;
+        popSize++;
+        foreach (var g in current.GetGroups()) {
+          for (var i = 0; i < g.Count - 1; i++)
+            for (var j = i + 1; j < g.Count; j++) {
+              freq[g[i], g[j]]++;
+              freq[g[j], g[i]]++;
+            }
+        }
+      } while (iter.MoveNext());
+      return new UnivariateModel(random, freq, popSize);
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/MemPRAlgorithm.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/MemPRAlgorithm.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/MemPRAlgorithm.cs	(revision 14666)
@@ -0,0 +1,682 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Threading;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Analysis;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Random;
+
+namespace HeuristicLab.Algorithms.MemPR {
+  [Item("MemPR Algorithm", "Base class for MemPR algorithms")]
+  [StorableClass]
+  public abstract class MemPRAlgorithm<TProblem, TSolution, TPopulationContext, TSolutionContext> : BasicAlgorithm, INotifyPropertyChanged
+      where TProblem : class, IItem, ISingleObjectiveHeuristicOptimizationProblem
+      where TSolution : class, IItem
+      where TPopulationContext : MemPRPopulationContext<TProblem, TSolution, TPopulationContext, TSolutionContext>, new()
+      where TSolutionContext : MemPRSolutionContext<TProblem, TSolution, TPopulationContext, TSolutionContext> {
+
+    public override Type ProblemType {
+      get { return typeof(TProblem); }
+    }
+
+    public new TProblem Problem {
+      get { return (TProblem)base.Problem; }
+      set { base.Problem = value; }
+    }
+
+    public override bool SupportsPause {
+      get { return true; }
+    }
+
+    protected string QualityName {
+      get { return Problem != null && Problem.Evaluator != null ? Problem.Evaluator.QualityParameter.ActualName : null; }
+    }
+
+    public int? MaximumEvaluations {
+      get {
+        var val = ((OptionalValueParameter<IntValue>)Parameters["MaximumEvaluations"]).Value;
+        return val != null ? val.Value : (int?)null;
+      }
+      set {
+        var param = (OptionalValueParameter<IntValue>)Parameters["MaximumEvaluations"];
+        param.Value = value.HasValue ? new IntValue(value.Value) : null;
+      }
+    }
+
+    public TimeSpan? MaximumExecutionTime {
+      get {
+        var val = ((OptionalValueParameter<TimeSpanValue>)Parameters["MaximumExecutionTime"]).Value;
+        return val != null ? val.Value : (TimeSpan?)null;
+      }
+      set {
+        var param = (OptionalValueParameter<TimeSpanValue>)Parameters["MaximumExecutionTime"];
+        param.Value = value.HasValue ? new TimeSpanValue(value.Value) : null;
+      }
+    }
+
+    public double? TargetQuality {
+      get {
+        var val = ((OptionalValueParameter<DoubleValue>)Parameters["TargetQuality"]).Value;
+        return val != null ? val.Value : (double?)null;
+      }
+      set {
+        var param = (OptionalValueParameter<DoubleValue>)Parameters["TargetQuality"];
+        param.Value = value.HasValue ? new DoubleValue(value.Value) : null;
+      }
+    }
+
+    protected FixedValueParameter<IntValue> MaximumPopulationSizeParameter {
+      get { return ((FixedValueParameter<IntValue>)Parameters["MaximumPopulationSize"]); }
+    }
+    public int MaximumPopulationSize {
+      get { return MaximumPopulationSizeParameter.Value.Value; }
+      set { MaximumPopulationSizeParameter.Value.Value = value; }
+    }
+
+    public bool SetSeedRandomly {
+      get { return ((FixedValueParameter<BoolValue>)Parameters["SetSeedRandomly"]).Value.Value; }
+      set { ((FixedValueParameter<BoolValue>)Parameters["SetSeedRandomly"]).Value.Value = value; }
+    }
+
+    public int Seed {
+      get { return ((FixedValueParameter<IntValue>)Parameters["Seed"]).Value.Value; }
+      set { ((FixedValueParameter<IntValue>)Parameters["Seed"]).Value.Value = value; }
+    }
+
+    public IAnalyzer Analyzer {
+      get { return ((ValueParameter<IAnalyzer>)Parameters["Analyzer"]).Value; }
+      set { ((ValueParameter<IAnalyzer>)Parameters["Analyzer"]).Value = value; }
+    }
+
+    public IConstrainedValueParameter<ISolutionModelTrainer<TPopulationContext>> SolutionModelTrainerParameter {
+      get { return (IConstrainedValueParameter<ISolutionModelTrainer<TPopulationContext>>)Parameters["SolutionModelTrainer"]; }
+    }
+
+    public IConstrainedValueParameter<ILocalSearch<TSolutionContext>> LocalSearchParameter {
+      get { return (IConstrainedValueParameter<ILocalSearch<TSolutionContext>>)Parameters["LocalSearch"]; }
+    }
+
+    [Storable]
+    private TPopulationContext context;
+    public TPopulationContext Context {
+      get { return context; }
+      protected set {
+        if (context == value) return;
+        context = value;
+        OnPropertyChanged("State");
+      }
+    }
+
+    [Storable]
+    private BestAverageWorstQualityAnalyzer qualityAnalyzer;
+    [Storable]
+    private QualityPerClockAnalyzer qualityPerClockAnalyzer;
+    [Storable]
+    private QualityPerEvaluationsAnalyzer qualityPerEvaluationsAnalyzer;
+
+    [StorableConstructor]
+    protected MemPRAlgorithm(bool deserializing) : base(deserializing) { }
+    protected MemPRAlgorithm(MemPRAlgorithm<TProblem, TSolution, TPopulationContext, TSolutionContext> original, Cloner cloner) : base(original, cloner) {
+      context = cloner.Clone(original.context);
+      qualityAnalyzer = cloner.Clone(original.qualityAnalyzer);
+      qualityPerClockAnalyzer = cloner.Clone(original.qualityPerClockAnalyzer);
+      qualityPerEvaluationsAnalyzer = cloner.Clone(original.qualityPerEvaluationsAnalyzer);
+
+      RegisterEventHandlers();
+    }
+    protected MemPRAlgorithm() {
+      Parameters.Add(new ValueParameter<IAnalyzer>("Analyzer", "The analyzer to apply to the population.", new MultiAnalyzer()));
+      Parameters.Add(new FixedValueParameter<IntValue>("MaximumPopulationSize", "The maximum size of the population that is evolved.", new IntValue(20)));
+      Parameters.Add(new OptionalValueParameter<IntValue>("MaximumEvaluations", "The maximum number of solution evaluations."));
+      Parameters.Add(new OptionalValueParameter<TimeSpanValue>("MaximumExecutionTime", "The maximum runtime.", new TimeSpanValue(TimeSpan.FromMinutes(10))));
+      Parameters.Add(new OptionalValueParameter<DoubleValue>("TargetQuality", "The target quality at which the algorithm terminates."));
+      Parameters.Add(new FixedValueParameter<BoolValue>("SetSeedRandomly", "Whether each run of the algorithm should be conducted with a new random seed.", new BoolValue(true)));
+      Parameters.Add(new FixedValueParameter<IntValue>("Seed", "The random number seed that is used in case SetSeedRandomly is false.", new IntValue(0)));
+      Parameters.Add(new ConstrainedValueParameter<ISolutionModelTrainer<TPopulationContext>>("SolutionModelTrainer", "The object that creates a solution model that can be sampled."));
+      Parameters.Add(new ConstrainedValueParameter<ILocalSearch<TSolutionContext>>("LocalSearch", "The local search operator to use."));
+
+      qualityAnalyzer = new BestAverageWorstQualityAnalyzer();
+      qualityPerClockAnalyzer = new QualityPerClockAnalyzer();
+      qualityPerEvaluationsAnalyzer = new QualityPerEvaluationsAnalyzer();
+
+      RegisterEventHandlers();
+    }
+
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      RegisterEventHandlers();
+    }
+
+    private void RegisterEventHandlers() {
+      MaximumPopulationSizeParameter.Value.ValueChanged += MaximumPopulationSizeOnChanged;
+    }
+
+    private void MaximumPopulationSizeOnChanged(object sender, EventArgs eventArgs) {
+      if (ExecutionState == ExecutionState.Started || ExecutionState == ExecutionState.Paused)
+        throw new InvalidOperationException("Cannot change maximum population size before algorithm finishes.");
+      Prepare();
+    }
+
+    protected override void OnProblemChanged() {
+      base.OnProblemChanged();
+      qualityAnalyzer.MaximizationParameter.ActualName = Problem.MaximizationParameter.Name;
+      qualityAnalyzer.MaximizationParameter.Hidden = true;
+      qualityAnalyzer.QualityParameter.ActualName = Problem.Evaluator.QualityParameter.ActualName;
+      qualityAnalyzer.QualityParameter.Depth = 1;
+      qualityAnalyzer.QualityParameter.Hidden = true;
+      qualityAnalyzer.BestKnownQualityParameter.ActualName = Problem.BestKnownQualityParameter.Name;
+      qualityAnalyzer.BestKnownQualityParameter.Hidden = true;
+
+      var multiAnalyzer = Analyzer as MultiAnalyzer;
+      if (multiAnalyzer != null) {
+        multiAnalyzer.Operators.Clear();
+        if (Problem != null) {
+          foreach (var analyzer in Problem.Operators.OfType<IAnalyzer>()) {
+            foreach (var param in analyzer.Parameters.OfType<IScopeTreeLookupParameter>())
+              param.Depth = 1;
+            multiAnalyzer.Operators.Add(analyzer, analyzer.EnabledByDefault || analyzer is ISimilarityBasedOperator);
+          }
+        }
+        multiAnalyzer.Operators.Add(qualityAnalyzer, qualityAnalyzer.EnabledByDefault);
+        multiAnalyzer.Operators.Add(qualityPerClockAnalyzer, true);
+        multiAnalyzer.Operators.Add(qualityPerEvaluationsAnalyzer, true);
+      }
+    }
+
+    public override void Prepare() {
+      base.Prepare();
+      Results.Clear();
+      Context = null;
+    }
+
+    protected virtual TPopulationContext CreateContext() {
+      return new TPopulationContext();
+    }
+
+    protected sealed override void Run(CancellationToken token) {
+      if (Context == null) {
+        Context = CreateContext();
+        if (SetSeedRandomly) Seed = new System.Random().Next();
+        Context.Random.Reset(Seed);
+        Context.Scope.Variables.Add(new Variable("Results", Results));
+        Context.Problem = Problem;
+      }
+
+      if (MaximumExecutionTime.HasValue)
+        CancellationTokenSource.CancelAfter(MaximumExecutionTime.Value);
+
+      IExecutionContext context = null;
+      foreach (var item in Problem.ExecutionContextItems)
+        context = new Core.ExecutionContext(context, item, Context.Scope);
+      context = new Core.ExecutionContext(context, this, Context.Scope);
+      Context.Parent = context;
+
+      if (!Context.Initialized) {
+        // We initialize the population with two local optima
+        while (Context.PopulationCount < 2) {
+          var child = Create(token);
+          Context.LocalSearchEvaluations += HillClimb(child, token);
+          Context.LocalOptimaLevel += child.Fitness;
+          Context.AddToPopulation(child);
+          Context.BestQuality = child.Fitness;
+          Analyze(CancellationToken.None);
+          token.ThrowIfCancellationRequested();
+          if (Terminate()) return;
+        }
+        Context.LocalSearchEvaluations /= 2;
+        Context.LocalOptimaLevel /= 2;
+        Context.Initialized = true;
+      }
+
+      while (!Terminate()) {
+        Iterate(token);
+        Analyze(token);
+        token.ThrowIfCancellationRequested();
+      }
+    }
+
+    private void Iterate(CancellationToken token) {
+      var replaced = false;
+      ISingleObjectiveSolutionScope<TSolution> offspring = null;
+      
+      offspring = Breed(token);
+      if (offspring != null) {
+        var replNew = Replace(offspring, token);
+        if (replNew) {
+          replaced = true;
+          Context.ByBreeding++;
+        }
+      }
+
+      offspring = Relink(token);
+      if (offspring != null) {
+        if (Replace(offspring, token)) {
+          replaced = true;
+          Context.ByRelinking++;
+        }
+      }
+
+      offspring = Delink(token);
+      if (offspring != null) {
+        if (Replace(offspring, token)) {
+          replaced = true;
+          Context.ByDelinking++;
+        }
+      }
+
+      offspring = Sample(token);
+      if (offspring != null) {
+        if (Replace(offspring, token)) {
+          replaced = true;
+          Context.BySampling++;
+        }
+      }
+
+      if (!replaced && offspring != null) {
+        if (Context.HillclimbingSuited(offspring.Fitness)) {
+          HillClimb(offspring, token, CalculateSubspace(Context.Population.Select(x => x.Solution)));
+          if (Replace(offspring, token)) {
+            Context.ByHillclimbing++;
+            replaced = true;
+          }
+        }
+      }
+
+      if (!replaced) {
+        var before = Context.Population.SampleRandom(Context.Random);
+        offspring = (ISingleObjectiveSolutionScope<TSolution>)before.Clone();
+        AdaptiveWalk(offspring, Context.LocalSearchEvaluations * 2, token);
+        if (!Eq(before, offspring))
+          Context.AddAdaptivewalkingResult(before, offspring);
+        if (Replace(offspring, token)) {
+          Context.ByAdaptivewalking++;
+          replaced = true;
+        }
+      }
+
+      Context.Iterations++;
+    }
+
+    protected void Analyze(CancellationToken token) {
+      IResult res;
+      if (!Results.TryGetValue("EvaluatedSolutions", out res))
+        Results.Add(new Result("EvaluatedSolutions", new IntValue(Context.EvaluatedSolutions)));
+      else ((IntValue)res.Value).Value = Context.EvaluatedSolutions;
+      if (!Results.TryGetValue("Iterations", out res))
+        Results.Add(new Result("Iterations", new IntValue(Context.Iterations)));
+      else ((IntValue)res.Value).Value = Context.Iterations;
+      if (!Results.TryGetValue("LocalSearch Evaluations", out res))
+        Results.Add(new Result("LocalSearch Evaluations", new IntValue(Context.LocalSearchEvaluations)));
+      else ((IntValue)res.Value).Value = Context.LocalSearchEvaluations;
+      if (!Results.TryGetValue("ByBreeding", out res))
+        Results.Add(new Result("ByBreeding", new IntValue(Context.ByBreeding)));
+      else ((IntValue)res.Value).Value = Context.ByBreeding;
+      if (!Results.TryGetValue("ByRelinking", out res))
+        Results.Add(new Result("ByRelinking", new IntValue(Context.ByRelinking)));
+      else ((IntValue)res.Value).Value = Context.ByRelinking;
+      if (!Results.TryGetValue("ByDelinking", out res))
+        Results.Add(new Result("ByDelinking", new IntValue(Context.ByDelinking)));
+      else ((IntValue)res.Value).Value = Context.ByDelinking;
+      if (!Results.TryGetValue("BySampling", out res))
+        Results.Add(new Result("BySampling", new IntValue(Context.BySampling)));
+      else ((IntValue)res.Value).Value = Context.BySampling;
+      if (!Results.TryGetValue("ByHillclimbing", out res))
+        Results.Add(new Result("ByHillclimbing", new IntValue(Context.ByHillclimbing)));
+      else ((IntValue)res.Value).Value = Context.ByHillclimbing;
+      if (!Results.TryGetValue("ByAdaptivewalking", out res))
+        Results.Add(new Result("ByAdaptivewalking", new IntValue(Context.ByAdaptivewalking)));
+      else ((IntValue)res.Value).Value = Context.ByAdaptivewalking;
+
+      var sp = new ScatterPlot("Breeding Correlation", "");
+      sp.Rows.Add(new ScatterPlotDataRow("Parent1 vs Offspring", "", Context.BreedingStat.Select(x => new Point2D<double>(x.Item1, x.Item4))) { VisualProperties = { PointSize = 6 }});
+      sp.Rows.Add(new ScatterPlotDataRow("Parent2 vs Offspring", "", Context.BreedingStat.Select(x => new Point2D<double>(x.Item2, x.Item4))) { VisualProperties = { PointSize = 6 } });
+      sp.Rows.Add(new ScatterPlotDataRow("Parent Distance vs Offspring", "", Context.BreedingStat.Select(x => new Point2D<double>(x.Item3, x.Item4))) { VisualProperties = { PointSize = 6 } });
+      if (!Results.TryGetValue("BreedingStat", out res)) {
+        Results.Add(new Result("BreedingStat", sp));
+      } else res.Value = sp;
+
+      sp = new ScatterPlot("Relinking Correlation", "");
+      sp.Rows.Add(new ScatterPlotDataRow("A vs Relink", "", Context.RelinkingStat.Select(x => new Point2D<double>(x.Item1, x.Item4))) { VisualProperties = { PointSize = 6 } });
+      sp.Rows.Add(new ScatterPlotDataRow("B vs Relink", "", Context.RelinkingStat.Select(x => new Point2D<double>(x.Item2, x.Item4))) { VisualProperties = { PointSize = 6 } });
+      sp.Rows.Add(new ScatterPlotDataRow("d(A,B) vs Offspring", "", Context.RelinkingStat.Select(x => new Point2D<double>(x.Item3, x.Item4))) { VisualProperties = { PointSize = 6 } });
+      if (!Results.TryGetValue("RelinkingStat", out res)) {
+        Results.Add(new Result("RelinkingStat", sp));
+      } else res.Value = sp;
+
+      sp = new ScatterPlot("Delinking Correlation", "");
+      sp.Rows.Add(new ScatterPlotDataRow("A vs Delink", "", Context.DelinkingStat.Select(x => new Point2D<double>(x.Item1, x.Item4))) { VisualProperties = { PointSize = 6 } });
+      sp.Rows.Add(new ScatterPlotDataRow("B vs Delink", "", Context.DelinkingStat.Select(x => new Point2D<double>(x.Item2, x.Item4))) { VisualProperties = { PointSize = 6 } });
+      sp.Rows.Add(new ScatterPlotDataRow("d(A,B) vs Offspring", "", Context.DelinkingStat.Select(x => new Point2D<double>(x.Item3, x.Item4))) { VisualProperties = { PointSize = 6 } });
+      if (!Results.TryGetValue("DelinkingStat", out res)) {
+        Results.Add(new Result("DelinkingStat", sp));
+      } else res.Value = sp;
+
+      sp = new ScatterPlot("Sampling Correlation", "");
+      sp.Rows.Add(new ScatterPlotDataRow("AvgFitness vs Sample", "", Context.SamplingStat.Select(x => new Point2D<double>(x.Item1, x.Item2))) { VisualProperties = { PointSize = 6 } });
+      if (!Results.TryGetValue("SampleStat", out res)) {
+        Results.Add(new Result("SampleStat", sp));
+      } else res.Value = sp;
+
+      sp = new ScatterPlot("Hillclimbing Correlation", "");
+      sp.Rows.Add(new ScatterPlotDataRow("Start vs Improvement", "", Context.HillclimbingStat.Select(x => new Point2D<double>(x.Item1, x.Item2))) { VisualProperties = { PointSize = 6 } });
+      if (!Results.TryGetValue("HillclimbingStat", out res)) {
+        Results.Add(new Result("HillclimbingStat", sp));
+      } else res.Value = sp;
+
+      sp = new ScatterPlot("Adaptivewalking Correlation", "");
+      sp.Rows.Add(new ScatterPlotDataRow("Start vs Best", "", Context.AdaptivewalkingStat.Select(x => new Point2D<double>(x.Item1, x.Item2))) { VisualProperties = { PointSize = 6 } });
+      if (!Results.TryGetValue("AdaptivewalkingStat", out res)) {
+        Results.Add(new Result("AdaptivewalkingStat", sp));
+      } else res.Value = sp;
+
+      Context.RunOperator(Analyzer, Context.Scope, token);
+    }
+
+    protected bool Replace(ISingleObjectiveSolutionScope<TSolution> child, CancellationToken token) {
+      if (double.IsNaN(child.Fitness)) {
+        Context.Evaluate(child, token);
+        Context.IncrementEvaluatedSolutions(1);
+      }
+      if (Context.IsBetter(child.Fitness, Context.BestQuality)) {
+        Context.BestQuality = child.Fitness;
+        Context.BestSolution = (TSolution)child.Solution.Clone();
+      }
+
+      var popSize = MaximumPopulationSize;
+      if (Context.Population.All(p => !Eq(p, child))) {
+
+        if (Context.PopulationCount < popSize) {
+          Context.AddToPopulation(child);
+          return true;// Context.PopulationCount - 1;
+        }
+        
+        // The set of replacement candidates consists of all solutions at least as good as the new one
+        var candidates = Context.Population.Select((p, i) => new { Index = i, Individual = p })
+                                         .Where(x => x.Individual.Fitness == child.Fitness
+                                           || Context.IsBetter(child, x.Individual)).ToList();
+        if (candidates.Count == 0) return false;// -1;
+
+        var repCand = -1;
+        var avgChildDist = 0.0;
+        var minChildDist = double.MaxValue;
+        var plateau = new List<int>();
+        var worstPlateau = -1;
+        var minAvgPlateauDist = double.MaxValue;
+        var minPlateauDist = double.MaxValue;
+        // If there are equally good solutions it is first tried to replace one of those
+        // The criteria for replacement is that the new solution has better average distance
+        // to all other solutions at this "plateau"
+        foreach (var c in candidates.Where(x => x.Individual.Fitness == child.Fitness)) {
+          var dist = Dist(c.Individual, child);
+          avgChildDist += dist;
+          if (dist < minChildDist) minChildDist = dist;
+          plateau.Add(c.Index);
+        }
+        if (plateau.Count > 2) {
+          avgChildDist /= plateau.Count;
+          foreach (var p in plateau) {
+            var avgDist = 0.0;
+            var minDist = double.MaxValue;
+            foreach (var q in plateau) {
+              if (p == q) continue;
+              var dist = Dist(Context.AtPopulation(p), Context.AtPopulation(q));
+              avgDist += dist;
+              if (dist < minDist) minDist = dist;
+            }
+
+            var d = Dist(Context.AtPopulation(p), child);
+            avgDist += d;
+            avgDist /= plateau.Count;
+            if (d < minDist) minDist = d;
+
+            if (minDist < minPlateauDist || (minDist == minPlateauDist && avgDist < avgChildDist)) {
+              minAvgPlateauDist = avgDist;
+              minPlateauDist = minDist;
+              worstPlateau = p;
+            }
+          }
+          if (minPlateauDist < minChildDist || (minPlateauDist == minChildDist && minAvgPlateauDist < avgChildDist))
+            repCand = worstPlateau;
+        }
+
+        if (repCand < 0) {
+          // If no solution at the same plateau were identified for replacement
+          // a worse solution with smallest distance is chosen
+          var minDist = double.MaxValue;
+          foreach (var c in candidates.Where(x => Context.IsBetter(child, x.Individual))) {
+            var d = Dist(c.Individual, child);
+            if (d < minDist) {
+              minDist = d;
+              repCand = c.Index;
+            }
+          }
+        }
+
+        // If no replacement was identified, this can only mean that there are
+        // no worse solutions and those on the same plateau are all better
+        // stretched out than the new one
+        if (repCand < 0) return false;// -1;
+        
+        Context.ReplaceAtPopulation(repCand, child);
+        return true;// repCand;
+      }
+      return false;// -1;
+    }
+
+    protected bool Eq(ISingleObjectiveSolutionScope<TSolution> a, ISingleObjectiveSolutionScope<TSolution> b) {
+      return Eq(a.Solution, b.Solution);
+    }
+    protected abstract bool Eq(TSolution a, TSolution b);
+    protected abstract double Dist(ISingleObjectiveSolutionScope<TSolution> a, ISingleObjectiveSolutionScope<TSolution> b);
+    protected abstract ISolutionSubspace<TSolution> CalculateSubspace(IEnumerable<TSolution> solutions, bool inverse = false);
+
+    #region Create
+    protected virtual ISingleObjectiveSolutionScope<TSolution> Create(CancellationToken token) {
+      var child = Context.ToScope(null);
+      Context.RunOperator(Problem.SolutionCreator, child, token);
+      return child;
+    }
+    #endregion
+
+    #region Improve
+    protected virtual int HillClimb(ISingleObjectiveSolutionScope<TSolution> scope, CancellationToken token, ISolutionSubspace<TSolution> subspace = null) {
+      if (double.IsNaN(scope.Fitness)) {
+        Context.Evaluate(scope, token);
+        Context.IncrementEvaluatedSolutions(1);
+      }
+      var before = (ISingleObjectiveSolutionScope<TSolution>)scope.Clone();
+      var lscontext = Context.CreateSingleSolutionContext(scope);
+      LocalSearchParameter.Value.Optimize(lscontext);
+      Context.AddHillclimbingResult(before, scope);
+      Context.IncrementEvaluatedSolutions(lscontext.EvaluatedSolutions);
+      return lscontext.EvaluatedSolutions;
+    }
+
+    protected virtual void AdaptiveClimb(ISingleObjectiveSolutionScope<TSolution> scope, int maxEvals, CancellationToken token, ISolutionSubspace<TSolution> subspace = null) {
+      if (double.IsNaN(scope.Fitness)) {
+        Context.Evaluate(scope, token);
+        Context.IncrementEvaluatedSolutions(1);
+      }
+      var newScope = (ISingleObjectiveSolutionScope<TSolution>)scope.Clone();
+      AdaptiveWalk(newScope, maxEvals, token, subspace);
+      
+      Context.AddAdaptivewalkingResult(scope, newScope);
+      if (Context.IsBetter(newScope, scope)) {
+        scope.Adopt(newScope);
+      }
+    }
+    protected abstract void AdaptiveWalk(ISingleObjectiveSolutionScope<TSolution> scope, int maxEvals, CancellationToken token, ISolutionSubspace<TSolution> subspace = null);
+    
+    #endregion
+
+    #region Breed
+    protected virtual ISingleObjectiveSolutionScope<TSolution> Breed(CancellationToken token) {
+      var i1 = Context.Random.Next(Context.PopulationCount);
+      var i2 = Context.Random.Next(Context.PopulationCount);
+      while (i1 == i2) i2 = Context.Random.Next(Context.PopulationCount);
+
+      var p1 = Context.AtPopulation(i1);
+      var p2 = Context.AtPopulation(i2);
+
+      if (double.IsNaN(p1.Fitness)) {
+        Context.Evaluate(p1, token);
+        Context.IncrementEvaluatedSolutions(1);
+      }
+      if (double.IsNaN(p2.Fitness)) {
+        Context.Evaluate(p2, token);
+        Context.IncrementEvaluatedSolutions(1);
+      }
+
+      if (!Context.BreedingSuited(p1, p2, Dist(p1, p2))) return null;
+
+      var offspring = Breed(p1, p2, token);
+
+      if (double.IsNaN(offspring.Fitness)) {
+        Context.Evaluate(offspring, token);
+        Context.IncrementEvaluatedSolutions(1);
+      }
+
+      Context.AddBreedingResult(p1, p2, Dist(p1, p2), offspring);
+
+      // new best solutions are improved using hill climbing in full solution space
+      if (Context.Population.All(p => Context.IsBetter(offspring, p)))
+        HillClimb(offspring, token);
+      else if (!Eq(offspring, p1) && !Eq(offspring, p2) && Context.HillclimbingSuited(offspring.Fitness))
+        HillClimb(offspring, token, CalculateSubspace(new[] { p1.Solution, p2.Solution }, inverse: false));
+
+      return offspring;
+    }
+
+    protected abstract ISingleObjectiveSolutionScope<TSolution> Breed(ISingleObjectiveSolutionScope<TSolution> p1, ISingleObjectiveSolutionScope<TSolution> p2, CancellationToken token);
+    #endregion
+
+    #region Relink/Delink
+    protected virtual ISingleObjectiveSolutionScope<TSolution> Relink(CancellationToken token) {
+      var i1 = Context.Random.Next(Context.PopulationCount);
+      var i2 = Context.Random.Next(Context.PopulationCount);
+      while (i1 == i2) i2 = Context.Random.Next(Context.PopulationCount);
+
+      var p1 = Context.AtPopulation(i1);
+      var p2 = Context.AtPopulation(i2);
+
+      if (!Context.RelinkSuited(p1, p2, Dist(p1, p2))) return null;
+
+      var link = PerformRelinking(p1, p2, token, delink: false);
+
+      // new best solutions are improved using hill climbing in full solution space
+      if (Context.Population.All(p => Context.IsBetter(link, p)))
+        HillClimb(link, token);
+      else if (!Eq(link, p1) && !Eq(link, p2) && Context.HillclimbingSuited(link.Fitness))
+        HillClimb(link, token, CalculateSubspace(new[] { p1.Solution, p2.Solution }, inverse: true));
+
+      return link;
+    }
+
+    protected virtual ISingleObjectiveSolutionScope<TSolution> Delink(CancellationToken token) {
+      var i1 = Context.Random.Next(Context.PopulationCount);
+      var i2 = Context.Random.Next(Context.PopulationCount);
+      while (i1 == i2) i2 = Context.Random.Next(Context.PopulationCount);
+
+      var p1 = Context.AtPopulation(i1);
+      var p2 = Context.AtPopulation(i2);
+      
+      if (!Context.DelinkSuited(p1, p2, Dist(p1, p2))) return null;
+
+      var link = PerformRelinking(p1, p2, token, delink: true);
+
+      // new best solutions are improved using hill climbing in full solution space
+      if (Context.Population.All(p => Context.IsBetter(link, p)))
+        HillClimb(link, token);
+      // intentionally not making hill climbing otherwise after delinking in sub-space
+      return link;
+    }
+
+    protected virtual ISingleObjectiveSolutionScope<TSolution> PerformRelinking(ISingleObjectiveSolutionScope<TSolution> a, ISingleObjectiveSolutionScope<TSolution> b, CancellationToken token, bool delink = false) {
+      var relink = Link(a, b, token, delink);
+
+      if (double.IsNaN(relink.Fitness)) {
+        Context.Evaluate(relink, token);
+        Context.IncrementEvaluatedSolutions(1);
+      }
+
+      if (delink) {
+        Context.AddDelinkingResult(a, b, Dist(a, b), relink);
+      } else {
+        Context.AddRelinkingResult(a, b, Dist(a, b), relink);
+      }
+
+      return relink;
+    }
+
+    protected abstract ISingleObjectiveSolutionScope<TSolution> Link(ISingleObjectiveSolutionScope<TSolution> a, ISingleObjectiveSolutionScope<TSolution> b, CancellationToken token, bool delink = false);
+    #endregion
+
+    #region Sample
+    protected virtual ISingleObjectiveSolutionScope<TSolution> Sample(CancellationToken token) {
+      if (Context.PopulationCount == MaximumPopulationSize) {
+        SolutionModelTrainerParameter.Value.TrainModel(Context);
+        ISingleObjectiveSolutionScope<TSolution> bestSample = null;
+        var tries = 1;
+        var avgDist = (from a in Context.Population.Shuffle(Context.Random)
+                       from b in Context.Population.Shuffle(Context.Random)
+                       select Dist(a, b)).Average();
+        for (; tries < 100; tries++) {
+          var sample = Context.ToScope(Context.Model.Sample());
+          Context.Evaluate(sample, token);
+          if (bestSample == null || Context.IsBetter(sample, bestSample)) {
+            bestSample = sample;
+            if (Context.Population.Any(x => !Context.IsBetter(x, bestSample))) break;
+          }
+          if (!Context.SamplingSuited(avgDist)) break;
+        }
+        Context.IncrementEvaluatedSolutions(tries);
+        Context.AddSamplingResult(bestSample, avgDist);
+        return bestSample;
+      }
+      return null;
+    }
+    #endregion
+
+    protected virtual bool Terminate() {
+      var maximization = ((IValueParameter<BoolValue>)Problem.MaximizationParameter).Value.Value;
+      return MaximumEvaluations.HasValue && Context.EvaluatedSolutions >= MaximumEvaluations.Value
+        || MaximumExecutionTime.HasValue && ExecutionTime >= MaximumExecutionTime.Value
+        || TargetQuality.HasValue && (maximization && Context.BestQuality >= TargetQuality.Value
+                                  || !maximization && Context.BestQuality <= TargetQuality.Value);
+    }
+
+    public event PropertyChangedEventHandler PropertyChanged;
+    protected void OnPropertyChanged(string property) {
+      var handler = PropertyChanged;
+      if (handler != null) handler(this, new PropertyChangedEventArgs(property));
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/MemPRContext.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/MemPRContext.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/MemPRContext.cs	(revision 14666)
@@ -0,0 +1,538 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using HeuristicLab.Algorithms.DataAnalysis;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Analysis;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Problems.DataAnalysis;
+using HeuristicLab.Random;
+using ExecutionContext = HeuristicLab.Core.ExecutionContext;
+
+namespace HeuristicLab.Algorithms.MemPR {
+  [Item("MemPRContext", "Abstract base class for MemPR contexts.")]
+  [StorableClass]
+  public abstract class MemPRPopulationContext<TProblem, TSolution, TPopulationContext, TSolutionContext> : ParameterizedNamedItem,
+    IPopulationBasedHeuristicAlgorithmContext<TProblem, TSolution>, ISolutionModelContext<TSolution>, IEvaluationServiceContext<TSolution>
+      where TProblem : class, IItem, ISingleObjectiveHeuristicOptimizationProblem
+      where TSolution : class, IItem
+      where TPopulationContext : MemPRPopulationContext<TProblem, TSolution, TPopulationContext, TSolutionContext>
+      where TSolutionContext : MemPRSolutionContext<TProblem, TSolution, TPopulationContext, TSolutionContext> {
+
+    private IExecutionContext parent;
+    public IExecutionContext Parent {
+      get { return parent; }
+      set { parent = value; }
+    }
+
+    [Storable]
+    private IScope scope;
+    public IScope Scope {
+      get { return scope; }
+      private set { scope = value; }
+    }
+
+    IKeyedItemCollection<string, IParameter> IExecutionContext.Parameters {
+      get { return Parameters; }
+    }
+
+    [Storable]
+    private IValueParameter<TProblem> problem;
+    public TProblem Problem {
+      get { return problem.Value; }
+      set { problem.Value = value; }
+    }
+    public bool Maximization {
+      get { return ((IValueParameter<BoolValue>)Problem.MaximizationParameter).Value.Value; }
+    }
+
+    [Storable]
+    private IValueParameter<BoolValue> initialized;
+    public bool Initialized {
+      get { return initialized.Value.Value; }
+      set { initialized.Value.Value = value; }
+    }
+
+    [Storable]
+    private IValueParameter<IntValue> iterations;
+    public int Iterations {
+      get { return iterations.Value.Value; }
+      set { iterations.Value.Value = value; }
+    }
+
+    [Storable]
+    private IValueParameter<IntValue> evaluatedSolutions;
+    public int EvaluatedSolutions {
+      get { return evaluatedSolutions.Value.Value; }
+      set { evaluatedSolutions.Value.Value = value; }
+    }
+
+    [Storable]
+    private IValueParameter<DoubleValue> bestQuality;
+    public double BestQuality {
+      get { return bestQuality.Value.Value; }
+      set { bestQuality.Value.Value = value; }
+    }
+
+    [Storable]
+    private IValueParameter<TSolution> bestSolution;
+    public TSolution BestSolution {
+      get { return bestSolution.Value; }
+      set { bestSolution.Value = value; }
+    }
+
+    [Storable]
+    private IValueParameter<IntValue> localSearchEvaluations;
+    public int LocalSearchEvaluations {
+      get { return localSearchEvaluations.Value.Value; }
+      set { localSearchEvaluations.Value.Value = value; }
+    }
+
+    [Storable]
+    private IValueParameter<DoubleValue> localOptimaLevel;
+    public double LocalOptimaLevel {
+      get { return localOptimaLevel.Value.Value; }
+      set { localOptimaLevel.Value.Value = value; }
+    }
+
+    [Storable]
+    private IValueParameter<IntValue> byBreeding;
+    public int ByBreeding {
+      get { return byBreeding.Value.Value; }
+      set { byBreeding.Value.Value = value; }
+    }
+
+    [Storable]
+    private IValueParameter<IntValue> byRelinking;
+    public int ByRelinking {
+      get { return byRelinking.Value.Value; }
+      set { byRelinking.Value.Value = value; }
+    }
+
+    [Storable]
+    private IValueParameter<IntValue> byDelinking;
+    public int ByDelinking {
+      get { return byDelinking.Value.Value; }
+      set { byDelinking.Value.Value = value; }
+    }
+
+    [Storable]
+    private IValueParameter<IntValue> bySampling;
+    public int BySampling {
+      get { return bySampling.Value.Value; }
+      set { bySampling.Value.Value = value; }
+    }
+
+    [Storable]
+    private IValueParameter<IntValue> byHillclimbing;
+    public int ByHillclimbing {
+      get { return byHillclimbing.Value.Value; }
+      set { byHillclimbing.Value.Value = value; }
+    }
+
+    [Storable]
+    private IValueParameter<IntValue> byAdaptivewalking;
+    public int ByAdaptivewalking {
+      get { return byAdaptivewalking.Value.Value; }
+      set { byAdaptivewalking.Value.Value = value; }
+    }
+
+    [Storable]
+    private IValueParameter<IRandom> random;
+    public IRandom Random {
+      get { return random.Value; }
+      set { random.Value = value; }
+    }
+    
+    public IEnumerable<ISingleObjectiveSolutionScope<TSolution>> Population {
+      get { return scope.SubScopes.OfType<ISingleObjectiveSolutionScope<TSolution>>(); }
+    }
+    public void AddToPopulation(ISingleObjectiveSolutionScope<TSolution> solScope) {
+      scope.SubScopes.Add(solScope);
+    }
+    public void ReplaceAtPopulation(int index, ISingleObjectiveSolutionScope<TSolution> solScope) {
+      scope.SubScopes[index] = solScope;
+    }
+    public ISingleObjectiveSolutionScope<TSolution> AtPopulation(int index) {
+      return scope.SubScopes[index] as ISingleObjectiveSolutionScope<TSolution>;
+    }
+    public void SortPopulation() {
+      scope.SubScopes.Replace(scope.SubScopes.OfType<ISingleObjectiveSolutionScope<TSolution>>().OrderBy(x => Maximization ? -x.Fitness : x.Fitness).ToList());
+    }
+    public int PopulationCount {
+      get { return scope.SubScopes.Count; }
+    }
+    
+    [Storable]
+    private List<Tuple<double, double, double, double>> breedingStat;
+    public IEnumerable<Tuple<double, double, double, double>> BreedingStat {
+      get { return breedingStat; }
+    }
+    [Storable]
+    private List<Tuple<double, double, double, double>> relinkingStat;
+    public IEnumerable<Tuple<double, double, double, double>> RelinkingStat {
+      get { return relinkingStat; }
+    }
+    [Storable]
+    private List<Tuple<double, double, double, double>> delinkingStat;
+    public IEnumerable<Tuple<double, double, double, double>> DelinkingStat {
+      get { return delinkingStat; }
+    }
+    [Storable]
+    private List<Tuple<double, double>> samplingStat;
+    public IEnumerable<Tuple<double, double>> SamplingStat {
+      get { return samplingStat; }
+    }
+    [Storable]
+    private List<Tuple<double, double>> hillclimbingStat;
+    public IEnumerable<Tuple<double, double>> HillclimbingStat {
+      get { return hillclimbingStat; }
+    }
+    [Storable]
+    private List<Tuple<double, double>> adaptivewalkingStat;
+    public IEnumerable<Tuple<double, double>> AdaptivewalkingStat {
+      get { return adaptivewalkingStat; }
+    }
+
+    [Storable]
+    public ISolutionModel<TSolution> Model { get; set; }
+
+    [StorableConstructor]
+    protected MemPRPopulationContext(bool deserializing) : base(deserializing) { }
+    protected MemPRPopulationContext(MemPRPopulationContext<TProblem, TSolution, TPopulationContext, TSolutionContext> original, Cloner cloner)
+      : base(original, cloner) {
+      scope = cloner.Clone(original.scope);
+      problem = cloner.Clone(original.problem);
+      initialized = cloner.Clone(original.initialized);
+      iterations = cloner.Clone(original.iterations);
+      evaluatedSolutions = cloner.Clone(original.evaluatedSolutions);
+      bestQuality = cloner.Clone(original.bestQuality);
+      bestSolution = cloner.Clone(original.bestSolution);
+      localSearchEvaluations = cloner.Clone(original.localSearchEvaluations);
+      localOptimaLevel = cloner.Clone(original.localOptimaLevel);
+      byBreeding = cloner.Clone(original.byBreeding);
+      byRelinking = cloner.Clone(original.byRelinking);
+      byDelinking = cloner.Clone(original.byDelinking);
+      bySampling = cloner.Clone(original.bySampling);
+      byHillclimbing = cloner.Clone(original.byHillclimbing);
+      byAdaptivewalking = cloner.Clone(original.byAdaptivewalking);
+      random = cloner.Clone(original.random);
+      breedingStat = original.breedingStat.Select(x => Tuple.Create(x.Item1, x.Item2, x.Item3, x.Item4)).ToList();
+      relinkingStat = original.relinkingStat.Select(x => Tuple.Create(x.Item1, x.Item2, x.Item3, x.Item4)).ToList();
+      delinkingStat = original.delinkingStat.Select(x => Tuple.Create(x.Item1, x.Item2, x.Item3, x.Item4)).ToList();
+      samplingStat = original.samplingStat.Select(x => Tuple.Create(x.Item1, x.Item2)).ToList();
+      hillclimbingStat = original.hillclimbingStat.Select(x => Tuple.Create(x.Item1, x.Item2)).ToList();
+      adaptivewalkingStat = original.adaptivewalkingStat.Select(x => Tuple.Create(x.Item1, x.Item2)).ToList();
+      
+      Model = cloner.Clone(original.Model);
+    }
+    public MemPRPopulationContext() : this("MemPRContext") { }
+    public MemPRPopulationContext(string name) : base(name) {
+      scope = new Scope("Global");
+
+      Parameters.Add(problem = new ValueParameter<TProblem>("Problem"));
+      Parameters.Add(initialized = new ValueParameter<BoolValue>("Initialized", new BoolValue(false)));
+      Parameters.Add(iterations = new ValueParameter<IntValue>("Iterations", new IntValue(0)));
+      Parameters.Add(evaluatedSolutions = new ValueParameter<IntValue>("EvaluatedSolutions", new IntValue(0)));
+      Parameters.Add(bestQuality = new ValueParameter<DoubleValue>("BestQuality", new DoubleValue(double.NaN)));
+      Parameters.Add(bestSolution = new ValueParameter<TSolution>("BestFoundSolution"));
+      Parameters.Add(localSearchEvaluations = new ValueParameter<IntValue>("LocalSearchEvaluations", new IntValue(0)));
+      Parameters.Add(localOptimaLevel = new ValueParameter<DoubleValue>("LocalOptimaLevel", new DoubleValue(0)));
+      Parameters.Add(byBreeding = new ValueParameter<IntValue>("ByBreeding", new IntValue(0)));
+      Parameters.Add(byRelinking = new ValueParameter<IntValue>("ByRelinking", new IntValue(0)));
+      Parameters.Add(byDelinking = new ValueParameter<IntValue>("ByDelinking", new IntValue(0)));
+      Parameters.Add(bySampling = new ValueParameter<IntValue>("BySampling", new IntValue(0)));
+      Parameters.Add(byHillclimbing = new ValueParameter<IntValue>("ByHillclimbing", new IntValue(0)));
+      Parameters.Add(byAdaptivewalking = new ValueParameter<IntValue>("ByAdaptivewalking", new IntValue(0)));
+      Parameters.Add(random = new ValueParameter<IRandom>("Random", new MersenneTwister()));
+
+      breedingStat = new List<Tuple<double, double, double, double>>();
+      relinkingStat = new List<Tuple<double, double, double, double>>();
+      delinkingStat = new List<Tuple<double, double, double, double>>();
+      samplingStat = new List<Tuple<double, double>>();
+      hillclimbingStat = new List<Tuple<double, double>>();
+      adaptivewalkingStat = new List<Tuple<double, double>>();
+    }
+
+    public abstract ISingleObjectiveSolutionScope<TSolution> ToScope(TSolution code, double fitness = double.NaN);
+
+    public virtual double Evaluate(TSolution solution, CancellationToken token) {
+      var solScope = ToScope(solution);
+      Evaluate(solScope, token);
+      return solScope.Fitness;
+    }
+
+    public virtual void Evaluate(ISingleObjectiveSolutionScope<TSolution> solScope, CancellationToken token) {
+      var pdef = Problem as ISingleObjectiveProblemDefinition;
+      if (pdef != null) {
+        var ind = new SingleEncodingIndividual(pdef.Encoding, solScope);
+        solScope.Fitness = pdef.Evaluate(ind, Random);
+      } else {
+        RunOperator(Problem.Evaluator, solScope, token);
+      }
+    }
+
+    public abstract TSolutionContext CreateSingleSolutionContext(ISingleObjectiveSolutionScope<TSolution> solution);
+
+    public void IncrementEvaluatedSolutions(int byEvaluations) {
+      if (byEvaluations < 0) throw new ArgumentException("Can only increment and not decrement evaluated solutions.");
+      EvaluatedSolutions += byEvaluations;
+    }
+
+    #region Breeding Performance
+    public void AddBreedingResult(ISingleObjectiveSolutionScope<TSolution> a, ISingleObjectiveSolutionScope<TSolution> b, double parentDist, ISingleObjectiveSolutionScope<TSolution> child) {
+      if (IsBetter(a, b))
+        breedingStat.Add(Tuple.Create(a.Fitness, b.Fitness, parentDist, child.Fitness));
+      else breedingStat.Add(Tuple.Create(b.Fitness, a.Fitness, parentDist, child.Fitness));
+    }
+    public bool BreedingSuited(ISingleObjectiveSolutionScope<TSolution> p1, ISingleObjectiveSolutionScope<TSolution> p2, double dist) {
+      return true;
+    }
+    #endregion
+
+    #region Relinking Performance
+    public void AddRelinkingResult(ISingleObjectiveSolutionScope<TSolution> a, ISingleObjectiveSolutionScope<TSolution> b, double parentDist, ISingleObjectiveSolutionScope<TSolution> child) {
+      if (IsBetter(a, b))
+        relinkingStat.Add(Tuple.Create(a.Fitness, b.Fitness, parentDist, Maximization ? child.Fitness - a.Fitness : a.Fitness - child.Fitness));
+      else relinkingStat.Add(Tuple.Create(a.Fitness, b.Fitness, parentDist, Maximization ? child.Fitness - b.Fitness : b.Fitness - child.Fitness));
+    }
+    public bool RelinkSuited(ISingleObjectiveSolutionScope<TSolution> p1, ISingleObjectiveSolutionScope<TSolution> p2, double dist) {
+      return true;
+    }
+    #endregion
+
+    #region Delinking Performance
+    public void AddDelinkingResult(ISingleObjectiveSolutionScope<TSolution> a, ISingleObjectiveSolutionScope<TSolution> b, double parentDist, ISingleObjectiveSolutionScope<TSolution> child) {
+      if (IsBetter(a, b))
+        delinkingStat.Add(Tuple.Create(a.Fitness, b.Fitness, parentDist, Maximization ? child.Fitness - a.Fitness : a.Fitness - child.Fitness));
+      else delinkingStat.Add(Tuple.Create(a.Fitness, b.Fitness, parentDist, Maximization ? child.Fitness - b.Fitness : b.Fitness - child.Fitness));
+    }
+    public bool DelinkSuited(ISingleObjectiveSolutionScope<TSolution> p1, ISingleObjectiveSolutionScope<TSolution> p2, double dist) {
+      return true;
+    }
+    #endregion
+
+    #region Sampling Performance
+    public void AddSamplingResult(ISingleObjectiveSolutionScope<TSolution> sample, double avgDist) {
+      samplingStat.Add(Tuple.Create(avgDist, sample.Fitness));
+    }
+    public bool SamplingSuited(double avgDist) {
+      return true;
+    }
+    #endregion
+
+    #region Hillclimbing Performance
+    public void AddHillclimbingResult(ISingleObjectiveSolutionScope<TSolution> input, ISingleObjectiveSolutionScope<TSolution> outcome) {
+      hillclimbingStat.Add(Tuple.Create(input.Fitness, Maximization ? outcome.Fitness - input.Fitness : input.Fitness - outcome.Fitness));
+    }
+    public bool HillclimbingSuited(double startingFitness) {
+      return true;
+    }
+    #endregion
+
+    #region Adaptivewalking Performance
+    public void AddAdaptivewalkingResult(ISingleObjectiveSolutionScope<TSolution> input, ISingleObjectiveSolutionScope<TSolution> outcome) {
+      adaptivewalkingStat.Add(Tuple.Create(input.Fitness, Maximization ? outcome.Fitness - input.Fitness : input.Fitness - outcome.Fitness));
+    }
+    public bool AdaptivewalkingSuited(double startingFitness) {
+      return true;
+    }
+    #endregion
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public bool IsBetter(ISingleObjectiveSolutionScope<TSolution> a, ISingleObjectiveSolutionScope<TSolution> b) {
+      return IsBetter(a.Fitness, b.Fitness);
+    }
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public bool IsBetter(double a, double b) {
+      return double.IsNaN(b) && !double.IsNaN(a)
+        || Maximization && a > b
+        || !Maximization && a < b;
+    }
+
+    #region IExecutionContext members
+    public IAtomicOperation CreateOperation(IOperator op) {
+      return new ExecutionContext(this, op, Scope);
+    }
+
+    public IAtomicOperation CreateOperation(IOperator op, IScope s) {
+      return new ExecutionContext(this, op, s);
+    }
+
+    public IAtomicOperation CreateChildOperation(IOperator op) {
+      return new ExecutionContext(this, op, Scope);
+    }
+
+    public IAtomicOperation CreateChildOperation(IOperator op, IScope s) {
+      return new ExecutionContext(this, op, s);
+    }
+    #endregion
+
+    #region Engine Helper
+    public void RunOperator(IOperator op, IScope scope, CancellationToken cancellationToken) {
+      var stack = new Stack<IOperation>();
+      stack.Push(CreateChildOperation(op, scope));
+
+      while (stack.Count > 0) {
+        cancellationToken.ThrowIfCancellationRequested();
+
+        var next = stack.Pop();
+        if (next is OperationCollection) {
+          var coll = (OperationCollection)next;
+          for (int i = coll.Count - 1; i >= 0; i--)
+            if (coll[i] != null) stack.Push(coll[i]);
+        } else if (next is IAtomicOperation) {
+          var operation = (IAtomicOperation)next;
+          try {
+            next = operation.Operator.Execute((IExecutionContext)operation, cancellationToken);
+          } catch (Exception ex) {
+            stack.Push(operation);
+            if (ex is OperationCanceledException) throw ex;
+            else throw new OperatorExecutionException(operation.Operator, ex);
+          }
+          if (next != null) stack.Push(next);
+        }
+      }
+    }
+    #endregion
+  }
+
+  [Item("SingleSolutionMemPRContext", "Abstract base class for single solution MemPR contexts.")]
+  [StorableClass]
+  public abstract class MemPRSolutionContext<TProblem, TSolution, TContext, TSolutionContext> : ParameterizedNamedItem,
+    ISingleSolutionHeuristicAlgorithmContext<TProblem, TSolution>, IEvaluationServiceContext<TSolution>
+      where TProblem : class, IItem, ISingleObjectiveHeuristicOptimizationProblem
+      where TSolution : class, IItem
+      where TContext : MemPRPopulationContext<TProblem, TSolution, TContext, TSolutionContext>
+      where TSolutionContext : MemPRSolutionContext<TProblem, TSolution, TContext, TSolutionContext> {
+
+    private TContext parent;
+    public IExecutionContext Parent {
+      get { return parent; }
+      set { throw new InvalidOperationException("Cannot set the parent of a single-solution context."); }
+    }
+
+    [Storable]
+    private ISingleObjectiveSolutionScope<TSolution> scope;
+    public IScope Scope {
+      get { return scope; }
+    }
+
+    IKeyedItemCollection<string, IParameter> IExecutionContext.Parameters {
+      get { return Parameters; }
+    }
+
+    public TProblem Problem {
+      get { return parent.Problem; }
+    }
+    public bool Maximization {
+      get { return parent.Maximization; }
+    }
+
+    public double BestQuality {
+      get { return parent.BestQuality; }
+      set { parent.BestQuality = value; }
+    }
+
+    public TSolution BestSolution {
+      get { return parent.BestSolution; }
+      set { parent.BestSolution = value; }
+    }
+
+    public IRandom Random {
+      get { return parent.Random; }
+    }
+
+    [Storable]
+    private IValueParameter<IntValue> evaluatedSolutions;
+    public int EvaluatedSolutions {
+      get { return evaluatedSolutions.Value.Value; }
+      set { evaluatedSolutions.Value.Value = value; }
+    }
+
+    [Storable]
+    private IValueParameter<IntValue> iterations;
+    public int Iterations {
+      get { return iterations.Value.Value; }
+      set { iterations.Value.Value = value; }
+    }
+
+    ISingleObjectiveSolutionScope<TSolution> ISingleSolutionHeuristicAlgorithmContext<TProblem, TSolution>.Solution {
+      get { return scope; }
+    }
+
+    [StorableConstructor]
+    protected MemPRSolutionContext(bool deserializing) : base(deserializing) { }
+    protected MemPRSolutionContext(MemPRSolutionContext<TProblem, TSolution, TContext, TSolutionContext> original, Cloner cloner)
+      : base(original, cloner) {
+      scope = cloner.Clone(original.scope);
+      evaluatedSolutions = cloner.Clone(original.evaluatedSolutions);
+      iterations = cloner.Clone(original.iterations);
+    }
+    public MemPRSolutionContext(TContext baseContext, ISingleObjectiveSolutionScope<TSolution> solution) {
+      parent = baseContext;
+      scope = solution;
+      
+      Parameters.Add(evaluatedSolutions = new ValueParameter<IntValue>("EvaluatedSolutions", new IntValue(0)));
+      Parameters.Add(iterations = new ValueParameter<IntValue>("Iterations", new IntValue(0)));
+    }
+
+    public void IncrementEvaluatedSolutions(int byEvaluations) {
+      if (byEvaluations < 0) throw new ArgumentException("Can only increment and not decrement evaluated solutions.");
+      EvaluatedSolutions += byEvaluations;
+    }
+    public virtual double Evaluate(TSolution solution, CancellationToken token) {
+      return parent.Evaluate(solution, token);
+    }
+
+    public virtual void Evaluate(ISingleObjectiveSolutionScope<TSolution> solScope, CancellationToken token) {
+      parent.Evaluate(solScope, token);
+    }
+
+    #region IExecutionContext members
+    public IAtomicOperation CreateOperation(IOperator op) {
+      return new ExecutionContext(this, op, Scope);
+    }
+
+    public IAtomicOperation CreateOperation(IOperator op, IScope s) {
+      return new ExecutionContext(this, op, s);
+    }
+
+    public IAtomicOperation CreateChildOperation(IOperator op) {
+      return new ExecutionContext(this, op, Scope);
+    }
+
+    public IAtomicOperation CreateChildOperation(IOperator op, IScope s) {
+      return new ExecutionContext(this, op, s);
+    }
+    #endregion
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/LocalSearch/ExhaustiveHillClimb.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/LocalSearch/ExhaustiveHillClimb.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/LocalSearch/ExhaustiveHillClimb.cs	(revision 14666)
@@ -0,0 +1,62 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Threading;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Algorithms.MemPR.Util;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.PermutationEncoding;
+using HeuristicLab.Optimization;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Algorithms.MemPR.Permutation.LocalSearch {
+  [Item("Exhaustive Hillclimber (permutation)", "", ExcludeGenericTypeInfo = true)]
+  [StorableClass]
+  public class ExhaustiveHillClimb<TContext> : NamedItem, ILocalSearch<TContext>
+      where TContext : ISingleSolutionHeuristicAlgorithmContext<ISingleObjectiveHeuristicOptimizationProblem, Encodings.PermutationEncoding.Permutation>,
+                       IEvaluationServiceContext<Encodings.PermutationEncoding.Permutation> {
+
+    [StorableConstructor]
+    protected ExhaustiveHillClimb(bool deserializing) : base(deserializing) { }
+    protected ExhaustiveHillClimb(ExhaustiveHillClimb<TContext> original, Cloner cloner) : base(original, cloner) { }
+    public ExhaustiveHillClimb() {
+      Name = ItemName;
+      Description = ItemDescription;
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new ExhaustiveHillClimb<TContext>(this, cloner);
+    }
+
+    public void Optimize(TContext context) {
+      var quality = context.Solution.Fitness;
+      try {
+        var result = Exhaustive.HillClimb(context.Random, context.Solution.Solution, ref quality,
+          context.Maximization, context.Evaluate, CancellationToken.None);
+        context.IncrementEvaluatedSolutions(result.Item1);
+        context.Iterations = result.Item2;
+      } finally {
+        context.Solution.Fitness = quality;
+      }
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/LocalSearch/ExhaustiveHillClimbSubspace.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/LocalSearch/ExhaustiveHillClimbSubspace.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/LocalSearch/ExhaustiveHillClimbSubspace.cs	(revision 14666)
@@ -0,0 +1,62 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Threading;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Algorithms.MemPR.Util;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.PermutationEncoding;
+using HeuristicLab.Optimization;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Algorithms.MemPR.Permutation.LocalSearch {
+  [Item("Exhaustive Subspace Hillclimber (permutation)", "", ExcludeGenericTypeInfo = true)]
+  [StorableClass]
+  public class ExhaustiveHillClimbSubspace<TContext> : NamedItem, ILocalSearch<TContext>
+      where TContext : ISingleSolutionHeuristicAlgorithmContext<ISingleObjectiveHeuristicOptimizationProblem, Encodings.PermutationEncoding.Permutation>,
+                       IPermutationSubspaceContext, IEvaluationServiceContext<Encodings.PermutationEncoding.Permutation> {
+
+    [StorableConstructor]
+    protected ExhaustiveHillClimbSubspace(bool deserializing) : base(deserializing) { }
+    protected ExhaustiveHillClimbSubspace(ExhaustiveHillClimbSubspace<TContext> original, Cloner cloner) : base(original, cloner) { }
+    public ExhaustiveHillClimbSubspace() {
+      Name = ItemName;
+      Description = ItemDescription;
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new ExhaustiveHillClimbSubspace<TContext>(this, cloner);
+    }
+
+    public void Optimize(TContext context) {
+      var quality = context.Solution.Fitness;
+      try {
+        var result = Exhaustive.HillClimb(context.Random, context.Solution.Solution, ref quality,
+          context.Maximization, context.Evaluate, CancellationToken.None, context.Subspace.Subspace);
+        context.IncrementEvaluatedSolutions(result.Item1);
+        context.Iterations = result.Item2;
+      } finally {
+        context.Solution.Fitness = quality;
+      }
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/LocalSearch/StaticAPI/Exhaustive.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/LocalSearch/StaticAPI/Exhaustive.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/LocalSearch/StaticAPI/Exhaustive.cs	(revision 14666)
@@ -0,0 +1,56 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Threading;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.PermutationEncoding;
+
+namespace HeuristicLab.Algorithms.MemPR.Permutation.LocalSearch {
+  public static class Exhaustive {
+#if DEBUG
+    private const bool VALIDATE = true;
+#else
+  private const bool VALIDATE = false;
+#endif
+
+    public static Tuple<int, int> HillClimb(IRandom random, Encodings.PermutationEncoding.Permutation perm,
+      ref double quality, bool maximization, Func<Encodings.PermutationEncoding.Permutation, CancellationToken, double> eval,
+      CancellationToken token, bool[,] subspace = null) {
+      if (double.IsNaN(quality)) quality = eval(perm, token);
+      Tuple<int, int> changes;
+      switch (perm.PermutationType) {
+        case PermutationTypes.Absolute:
+          changes = ExhaustiveSwap2.HillClimb(random, perm, ref quality, maximization, eval, token, subspace);
+          break;
+        case PermutationTypes.RelativeDirected:
+          changes = Exhaustive1Shift.HillClimb(random, perm, ref quality, maximization, eval, token, subspace);
+          break;
+        case PermutationTypes.RelativeUndirected:
+          changes = Exhaustive2Opt.HillClimb(random, perm, ref quality, maximization, eval, token, subspace);
+          break;
+        default: throw new ArgumentException(string.Format("Permutation type {0} is not known", perm.PermutationType));
+      }
+      if (VALIDATE && !perm.Validate()) throw new ArgumentException("HillClimb produced invalid child");
+      return changes;
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/LocalSearch/StaticAPI/Exhaustive1Shift.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/LocalSearch/StaticAPI/Exhaustive1Shift.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/LocalSearch/StaticAPI/Exhaustive1Shift.cs	(revision 14666)
@@ -0,0 +1,79 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Linq;
+using System.Threading;
+using HeuristicLab.Algorithms.MemPR.Util;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.PermutationEncoding;
+using HeuristicLab.Random;
+
+namespace HeuristicLab.Algorithms.MemPR.Permutation.LocalSearch {
+  public static class Exhaustive1Shift {
+    public static Tuple<int, int> HillClimb(IRandom random, Encodings.PermutationEncoding.Permutation perm,
+      ref double quality, bool maximization, Func<Encodings.PermutationEncoding.Permutation, CancellationToken, double> eval,
+      CancellationToken token, bool[,] subspace = null) {
+      var evaluations = 0;
+      var current = perm;
+      if (double.IsNaN(quality)) {
+        quality = eval(current, token);
+        evaluations++;
+      }
+      TranslocationMove lastSuccessMove = null;
+      var steps = 0;
+      var neighborhood = ExhaustiveInsertionMoveGenerator.Generate(current).Shuffle(random).ToList();
+      while (true) {
+        foreach (var shift in neighborhood) {
+          if (lastSuccessMove != null && shift.Index1 == lastSuccessMove.Index1 && shift.Index2 == lastSuccessMove.Index2 && shift.Index3 == lastSuccessMove.Index3) {
+            // been there, done that
+            lastSuccessMove = null;
+            break;
+          }
+          var prev1 = shift.Index1 - 1;
+          var next1 = (shift.Index1 + 1) % current.Length;
+          if (prev1 < 0) prev1 += current.Length;
+          var prev3 = shift.Index3 - 1;
+          var next3 = (shift.Index3 + 1) % current.Length;
+          if (prev3 < 0) prev3 += current.Length;
+          if (subspace != null && !(subspace[current[prev1], current[shift.Index1]] && subspace[current[shift.Index1], current[next1]]
+                                && subspace[current[prev3], current[shift.Index3]] && subspace[current[shift.Index3], current[next3]]))
+            continue;
+          TranslocationManipulator.Apply(current, shift.Index1, shift.Index2, shift.Index3);
+          var q = eval(current, token);
+          evaluations++;
+          if (FitnessComparer.IsBetter(maximization, q, quality)) {
+            steps++;
+            quality = q;
+            lastSuccessMove = shift;
+          } else TranslocationManipulator.Apply(current, shift.Index3, shift.Index3, shift.Index1);
+
+          if (token.IsCancellationRequested) {
+            lastSuccessMove = null;
+            break;
+          }
+        }
+        if (lastSuccessMove == null) break;
+      }
+      return Tuple.Create(evaluations, steps);
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/LocalSearch/StaticAPI/Exhaustive2Opt.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/LocalSearch/StaticAPI/Exhaustive2Opt.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/LocalSearch/StaticAPI/Exhaustive2Opt.cs	(revision 14666)
@@ -0,0 +1,76 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Linq;
+using System.Threading;
+using HeuristicLab.Algorithms.MemPR.Util;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.PermutationEncoding;
+using HeuristicLab.Random;
+
+namespace HeuristicLab.Algorithms.MemPR.Permutation.LocalSearch {
+  public static class Exhaustive2Opt {
+    public static Tuple<int, int> HillClimb(IRandom random, Encodings.PermutationEncoding.Permutation perm,
+      ref double quality, bool maximization, Func<Encodings.PermutationEncoding.Permutation, CancellationToken, double> eval,
+      CancellationToken token, bool[,] subspace = null) {
+      var evaluations = 0;
+      var current = perm;
+      if (double.IsNaN(quality)) {
+        quality = eval(current, token);
+        evaluations++;
+      }
+      InversionMove lastSuccessMove = null;
+      var steps = 0;
+      var neighborhood = ExhaustiveInversionMoveGenerator.Generate(current).Shuffle(random).ToList();
+      while (true) {
+        foreach (var opt in neighborhood) {
+          if (lastSuccessMove != null && opt.Index1 == lastSuccessMove.Index1 && opt.Index2 == lastSuccessMove.Index2) {
+            // been there, done that
+            lastSuccessMove = null;
+            break;
+          }
+          var prev = opt.Index1 - 1;
+          var next = (opt.Index2 + 1) % current.Length;
+          if (prev < 0) prev += current.Length;
+          if (subspace != null && !(subspace[current[prev], current[opt.Index1]] && subspace[current[opt.Index2], current[next]]))
+            continue;
+
+          current.Reverse(opt.Index1, opt.Index2 - opt.Index1 + 1);
+          var q = eval(current, token);
+          evaluations++;
+          if (FitnessComparer.IsBetter(maximization, q, quality)) {
+            steps++;
+            quality = q;
+            lastSuccessMove = opt;
+          } else current.Reverse(opt.Index1, opt.Index2 - opt.Index1 + 1);
+
+          if (token.IsCancellationRequested) {
+            lastSuccessMove = null;
+            break;
+          }
+        }
+        if (lastSuccessMove == null) break;
+      }
+      return Tuple.Create(evaluations, steps);
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/LocalSearch/StaticAPI/ExhaustiveSwap2.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/LocalSearch/StaticAPI/ExhaustiveSwap2.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/LocalSearch/StaticAPI/ExhaustiveSwap2.cs	(revision 14666)
@@ -0,0 +1,78 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Linq;
+using System.Threading;
+using HeuristicLab.Algorithms.MemPR.Util;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.PermutationEncoding;
+using HeuristicLab.Random;
+
+namespace HeuristicLab.Algorithms.MemPR.Permutation.LocalSearch {
+  public static class ExhaustiveSwap2 {
+    public static Tuple<int, int> HillClimb(IRandom random, Encodings.PermutationEncoding.Permutation perm,
+      ref double quality, bool maximization, Func<Encodings.PermutationEncoding.Permutation, CancellationToken, double> eval,
+      CancellationToken token, bool[,] subspace = null) {
+      var evaluations = 0;
+      var current = perm;
+      if (double.IsNaN(quality)) {
+        quality = eval(current, token);
+        evaluations++;
+      }
+      Swap2Move lastSuccessMove = null;
+      var steps = 0;
+      var neighborhood = ExhaustiveSwap2MoveGenerator.Generate(current).Shuffle(random).ToList();
+      while (true) {
+        foreach (var swap in neighborhood) {
+          if (lastSuccessMove != null && swap.Index1 == lastSuccessMove.Index1 && swap.Index2 == lastSuccessMove.Index2) {
+            // been there, done that
+            lastSuccessMove = null;
+            break;
+          }
+          if (subspace != null && !(subspace[swap.Index1, 0] && subspace[swap.Index2, 0]))
+            continue;
+
+          var h = current[swap.Index1];
+          current[swap.Index1] = current[swap.Index2];
+          current[swap.Index2] = h;
+          var q = eval(current, token);
+          evaluations++;
+          if (FitnessComparer.IsBetter(maximization, q, quality)) {
+            steps++;
+            quality = q;
+            lastSuccessMove = swap;
+          } else {
+            current[swap.Index2] = current[swap.Index1];
+            current[swap.Index1] = h;
+          }
+
+          if (token.IsCancellationRequested) {
+            lastSuccessMove = null;
+            break;
+          }
+        }
+        if (lastSuccessMove == null) break;
+      }
+      return Tuple.Create(evaluations, steps);
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/PermutationMemPR.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/PermutationMemPR.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/PermutationMemPR.cs	(revision 14666)
@@ -0,0 +1,909 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Algorithms.MemPR.Util;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.PermutationEncoding;
+using HeuristicLab.Optimization;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.PluginInfrastructure;
+using HeuristicLab.Random;
+
+namespace HeuristicLab.Algorithms.MemPR.Permutation {
+  [Item("MemPR (permutation)", "MemPR implementation for permutations.")]
+  [StorableClass]
+  [Creatable(CreatableAttribute.Categories.PopulationBasedAlgorithms, Priority = 999)]
+  public class PermutationMemPR : MemPRAlgorithm<ISingleObjectiveHeuristicOptimizationProblem, Encodings.PermutationEncoding.Permutation, PermutationMemPRPopulationContext, PermutationMemPRSolutionContext> {
+#if DEBUG
+    private const bool VALIDATE = true;
+#else
+    private const bool VALIDATE = false;
+#endif
+
+    [StorableConstructor]
+    protected PermutationMemPR(bool deserializing) : base(deserializing) { }
+    protected PermutationMemPR(PermutationMemPR original, Cloner cloner) : base(original, cloner) { }
+    public PermutationMemPR() {
+      foreach (var trainer in ApplicationManager.Manager.GetInstances<ISolutionModelTrainer<PermutationMemPRPopulationContext>>())
+        SolutionModelTrainerParameter.ValidValues.Add(trainer);
+
+      if (SolutionModelTrainerParameter.ValidValues.Count > 0) {
+        var unbiased = SolutionModelTrainerParameter.ValidValues.FirstOrDefault(x => !x.Bias);
+        if (unbiased != null) SolutionModelTrainerParameter.Value = unbiased;
+      }
+
+      foreach (var localSearch in ApplicationManager.Manager.GetInstances<ILocalSearch<PermutationMemPRSolutionContext>>()) {
+        LocalSearchParameter.ValidValues.Add(localSearch);
+      }
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new PermutationMemPR(this, cloner);
+    }
+
+    protected override bool Eq(Encodings.PermutationEncoding.Permutation a, Encodings.PermutationEncoding.Permutation b) {
+      return new PermutationEqualityComparer().Equals(a, b);
+    }
+
+    protected override double Dist(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> a, ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> b) {
+      return Dist(a.Solution, b.Solution);
+    }
+
+    private static double Dist(Encodings.PermutationEncoding.Permutation a, Encodings.PermutationEncoding.Permutation b) {
+      return 1.0 - HammingSimilarityCalculator.CalculateSimilarity(a, b);
+    }
+
+    protected override ISolutionSubspace<Encodings.PermutationEncoding.Permutation> CalculateSubspace(IEnumerable<Encodings.PermutationEncoding.Permutation> solutions, bool inverse = false) {
+      var solutionsIter = solutions.GetEnumerator();
+      if (!solutionsIter.MoveNext()) throw new ArgumentException("Cannot calculate sub-space when no solutions are given.");
+      var first = solutionsIter.Current;
+
+      var N = solutionsIter.Current.Length;
+      var type = solutionsIter.Current.PermutationType;
+      var subspace = new bool[N, type == PermutationTypes.Absolute ? 1 : N];
+      switch (type) {
+        case PermutationTypes.Absolute: {
+            if (inverse) {
+              for (var i = 0; i < subspace.GetLength(0); i++)
+                subspace[i, 0] = true;
+            }
+            while (solutionsIter.MoveNext()) {
+              var s = solutionsIter.Current;
+              for (var i = 0; i < s.Length; i++) {
+                if (first[i] != s[i]) subspace[i, 0] = !inverse;
+              }
+            }
+          }
+          break;
+        case PermutationTypes.RelativeDirected: {
+            if (inverse) {
+              for (var i = 0; i < subspace.GetLength(0); i++)
+                for (var j = 0; j < subspace.GetLength(1); j++)
+                  subspace[i, j] = true;
+            }
+            var placedFirst = new int[first.Length];
+            for (var i = 0; i < first.Length; i++) {
+              placedFirst[first[i]] = i;
+            }
+            while (solutionsIter.MoveNext()) {
+              var s = solutionsIter.Current;
+              for (var i = 0; i < s.Length; i++) {
+                if (placedFirst[s[i]] - placedFirst[s.GetCircular(i + 1)] != -1)
+                  subspace[i, 0] = !inverse;
+              }
+            }
+          }
+          break;
+        case PermutationTypes.RelativeUndirected: {
+            if (inverse) {
+              for (var i = 0; i < subspace.GetLength(0); i++)
+                for (var j = 0; j < subspace.GetLength(1); j++)
+                  subspace[i, j] = true;
+            }
+            var placedFirst = new int[first.Length];
+            for (var i = 0; i < first.Length; i++) {
+              placedFirst[first[i]] = i;
+            }
+            while (solutionsIter.MoveNext()) {
+              var s = solutionsIter.Current;
+              for (var i = 0; i < s.Length; i++) {
+                if (Math.Abs(placedFirst[s[i]] - placedFirst[s.GetCircular(i + 1)]) != 1)
+                  subspace[i, 0] = !inverse;
+              }
+            }
+          }
+          break;
+        default:
+          throw new ArgumentException(string.Format("Unknown permutation type {0}", type));
+      }
+      return new PermutationSolutionSubspace(subspace);
+    }
+
+    protected override void AdaptiveWalk(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> scope, int maxEvals, CancellationToken token, ISolutionSubspace<Encodings.PermutationEncoding.Permutation> subspace = null) {
+      var quality = scope.Fitness;
+      try {
+        TabuWalk(Context.Random, scope.Solution, Context.Evaluate, token, ref quality, maxEvals, subspace != null ? ((PermutationSolutionSubspace)subspace).Subspace : null);
+      } finally {
+        scope.Fitness = quality;
+      }
+    }
+
+    public void TabuWalk(IRandom random, Encodings.PermutationEncoding.Permutation perm, Func<Encodings.PermutationEncoding.Permutation, CancellationToken, double> eval, CancellationToken token, ref double quality, int maxEvals = int.MaxValue, bool[,] subspace = null) {
+      switch (perm.PermutationType) {
+        case PermutationTypes.Absolute:
+          TabuWalkSwap(random, perm, eval, token, ref quality, maxEvals, subspace);
+          break;
+        case PermutationTypes.RelativeDirected:
+          TabuWalkShift(random, perm, eval, token, ref quality, maxEvals, subspace);
+          break;
+        case PermutationTypes.RelativeUndirected:
+          TabuWalkOpt(random, perm, eval, token, ref quality, maxEvals, subspace);
+          break;
+        default: throw new ArgumentException(string.Format("Permutation type {0} is not known", perm.PermutationType));
+      }
+      if (VALIDATE && !perm.Validate()) throw new ArgumentException("TabuWalk produced invalid child");
+    }
+
+    public int TabuWalkSwap(IRandom random, Encodings.PermutationEncoding.Permutation perm, Func<Encodings.PermutationEncoding.Permutation, CancellationToken, double> eval, CancellationToken token, ref double quality, int maxEvals = int.MaxValue, bool[,] subspace = null) {
+      var evaluations = 0;
+      var maximization = Context.Maximization;
+      if (double.IsNaN(quality)) {
+        quality = eval(perm, token);
+        evaluations++;
+      }
+      Encodings.PermutationEncoding.Permutation bestOfTheWalk = null;
+      double bestOfTheWalkF = double.NaN;
+      var current = (Encodings.PermutationEncoding.Permutation)perm.Clone();
+      var currentF = quality;
+      var overallImprovement = false;
+      var tabu = new double[current.Length, current.Length];
+      for (var i = 0; i < current.Length; i++) {
+        for (var j = i; j < current.Length; j++) {
+          tabu[i, j] = tabu[j, i] = maximization ? double.MinValue : double.MaxValue;
+        }
+        tabu[i, current[i]] = currentF;
+      }
+
+      var steps = 0;
+      var stepsUntilBestOfWalk = 0;
+      for (var iter = 0; iter < int.MaxValue; iter++) {
+        var allTabu = true;
+        var bestOfTheRestF = double.NaN;
+        Swap2Move bestOfTheRest = null;
+        var improved = false;
+        foreach (var swap in ExhaustiveSwap2MoveGenerator.Generate(current).Shuffle(random)) {
+          if (subspace != null && !(subspace[swap.Index1, 0] && subspace[swap.Index2, 0]))
+            continue;
+
+          var h = current[swap.Index1];
+          current[swap.Index1] = current[swap.Index2];
+          current[swap.Index2] = h;
+          var q = eval(current, token);
+          evaluations++;
+          if (FitnessComparer.IsBetter(maximization, q, quality)) {
+            overallImprovement = true;
+            quality = q;
+            for (var i = 0; i < current.Length; i++) perm[i] = current[i];
+          }
+          // check if it would not be an improvement to swap these into their positions
+          var isTabu = !FitnessComparer.IsBetter(maximization, q, tabu[swap.Index1, current[swap.Index1]])
+                    && !FitnessComparer.IsBetter(maximization, q, tabu[swap.Index2, current[swap.Index2]]);
+          if (!isTabu) allTabu = false;
+          if (FitnessComparer.IsBetter(maximization, q, currentF) && !isTabu) {
+            if (FitnessComparer.IsBetter(maximization, q, bestOfTheWalkF)) {
+              bestOfTheWalk = (Encodings.PermutationEncoding.Permutation)current.Clone();
+              bestOfTheWalkF = q;
+              stepsUntilBestOfWalk = steps;
+            }
+            steps++;
+            improved = true;
+            // perform the move
+            currentF = q;
+            // mark that to move them to their previous position requires to make an improvement
+            tabu[swap.Index1, current[swap.Index2]] = maximization ? Math.Max(q, tabu[swap.Index1, current[swap.Index2]])
+                                                                   : Math.Min(q, tabu[swap.Index1, current[swap.Index2]]);
+            tabu[swap.Index2, current[swap.Index1]] = maximization ? Math.Max(q, tabu[swap.Index2, current[swap.Index1]])
+                                                                   : Math.Min(q, tabu[swap.Index2, current[swap.Index1]]);
+          } else { // undo the move
+            if (!isTabu && FitnessComparer.IsBetter(maximization, q, bestOfTheRestF)) {
+              bestOfTheRest = swap;
+              bestOfTheRestF = q;
+            }
+            current[swap.Index2] = current[swap.Index1];
+            current[swap.Index1] = h;
+          }
+          if (evaluations >= maxEvals) break;
+        }
+        if (!allTabu && !improved && bestOfTheRest != null) {
+          tabu[bestOfTheRest.Index1, current[bestOfTheRest.Index1]] = maximization ? Math.Max(currentF, tabu[bestOfTheRest.Index1, current[bestOfTheRest.Index1]])
+                                                                                   : Math.Min(currentF, tabu[bestOfTheRest.Index1, current[bestOfTheRest.Index1]]);
+          tabu[bestOfTheRest.Index2, current[bestOfTheRest.Index2]] = maximization ? Math.Max(currentF, tabu[bestOfTheRest.Index2, current[bestOfTheRest.Index2]])
+                                                                                   : Math.Min(currentF, tabu[bestOfTheRest.Index2, current[bestOfTheRest.Index2]]);
+
+          var h = current[bestOfTheRest.Index1];
+          current[bestOfTheRest.Index1] = current[bestOfTheRest.Index2];
+          current[bestOfTheRest.Index2] = h;
+
+          currentF = bestOfTheRestF;
+          steps++;
+        } else if (allTabu) break;
+        if (evaluations >= maxEvals) break;
+      }
+      Context.IncrementEvaluatedSolutions(evaluations);
+      if (!overallImprovement && bestOfTheWalk != null) {
+        quality = bestOfTheWalkF;
+        for (var i = 0; i < current.Length; i++) perm[i] = bestOfTheWalk[i];
+      }
+      return stepsUntilBestOfWalk;
+    }
+
+    public int TabuWalkShift(IRandom random, Encodings.PermutationEncoding.Permutation perm, Func<Encodings.PermutationEncoding.Permutation, CancellationToken, double> eval, CancellationToken token, ref double quality, int maxEvals = int.MaxValue, bool[,] subspace = null) {
+      return 0;
+    }
+
+    public int TabuWalkOpt(IRandom random, Encodings.PermutationEncoding.Permutation perm, Func<Encodings.PermutationEncoding.Permutation, CancellationToken, double> eval, CancellationToken token, ref double quality, int maxEvals = int.MaxValue, bool[,] subspace = null) {
+      var maximization = Context.Maximization;
+      var evaluations = 0;
+      if (double.IsNaN(quality)) {
+        quality = eval(perm, token);
+        evaluations++;
+      }
+      Encodings.PermutationEncoding.Permutation bestOfTheWalk = null;
+      var bestOfTheWalkF = double.NaN;
+      var current = (Encodings.PermutationEncoding.Permutation)perm.Clone();
+      var currentF = quality;
+      var overallImprovement = false;
+      var tabu = new double[current.Length, current.Length];
+      for (var i = 0; i < current.Length; i++) {
+        for (var j = i; j < current.Length; j++) {
+          tabu[i, j] = tabu[j, i] = double.MaxValue;
+        }
+        tabu[current[i], current.GetCircular(i + 1)] = currentF;
+        tabu[current.GetCircular(i + 1), current[i]] = currentF;
+      }
+
+      var steps = 0;
+      var stepsUntilBestOfWalk = 0;
+      for (var iter = 0; iter < int.MaxValue; iter++) {
+        var allTabu = true;
+        var bestOfTheRestF = double.NaN;
+        InversionMove bestOfTheRest = null;
+        var improved = false;
+        
+        foreach (var opt in ExhaustiveInversionMoveGenerator.Generate(current).Shuffle(random)) {
+          var prev = opt.Index1 - 1;
+          var next = (opt.Index2 + 1) % current.Length;
+          if (prev < 0) prev += current.Length;
+          if (subspace != null && !(subspace[current[prev], current[opt.Index1]] && subspace[current[opt.Index2], current[next]]))
+            continue;
+
+          current.Reverse(opt.Index1, opt.Index2 - opt.Index1 + 1);
+
+          var q = eval(current, token);
+          evaluations++;
+          if (FitnessComparer.IsBetter(maximization, q, quality)) {
+            overallImprovement = true;
+            quality = q;
+            for (var i = 0; i < current.Length; i++) perm[i] = current[i];
+          }
+          // check if it would not be an improvement to opt these into their positions
+          var isTabu = !FitnessComparer.IsBetter(maximization, q, tabu[current[prev], current[opt.Index1]])
+                    && !FitnessComparer.IsBetter(maximization, q, tabu[current[opt.Index2], current[next]]);
+          if (!isTabu) allTabu = false;
+          if (!isTabu && FitnessComparer.IsBetter(maximization, q, currentF)) {
+            if (FitnessComparer.IsBetter(maximization, q, bestOfTheWalkF)) {
+              bestOfTheWalk = (Encodings.PermutationEncoding.Permutation)current.Clone();
+              bestOfTheWalkF = q;
+              stepsUntilBestOfWalk = steps;
+            }
+
+            steps++;
+            improved = true;
+            // perform the move
+            currentF = q;
+            // mark that to move them to their previous position requires to make an improvement
+            if (maximization) {
+              tabu[current[prev], current[opt.Index2]] = Math.Max(q, tabu[current[prev], current[opt.Index2]]);
+              tabu[current[opt.Index2], current[prev]] = Math.Max(q, tabu[current[opt.Index2], current[prev]]);
+              tabu[current[opt.Index1], current[next]] = Math.Max(q, tabu[current[opt.Index1], current[next]]);
+              tabu[current[next], current[opt.Index1]] = Math.Max(q, tabu[current[next], current[opt.Index1]]);
+            } else {
+              tabu[current[prev], current[opt.Index2]] = Math.Min(q, tabu[current[prev], current[opt.Index2]]);
+              tabu[current[opt.Index2], current[prev]] = Math.Min(q, tabu[current[opt.Index2], current[prev]]);
+              tabu[current[opt.Index1], current[next]] = Math.Min(q, tabu[current[opt.Index1], current[next]]);
+              tabu[current[next], current[opt.Index1]] = Math.Min(q, tabu[current[next], current[opt.Index1]]);
+            }
+          } else { // undo the move
+            if (!isTabu && FitnessComparer.IsBetter(maximization, q, bestOfTheRestF)) {
+              bestOfTheRest = opt;
+              bestOfTheRestF = q;
+            }
+            
+            current.Reverse(opt.Index1, opt.Index2 - opt.Index1 + 1);
+          }
+          if (evaluations >= maxEvals) break;
+        }
+        if (!allTabu && !improved && bestOfTheRest != null) {
+          var prev = bestOfTheRest.Index1 - 1;
+          var next = (bestOfTheRest.Index2 + 1) % current.Length;
+          if (prev < 0) prev += current.Length;
+
+          if (maximization) {
+            tabu[current[prev], current[bestOfTheRest.Index1]] = Math.Max(currentF, tabu[current[prev], current[bestOfTheRest.Index1]]);
+            tabu[current[bestOfTheRest.Index1], current[prev]] = Math.Max(currentF, tabu[current[bestOfTheRest.Index1], current[prev]]);
+            tabu[current[bestOfTheRest.Index2], current[next]] = Math.Max(currentF, tabu[current[bestOfTheRest.Index2], current[next]]);
+            tabu[current[next], current[bestOfTheRest.Index2]] = Math.Max(currentF, tabu[current[next], current[bestOfTheRest.Index2]]);
+          } else {
+            tabu[current[prev], current[bestOfTheRest.Index1]] = Math.Min(currentF, tabu[current[prev], current[bestOfTheRest.Index1]]);
+            tabu[current[bestOfTheRest.Index1], current[prev]] = Math.Min(currentF, tabu[current[bestOfTheRest.Index1], current[prev]]);
+            tabu[current[bestOfTheRest.Index2], current[next]] = Math.Min(currentF, tabu[current[bestOfTheRest.Index2], current[next]]);
+            tabu[current[next], current[bestOfTheRest.Index2]] = Math.Min(currentF, tabu[current[next], current[bestOfTheRest.Index2]]);
+          }
+          current.Reverse(bestOfTheRest.Index1, bestOfTheRest.Index2 - bestOfTheRest.Index1 + 1);
+
+          currentF = bestOfTheRestF;
+          steps++;
+        } else if (allTabu) break;
+        if (evaluations >= maxEvals) break;
+      }
+      Context.IncrementEvaluatedSolutions(evaluations);
+      if (!overallImprovement && bestOfTheWalk != null) {
+        quality = bestOfTheWalkF;
+        for (var i = 0; i < current.Length; i++) perm[i] = bestOfTheWalk[i];
+      }
+      return stepsUntilBestOfWalk;
+    }
+
+    protected override ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> Breed(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> p1, ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> p2, CancellationToken token) {
+      ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> child = null;
+
+      if (p1.Solution.PermutationType == PermutationTypes.Absolute) {
+        child = CrossAbsolute(p1, p2, token);
+      } else if (p1.Solution.PermutationType == PermutationTypes.RelativeDirected) {
+        child = CrossRelativeDirected(p1, p2, token);
+      } else if (p1.Solution.PermutationType == PermutationTypes.RelativeUndirected) {
+        child = CrossRelativeUndirected(p1, p2, token);
+      } else throw new ArgumentException(string.Format("Unknown permutation type {0}", p1.Solution.PermutationType));
+
+      if (VALIDATE && !child.Solution.Validate()) throw new ArgumentException("Cross produced invalid child");
+      return child;
+    }
+
+    private ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> CrossAbsolute(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> p1, ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> p2, CancellationToken token) {
+      var cache = new HashSet<Encodings.PermutationEncoding.Permutation>(new PermutationEqualityComparer());
+      cache.Add(p1.Solution);
+      cache.Add(p2.Solution);
+
+      var cacheHits = new Dictionary<int, int>() { { 0, 0 }, { 1, 0 }, { 2, 0 } };
+      var evaluations = 0;
+      ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> offspring = null;
+      var probe = Context.ToScope((Encodings.PermutationEncoding.Permutation)p1.Solution.Clone());
+      while (evaluations < p1.Solution.Length) {
+        Encodings.PermutationEncoding.Permutation c = null;
+        var xochoice = cacheHits.SampleRandom(Context.Random).Key;
+        switch (xochoice) {
+          case 0: c = CyclicCrossover2.Apply(Context.Random, p1.Solution, p2.Solution); break;
+          case 1: c = PartiallyMatchedCrossover.Apply(Context.Random, p1.Solution, p2.Solution); break;
+          case 2: c = UniformLikeCrossover.Apply(Context.Random, p1.Solution, p2.Solution); break;
+        }
+        if (cache.Contains(c)) {
+          cacheHits[xochoice]++;
+          if (cacheHits[xochoice] > 10) {
+            cacheHits.Remove(xochoice);
+            if (cacheHits.Count == 0) break;
+          }
+          continue;
+        }
+        probe.Solution = c;
+        Context.Evaluate(probe, token);
+        evaluations++;
+        cache.Add(c);
+        if (offspring == null || Context.IsBetter(probe, offspring)) {
+          offspring = probe;
+          if (Context.IsBetter(offspring, p1) && Context.IsBetter(offspring, p2))
+            break;
+        }
+      }
+      Context.IncrementEvaluatedSolutions(evaluations);
+      return offspring ?? p1;
+    }
+
+    private ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> CrossRelativeDirected(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> p1, ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> p2, CancellationToken token) {
+      var cache = new HashSet<Encodings.PermutationEncoding.Permutation>(new PermutationEqualityComparer());
+      cache.Add(p1.Solution);
+      cache.Add(p2.Solution);
+
+      var cacheHits = new Dictionary<int, int>() { { 0, 0 }, { 1, 0 }, { 2, 0 } };
+      var evaluations = 0;
+      ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> offspring = null;
+      var probe = Context.ToScope((Encodings.PermutationEncoding.Permutation)p1.Solution.Clone());
+      while (evaluations < p1.Solution.Length) {
+        Encodings.PermutationEncoding.Permutation c = null;
+        var xochoice = cacheHits.SampleRandom(Context.Random).Key;
+        switch (xochoice) {
+          case 0: c = OrderCrossover2.Apply(Context.Random, p1.Solution, p2.Solution); break;
+          case 1: c = PartiallyMatchedCrossover.Apply(Context.Random, p1.Solution, p2.Solution); break;
+          case 2: c = MaximalPreservativeCrossover.Apply(Context.Random, p1.Solution, p2.Solution); break;
+        }
+        if (cache.Contains(c)) {
+          cacheHits[xochoice]++;
+          if (cacheHits[xochoice] > 10) {
+            cacheHits.Remove(xochoice);
+            if (cacheHits.Count == 0) break;
+          }
+          continue;
+        }
+        probe.Solution = c;
+        Context.Evaluate(probe, token);
+        evaluations++;
+        cache.Add(c);
+        if (offspring == null || Context.IsBetter(probe, offspring)) {
+          offspring = probe;
+          if (Context.IsBetter(offspring, p1) && Context.IsBetter(offspring, p2))
+            break;
+        }
+      }
+      Context.IncrementEvaluatedSolutions(evaluations);
+      return offspring ?? p1;
+    }
+
+    private ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> CrossRelativeUndirected(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> p1, ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> p2, CancellationToken token) {
+      var cache = new HashSet<Encodings.PermutationEncoding.Permutation>(new PermutationEqualityComparer());
+      cache.Add(p1.Solution);
+      cache.Add(p2.Solution);
+
+      var cacheHits = new Dictionary<int, int>() { { 0, 0 }, { 1, 0 }, { 2, 0 } };
+      var evaluations = 0;
+      ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> offspring = null;
+      var probe = Context.ToScope((Encodings.PermutationEncoding.Permutation)p1.Solution.Clone());
+      while (evaluations <= p1.Solution.Length) {
+        Encodings.PermutationEncoding.Permutation c = null;
+        var xochoice = cacheHits.SampleRandom(Context.Random).Key;
+        switch (xochoice) {
+          case 0: c = OrderCrossover2.Apply(Context.Random, p1.Solution, p2.Solution); break;
+          case 1: c = EdgeRecombinationCrossover.Apply(Context.Random, p1.Solution, p2.Solution); break;
+          case 2: c = MaximalPreservativeCrossover.Apply(Context.Random, p1.Solution, p2.Solution); break;
+        }
+        if (cache.Contains(c)) {
+          cacheHits[xochoice]++;
+          if (cacheHits[xochoice] > 10) {
+            cacheHits.Remove(xochoice);
+            if (cacheHits.Count == 0) break;
+          }
+          continue;
+        }
+        probe.Solution = c;
+        Context.Evaluate(probe, token);
+        evaluations++;
+        cache.Add(c);
+        if (offspring == null || Context.IsBetter(probe, offspring)) {
+          offspring = probe;
+          if (Context.IsBetter(offspring, p1) && Context.IsBetter(offspring, p2))
+            break;
+        }
+      }
+      Context.IncrementEvaluatedSolutions(evaluations);
+      return offspring ?? p1;
+    }
+
+    protected override ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> Link(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> a, ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> b, CancellationToken token, bool delink = false) {
+      double quality;
+      return Context.ToScope(Relink(Context.Random, a.Solution, b.Solution, Context.Evaluate, token, delink, out quality));
+    }
+
+    public Encodings.PermutationEncoding.Permutation Relink(IRandom random, Encodings.PermutationEncoding.Permutation p1, Encodings.PermutationEncoding.Permutation p2, Func<Encodings.PermutationEncoding.Permutation, CancellationToken, double> eval, CancellationToken token, bool delink, out double best) {
+      if (p1.PermutationType != p2.PermutationType) throw new ArgumentException(string.Format("Unequal permutation types {0} and {1}", p1.PermutationType, p2.PermutationType));
+      switch (p1.PermutationType) {
+        case PermutationTypes.Absolute:
+          return delink ? DelinkSwap(random, p1, p2, eval, token, out best) : RelinkSwap(random, p1, p2, eval, token, out best);
+        case PermutationTypes.RelativeDirected:
+          return RelinkShift(random, p1, p2, eval, token, delink, out best);
+        case PermutationTypes.RelativeUndirected:
+          return delink ? DelinkOpt(random, p1, p2, eval, token, out best) : RelinkOpt(random, p1, p2, eval, token, out best);
+        default: throw new ArgumentException(string.Format("Unknown permutation type {0}", p1.PermutationType));
+      }
+    }
+
+    public Encodings.PermutationEncoding.Permutation RelinkSwap(IRandom random, Encodings.PermutationEncoding.Permutation p1, Encodings.PermutationEncoding.Permutation p2, Func<Encodings.PermutationEncoding.Permutation, CancellationToken, double> eval, CancellationToken token, out double best) {
+      var maximization = Context.Maximization;
+      var evaluations = 0;
+      var child = (Encodings.PermutationEncoding.Permutation)p1.Clone();
+
+      best = double.NaN;
+      Encodings.PermutationEncoding.Permutation bestChild = null;
+
+      var options = Enumerable.Range(0, child.Length).Where(x => child[x] != p2[x]).ToList();
+      var invChild = new int[child.Length];
+      for (var i = 0; i < child.Length; i++) invChild[child[i]] = i;
+      
+      while (options.Count > 0) {
+        int bestOption = -1;
+        var bestChange = double.NaN;
+        for (var j = 0; j < options.Count; j++) {
+          var idx = options[j];
+          if (child[idx] == p2[idx]) {
+            options.RemoveAt(j);
+            j--;
+            continue;
+          }
+          Swap(child, invChild[p2[idx]], idx);
+          var moveF = eval(child, token);
+          evaluations++;
+          if (FitnessComparer.IsBetter(maximization, moveF, bestChange)) {
+            bestChange = moveF;
+            bestOption = j;
+          }
+          // undo
+          Swap(child, invChild[p2[idx]], idx);
+        }
+        if (!double.IsNaN(bestChange)) {
+          var idx1 = options[bestOption];
+          var idx2 = invChild[p2[idx1]];
+          Swap(child, idx1, idx2);
+          invChild[child[idx1]] = idx1;
+          invChild[child[idx2]] = idx2;
+          if (FitnessComparer.IsBetter(maximization, bestChange, best)) {
+            if (Dist(child, p2) > 0) {
+              best = bestChange;
+              bestChild = (Encodings.PermutationEncoding.Permutation)child.Clone();
+            }
+          }
+          options.RemoveAt(bestOption);
+        }
+      }
+      if (bestChild == null) {
+        best = eval(child, token);
+        evaluations++;
+      }
+      Context.IncrementEvaluatedSolutions(evaluations);
+
+      if (VALIDATE && bestChild != null && !bestChild.Validate()) throw new ArgumentException("Relinking produced invalid child");
+      if (VALIDATE && Dist(child, p2) > 0) throw new InvalidOperationException("Child is not equal to p2 after relinking");
+
+      return bestChild ?? child;
+    }
+
+    public Encodings.PermutationEncoding.Permutation DelinkSwap(IRandom random, Encodings.PermutationEncoding.Permutation p1, Encodings.PermutationEncoding.Permutation p2, Func<Encodings.PermutationEncoding.Permutation, CancellationToken, double> eval, CancellationToken token, out double best) {
+      var maximization = Context.Maximization;
+      var evaluations = 0;
+      var child = (Encodings.PermutationEncoding.Permutation)p1.Clone();
+
+      best = double.NaN;
+      Encodings.PermutationEncoding.Permutation bestChild = null;
+
+      var options = Enumerable.Range(0, child.Length).Where(x => child[x] == p2[x]).ToList();
+      
+      while (options.Count > 0) {
+        int bestOption = -1;
+        int bestCompanion = -1;
+        var bestChange = double.NaN;
+        for (var j = 0; j < options.Count; j++) {
+          var idx = options[j];
+          if (child[idx] != p2[idx]) {
+            options.RemoveAt(j);
+            j--;
+            continue;
+          }
+          for (var k = 0; k < child.Length; k++) {
+            if (k == idx) continue;
+            Swap(child, k, idx);
+            var moveF = eval(child, token);
+            evaluations++;
+            if (FitnessComparer.IsBetter(maximization, moveF, bestChange)) {
+              bestChange = moveF;
+              bestOption = j;
+              bestCompanion = k;
+            }
+            // undo
+            Swap(child, k, idx);
+          }
+        }
+        if (!double.IsNaN(bestChange)) {
+          var idx1 = options[bestOption];
+          Swap(child, idx1, bestCompanion);
+          if (FitnessComparer.IsBetter(maximization, bestChange, best)) {
+            if (!Eq(child, p2)) {
+              best = bestChange;
+              bestChild = (Encodings.PermutationEncoding.Permutation)child.Clone();
+            }
+          }
+          options.RemoveAt(bestOption);
+        }
+      }
+      if (bestChild == null) {
+        best = eval(child, token);
+        evaluations++;
+      }
+      Context.IncrementEvaluatedSolutions(evaluations);
+
+      if (VALIDATE && bestChild != null && !bestChild.Validate()) throw new ArgumentException("Delinking produced invalid child");
+
+      return bestChild ?? child;
+    }
+
+    public Encodings.PermutationEncoding.Permutation RelinkShift(IRandom random, Encodings.PermutationEncoding.Permutation p1, Encodings.PermutationEncoding.Permutation p2, Func<Encodings.PermutationEncoding.Permutation, CancellationToken, double> eval, CancellationToken token, bool delink, out double best) {
+      var maximization = Context.Maximization;
+      var evaluations = 0;
+      var child = (Encodings.PermutationEncoding.Permutation)p1.Clone();
+
+      best = double.NaN;
+      Encodings.PermutationEncoding.Permutation bestChild = null;
+
+      var invChild = new int[child.Length];
+      for (var i = 0; i < child.Length; i++) invChild[child[i]] = i;
+
+      var bestChange = double.NaN;
+      do {
+        int bestFrom = -1, bestTo = -1;
+        bestChange = double.NaN;
+        for (var j = 0; j < child.Length; j++) {
+          var c = invChild[p2[j]];
+          var n = invChild[p2.GetCircular(j + 1)];
+          if (n - c == 1 || c == child.Length - 1 && n == 0) continue;
+
+          if (c < n) Shift(child, from: n, to: c + 1);
+          else Shift(child, from: c, to: n);
+          var moveF = eval(child, token);
+          evaluations++;
+          if (FitnessComparer.IsBetter(maximization, moveF, bestChange)) {
+            bestChange = moveF;
+            bestFrom = c < n ? n : c;
+            bestTo = c < n ? c + 1 : n;
+          }
+          // undo
+          if (c < n) Shift(child, from: c + 1, to: n);
+          else Shift(child, from: n, to: c);
+        }
+        if (!double.IsNaN(bestChange)) {
+          Shift(child, bestFrom, bestTo);
+          for (var i = Math.Min(bestFrom, bestTo); i < Math.Max(bestFrom, bestTo); i++) invChild[child[i]] = i;
+          if (FitnessComparer.IsBetter(maximization, bestChange, best)) {
+            best = bestChange;
+            bestChild = (Encodings.PermutationEncoding.Permutation)child.Clone();
+          }
+        }
+      } while (!double.IsNaN(bestChange));
+
+      if (bestChild == null) {
+        best = eval(child, token);
+        evaluations++;
+      }
+      Context.IncrementEvaluatedSolutions(evaluations);
+
+      if (VALIDATE && bestChild != null && !bestChild.Validate()) throw new ArgumentException("Relinking produced invalid child");
+      if (VALIDATE && Dist(child, p2) > 0) throw new InvalidOperationException("Child is not equal to p2 after relinking");
+
+      return bestChild ?? child;
+    }
+
+    public Encodings.PermutationEncoding.Permutation RelinkOpt(IRandom random, Encodings.PermutationEncoding.Permutation p1, Encodings.PermutationEncoding.Permutation p2, Func<Encodings.PermutationEncoding.Permutation, CancellationToken, double> eval, CancellationToken token, out double best) {
+      var maximization = Context.Maximization;
+      var evaluations = 0;
+      var child = (Encodings.PermutationEncoding.Permutation)p1.Clone();
+
+      best = double.NaN;
+      Encodings.PermutationEncoding.Permutation bestChild = null;
+
+      var invChild = new int[child.Length];
+      var invP2 = new int[child.Length];
+      for (var i = 0; i < child.Length; i++) {
+        invChild[child[i]] = i;
+        invP2[p2[i]] = i;
+      }
+
+      var bestChange = double.NaN;
+      var moveQueue = new Queue<Tuple<int, int>>();
+      var undoStack = new Stack<Tuple<int, int>>();
+      do {
+        Queue<Tuple<int, int>> bestQueue = null;
+        bestChange = double.NaN;
+        for (var j = 0; j < p2.Length; j++) {
+          if (IsUndirectedEdge(invChild, p2[j], p2.GetCircular(j + 1))) continue;
+
+          var a = invChild[p2[j]];
+          var b = invChild[p2.GetCircular(j + 1)];
+          if (a > b) { var h = a; a = b; b = h; }
+          var aprev = a - 1;
+          var bprev = b - 1;
+          while (IsUndirectedEdge(invP2, child.GetCircular(aprev), child.GetCircular(aprev + 1))) {
+            aprev--;
+          }
+          while (IsUndirectedEdge(invP2, child.GetCircular(bprev), child.GetCircular(bprev + 1))) {
+            bprev--;
+          }
+          var bnext = b + 1;
+          var anext = a + 1;
+          while (IsUndirectedEdge(invP2, child.GetCircular(bnext - 1), child.GetCircular(bnext))) {
+            bnext++;
+          }
+          while (IsUndirectedEdge(invP2, child.GetCircular(anext - 1), child.GetCircular(anext))) {
+            anext++;
+          }
+          aprev++; bprev++; anext--; bnext--;
+
+          if (aprev < a && bnext > b) {
+            if (aprev < 0) {
+              moveQueue.Enqueue(Tuple.Create(a + 1, bnext));
+              moveQueue.Enqueue(Tuple.Create(a + 1, a + 1 + (bnext - b)));
+            } else {
+              moveQueue.Enqueue(Tuple.Create(aprev, b - 1));
+              moveQueue.Enqueue(Tuple.Create(b - 1 - (a - aprev), b - 1));
+            }
+          } else if (aprev < a && bnext == b && bprev == b) {
+            moveQueue.Enqueue(Tuple.Create(a + 1, b));
+          } else if (aprev < a && bprev < b) {
+            moveQueue.Enqueue(Tuple.Create(a + 1, b));
+          } else if (aprev == a && anext == a && bnext > b) {
+            moveQueue.Enqueue(Tuple.Create(a, b - 1));
+          } else if (aprev == a && anext == a && bnext == b && bprev == b) {
+            moveQueue.Enqueue(Tuple.Create(a, b - 1));
+          } else if (aprev == a && anext == a && bprev < b) {
+            moveQueue.Enqueue(Tuple.Create(a + 1, b));
+          } else if (anext > a && bnext > b) {
+            moveQueue.Enqueue(Tuple.Create(a, b - 1));
+          } else if (anext > a && bnext == b && bprev == b) {
+            moveQueue.Enqueue(Tuple.Create(a, b - 1));
+          } else /*if (anext > a && bprev < b)*/ {
+            moveQueue.Enqueue(Tuple.Create(a, bprev - 1));
+            moveQueue.Enqueue(Tuple.Create(bprev, b));
+          }
+
+          while (moveQueue.Count > 0) {
+            var m = moveQueue.Dequeue();
+            Opt(child, m.Item1, m.Item2);
+            undoStack.Push(m);
+          }
+          var moveF = eval(child, token);
+          evaluations++;
+          if (FitnessComparer.IsBetter(maximization, moveF, bestChange)) {
+            bestChange = moveF;
+            bestQueue = new Queue<Tuple<int, int>>(undoStack.Reverse());
+          }
+          // undo
+          while (undoStack.Count > 0) {
+            var m = undoStack.Pop();
+            Opt(child, m.Item1, m.Item2);
+          }
+        }
+        if (!double.IsNaN(bestChange)) {
+          while (bestQueue.Count > 0) {
+            var m = bestQueue.Dequeue();
+            Opt(child, m.Item1, m.Item2);
+            for (var i = m.Item1; i <= m.Item2; i++) invChild[child[i]] = i;
+          }
+          if (FitnessComparer.IsBetter(maximization, bestChange, best)) {
+            best = bestChange;
+            bestChild = (Encodings.PermutationEncoding.Permutation)child.Clone();
+          }
+        }
+      } while (!double.IsNaN(bestChange));
+
+      if (bestChild == null) {
+        best = eval(child, token);
+        evaluations++;
+      }
+      Context.IncrementEvaluatedSolutions(evaluations);
+      
+      if (VALIDATE && bestChild != null && !bestChild.Validate()) throw new ArgumentException("Relinking produced invalid child");
+      if (VALIDATE && Dist(child, p2) > 0) throw new InvalidOperationException("Child is not equal to p2 after relinking");
+      return bestChild ?? child;
+    }
+
+    public Encodings.PermutationEncoding.Permutation DelinkOpt(IRandom random, Encodings.PermutationEncoding.Permutation p1, Encodings.PermutationEncoding.Permutation p2, Func<Encodings.PermutationEncoding.Permutation, CancellationToken, double> eval, CancellationToken token, out double best) {
+      var evaluations = 0;
+      var child = (Encodings.PermutationEncoding.Permutation)p1.Clone();
+
+      best = double.NaN;
+      Encodings.PermutationEncoding.Permutation bestChild = null;
+
+      var invChild = new int[child.Length];
+      var invP2 = new int[child.Length];
+      for (var i = 0; i < child.Length; i++) {
+        invChild[child[i]] = i;
+        invP2[p2[i]] = i;
+      }
+
+      var order = Enumerable.Range(0, p2.Length).Where(x => IsUndirectedEdge(invP2, child[x], child.GetCircular(x + 1))).Shuffle(Context.Random).ToList();
+      while (order.Count > 0) {
+        var idx = order.First();
+        var bestChange = double.NaN;
+        var bestIdx = -1;
+        for (var m = 0; m < p2.Length; m++) {
+          if (Math.Abs(m - idx) <= 1 || Math.Abs(m - idx) >= p2.Length - 2) continue;
+          if (m < idx) {
+            if (IsUndirectedEdge(invP2, child.GetCircular(m - 1), child[idx])
+              || IsUndirectedEdge(invP2, child[m], child.GetCircular(idx + 1))) continue;
+            Opt(child, m, idx);
+            var moveF = eval(child, token);
+            evaluations++;
+            if (Context.IsBetter(moveF, bestChange)) {
+              bestChange = moveF;
+              bestIdx = m;
+            }
+            // undo
+            Opt(child, m, idx);
+          } else {
+            if (IsUndirectedEdge(invP2, child[idx], child[m])
+              || IsUndirectedEdge(invP2, child.GetCircular(idx + 1), child.GetCircular(m + 1))) continue;
+            Opt(child, idx + 1, m);
+            var moveF = eval(child, token);
+            evaluations++;
+            if (Context.IsBetter(moveF, bestChange)) {
+              bestChange = moveF;
+              bestIdx = m;
+            }
+            // undo
+            Opt(child, idx + 1, m);
+          }
+        }
+        if (bestIdx >= 0) {
+          if (bestIdx > idx)
+            Opt(child, idx + 1, bestIdx);
+          else Opt(child, bestIdx, idx);
+          for (var i = Math.Min(idx, bestIdx); i <= Math.Max(idx, bestIdx); i++)
+            invChild[child[i]] = i;
+
+          order = Enumerable.Range(0, p2.Length).Where(x => IsUndirectedEdge(invP2, child[x], child.GetCircular(x + 1))).Shuffle(Context.Random).ToList();
+          if (Context.IsBetter(bestChange, best)) {
+            best = bestChange;
+            bestChild = (Encodings.PermutationEncoding.Permutation)child.Clone();
+          }
+        }
+      }
+
+      if (bestChild == null) {
+        best = eval(child, token);
+        evaluations++;
+      }
+      Context.IncrementEvaluatedSolutions(evaluations);
+
+      if (VALIDATE && bestChild != null && !bestChild.Validate()) throw new ArgumentException("Delinking produced invalid child");
+      if (VALIDATE && Dist(child, p2) < 1) throw new InvalidOperationException("Child is not different from p2 after delinking");
+      return bestChild ?? child;
+    }
+
+    private static bool IsUndirectedEdge(int[] invP, int a, int b) {
+      var d = Math.Abs(invP[a] - invP[b]);
+      return d == 1 || d == invP.Length - 1;
+    }
+
+    private static void Swap(Encodings.PermutationEncoding.Permutation child, int from, int to) {
+      Swap2Manipulator.Apply(child, from, to);
+    }
+
+    private static void Shift(Encodings.PermutationEncoding.Permutation child, int from, int to) {
+      TranslocationManipulator.Apply(child, from, from, to);
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    private static void Opt(Encodings.PermutationEncoding.Permutation child, int from, int to) {
+      if (from > to) child.Reverse(to, from - to + 1);
+      else child.Reverse(from, to - from + 1);
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/PermutationMemPRContext.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/PermutationMemPRContext.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/PermutationMemPRContext.cs	(revision 14666)
@@ -0,0 +1,90 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Runtime.Remoting.Contexts;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.PermutationEncoding;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Algorithms.MemPR.Permutation {
+  [Item("MemPR Population Context (permutation)", "MemPR population context for permutation encoded problems.")]
+  [StorableClass]
+  public sealed class PermutationMemPRPopulationContext : MemPRPopulationContext<ISingleObjectiveHeuristicOptimizationProblem, Encodings.PermutationEncoding.Permutation, PermutationMemPRPopulationContext, PermutationMemPRSolutionContext> {
+
+    [StorableConstructor]
+    private PermutationMemPRPopulationContext(bool deserializing) : base(deserializing) { }
+    private PermutationMemPRPopulationContext(PermutationMemPRPopulationContext original, Cloner cloner)
+      : base(original, cloner) { }
+    public PermutationMemPRPopulationContext() : base("PermutationMemPRPopulationContext") { }
+    public PermutationMemPRPopulationContext(string name) : base(name) { }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new PermutationMemPRPopulationContext(this, cloner);
+    }
+
+    public override PermutationMemPRSolutionContext CreateSingleSolutionContext(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> solution) {
+      return new PermutationMemPRSolutionContext(this, solution);
+    }
+
+    public override ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> ToScope(Encodings.PermutationEncoding.Permutation code, double fitness = double.NaN) {
+      var creator = Problem.SolutionCreator as IPermutationCreator;
+      if (creator == null) throw new InvalidOperationException("Can only solve binary encoded problems with MemPR (binary)");
+      return new SingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation>(code, creator.PermutationParameter.ActualName, fitness, Problem.Evaluator.QualityParameter.ActualName) {
+        Parent = Scope
+      };
+    }
+  }
+
+  [Item("MemPR Solution Context (permutation)", "MemPR solution context for permutation encoded problems.")]
+  [StorableClass]
+  public sealed class PermutationMemPRSolutionContext : MemPRSolutionContext<ISingleObjectiveHeuristicOptimizationProblem, Encodings.PermutationEncoding.Permutation, PermutationMemPRPopulationContext, PermutationMemPRSolutionContext>, IPermutationSubspaceContext {
+
+    [Storable]
+    private IValueParameter<PermutationSolutionSubspace> subspace;
+    public PermutationSolutionSubspace Subspace {
+      get { return subspace.Value; }
+    }
+    ISolutionSubspace<Encodings.PermutationEncoding.Permutation> ISolutionSubspaceContext<Encodings.PermutationEncoding.Permutation>.Subspace {
+      get { return Subspace; }
+    }
+
+    [StorableConstructor]
+    private PermutationMemPRSolutionContext(bool deserializing) : base(deserializing) { }
+    private PermutationMemPRSolutionContext(PermutationMemPRSolutionContext original, Cloner cloner)
+      : base(original, cloner) {
+
+    }
+    public PermutationMemPRSolutionContext(PermutationMemPRPopulationContext baseContext, ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> solution)
+      : base(baseContext, solution) {
+
+      Parameters.Add(subspace = new ValueParameter<PermutationSolutionSubspace>("Subspace", new PermutationSolutionSubspace(null)));
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new PermutationMemPRSolutionContext(this, cloner);
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/PermutationSolutionSubspace.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/PermutationSolutionSubspace.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/PermutationSolutionSubspace.cs	(revision 14666)
@@ -0,0 +1,50 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Algorithms.MemPR.Permutation {
+  [Item("Solution subspace (Permutation)", "")]
+  [StorableClass]
+  public sealed class PermutationSolutionSubspace : Item, ISolutionSubspace<Encodings.PermutationEncoding.Permutation> {
+
+    [Storable]
+    private bool[,] subspace;
+    public bool[,] Subspace { get { return subspace; } }
+
+    [StorableConstructor]
+    private PermutationSolutionSubspace(bool deserializing) : base(deserializing) { }
+    private PermutationSolutionSubspace(PermutationSolutionSubspace original, Cloner cloner)
+      : base(original, cloner) {
+      subspace = (bool[,])original.subspace.Clone();
+    }
+    public PermutationSolutionSubspace(bool[,] subspace) {
+      this.subspace = subspace;
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new PermutationSolutionSubspace(this, cloner);
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/SolutionModel/Univariate/BiasedModelTrainer.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/SolutionModel/Univariate/BiasedModelTrainer.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/SolutionModel/Univariate/BiasedModelTrainer.cs	(revision 14666)
@@ -0,0 +1,65 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Linq;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Algorithms.MemPR.Permutation.SolutionModel.Univariate {
+  [Item("Biased Univariate Model Trainer (Permutation)", "", ExcludeGenericTypeInfo = true)]
+  [StorableClass]
+  public class BiasedModelTrainer<TContext> : ParameterizedNamedItem, ISolutionModelTrainer<TContext>
+    where TContext : IPopulationBasedHeuristicAlgorithmContext<ISingleObjectiveHeuristicOptimizationProblem, Encodings.PermutationEncoding.Permutation>,
+    ISolutionModelContext<Encodings.PermutationEncoding.Permutation>, IEvaluationServiceContext<Encodings.PermutationEncoding.Permutation> {
+
+    public bool Bias { get { return true; } }
+
+    [Storable]
+    private IValueParameter<EnumValue<ModelBiasOptions>> modelBiasParameter;
+    public ModelBiasOptions ModelBias {
+      get { return modelBiasParameter.Value.Value; }
+      set { modelBiasParameter.Value.Value = value; }
+    }
+
+    [StorableConstructor]
+    protected BiasedModelTrainer(bool deserializing) : base(deserializing) { }
+    protected BiasedModelTrainer(BiasedModelTrainer<TContext> original, Cloner cloner)
+      : base(original, cloner) {
+      modelBiasParameter = cloner.Clone(original.modelBiasParameter);
+    }
+    public BiasedModelTrainer() {
+      Parameters.Add(modelBiasParameter = new ValueParameter<EnumValue<ModelBiasOptions>>("Model Bias", "What kind of bias towards better individuals is chosen."));
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new BiasedModelTrainer<TContext>(this, cloner);
+    }
+
+    public void TrainModel(TContext context) {
+      context.Model = Trainer.TrainBiased(ModelBias, context.Random, context.Maximization, context.Population.Select(x => x.Solution).ToList(), context.Population.Select(x => x.Fitness).ToList(), context.Population.First().Solution.Length);
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/SolutionModel/Univariate/StaticAPI/Trainer.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/SolutionModel/Univariate/StaticAPI/Trainer.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/SolutionModel/Univariate/StaticAPI/Trainer.cs	(revision 14666)
@@ -0,0 +1,79 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.PermutationEncoding;
+
+namespace HeuristicLab.Algorithms.MemPR.Permutation.SolutionModel.Univariate {
+  public enum ModelBiasOptions { Rank, Fitness }
+
+  public static class Trainer {
+    public static ISolutionModel<Encodings.PermutationEncoding.Permutation> TrainUnbiased(IRandom random, IList<Encodings.PermutationEncoding.Permutation> pop, int N) {
+      ISolutionModel<Encodings.PermutationEncoding.Permutation> model;
+      switch (pop[0].PermutationType) {
+        case PermutationTypes.Absolute:
+          model = UnivariateAbsoluteModel.CreateUnbiased(random, pop, N);
+          break;
+        case PermutationTypes.RelativeDirected:
+          model = UnivariateRelativeModel.CreateDirected(random, pop, N);
+          break;
+        case PermutationTypes.RelativeUndirected:
+          model = UnivariateRelativeModel.CreateUndirected(random, pop, N);
+          break;
+        default: throw new ArgumentException(string.Format("unknown permutation type {0}", pop[0].PermutationType));
+      }
+      return model;
+    }
+
+    public static ISolutionModel<Encodings.PermutationEncoding.Permutation> TrainBiased(ModelBiasOptions modelBias, IRandom random, bool maximization, IList<Encodings.PermutationEncoding.Permutation> pop, IList<double> qualities, int N) {
+      if (pop.Count != qualities.Count) throw new ArgumentException("Unequal length of population and qualities list.");
+      ISolutionModel<Encodings.PermutationEncoding.Permutation> model;
+      switch (pop[0].PermutationType) {
+        case PermutationTypes.Absolute:
+          if (modelBias == ModelBiasOptions.Rank)
+            model = UnivariateAbsoluteModel.CreateWithRankBias(random, maximization, pop, qualities, N);
+          else if (modelBias == ModelBiasOptions.Fitness)
+            model = UnivariateAbsoluteModel.CreateWithFitnessBias(random, maximization, pop, qualities, N);
+          else throw new ArgumentException(string.Format("Bias type {0} is not supported.", modelBias));
+          break;
+        case PermutationTypes.RelativeDirected:
+          if (modelBias == ModelBiasOptions.Rank)
+            model = UnivariateRelativeModel.CreateDirectedWithRankBias(random, maximization, pop, qualities, N);
+          else if (modelBias == ModelBiasOptions.Fitness)
+            model = UnivariateRelativeModel.CreateDirectedWithFitnessBias(random, maximization, pop, qualities, N);
+          else throw new ArgumentException(string.Format("Bias type {0} is not supported.", modelBias));
+          break;
+        case PermutationTypes.RelativeUndirected:
+          if (modelBias == ModelBiasOptions.Rank)
+            model = UnivariateRelativeModel.CreateUndirectedWithRankBias(random, maximization, pop, qualities, N);
+          else if (modelBias == ModelBiasOptions.Fitness)
+            model = UnivariateRelativeModel.CreateUndirectedWithFitnessBias(random, maximization, pop, qualities, N);
+          else throw new ArgumentException(string.Format("Bias type {0} is not supported.", modelBias));
+          break;
+        default: throw new ArgumentException(string.Format("unknown permutation type {0}", pop[0].PermutationType));
+      }
+      return model;
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/SolutionModel/Univariate/UnbiasedModelTrainer.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/SolutionModel/Univariate/UnbiasedModelTrainer.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/SolutionModel/Univariate/UnbiasedModelTrainer.cs	(revision 14666)
@@ -0,0 +1,54 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Linq;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Optimization;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Algorithms.MemPR.Permutation.SolutionModel.Univariate {
+  [Item("Unbiased Univariate Model Trainer (Permutation)", "", ExcludeGenericTypeInfo = true)]
+  [StorableClass]
+  public class UnbiasedModelTrainer<TContext> : NamedItem, ISolutionModelTrainer<TContext>
+    where TContext : IPopulationBasedHeuristicAlgorithmContext<ISingleObjectiveHeuristicOptimizationProblem, Encodings.PermutationEncoding.Permutation>,
+    ISolutionModelContext<Encodings.PermutationEncoding.Permutation> {
+
+    public bool Bias { get { return false; } }
+
+    [StorableConstructor]
+    protected UnbiasedModelTrainer(bool deserializing) : base(deserializing) { }
+    protected UnbiasedModelTrainer(UnbiasedModelTrainer<TContext> original, Cloner cloner) : base(original, cloner) { }
+    public UnbiasedModelTrainer() {
+      Name = ItemName;
+      Description = ItemDescription;
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new UnbiasedModelTrainer<TContext>(this, cloner);
+    }
+
+    public void TrainModel(TContext context) {
+      context.Model = Trainer.TrainUnbiased(context.Random, context.Population.Select(x => x.Solution).ToList(), context.Population.First().Solution.Length);
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/SolutionModel/Univariate/UnivariateAbsoluteModel.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/SolutionModel/Univariate/UnivariateAbsoluteModel.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/SolutionModel/Univariate/UnivariateAbsoluteModel.cs	(revision 14666)
@@ -0,0 +1,137 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Encodings.PermutationEncoding;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Random;
+
+namespace HeuristicLab.Algorithms.MemPR.Permutation.SolutionModel.Univariate {
+  [Item("Univariate solution model (Permutation.Absolute)", "")]
+  [StorableClass]
+  public sealed class UnivariateAbsoluteModel : Item, ISolutionModel<Encodings.PermutationEncoding.Permutation> {
+    [Storable]
+    public DoubleMatrix Probabilities { get; set; }
+    [Storable]
+    public IRandom Random { get; set; }
+
+    [StorableConstructor]
+    private UnivariateAbsoluteModel(bool deserializing) : base(deserializing) { }
+    private UnivariateAbsoluteModel(UnivariateAbsoluteModel original, Cloner cloner)
+      : base(original, cloner) {
+      Probabilities = cloner.Clone(original.Probabilities);
+      Random = cloner.Clone(original.Random);
+    }
+    public UnivariateAbsoluteModel(IRandom random, double[,] probabilities) {
+      Probabilities = new DoubleMatrix(probabilities);
+      Random = random;
+    }
+    public UnivariateAbsoluteModel(IRandom random, DoubleMatrix probabilties) {
+      Probabilities = probabilties;
+      Random = random;
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new UnivariateAbsoluteModel(this, cloner);
+    }
+
+    public Encodings.PermutationEncoding.Permutation Sample() {
+      var N = Probabilities.Rows;
+      var child = new Encodings.PermutationEncoding.Permutation(PermutationTypes.Absolute, N);
+      var indices = Enumerable.Range(0, N).Shuffle(Random).ToList();
+      var values = Enumerable.Range(0, N).Shuffle(Random).ToList();
+      for (var i = N - 1; i > 0; i--) {
+        var nextIndex = indices[i];
+        var ball = Random.NextDouble();
+        for (var v = 0; v < values.Count; v++) {
+          ball -= Probabilities[nextIndex, values[v]] + 1.0 / N;
+          if (ball > 0.0) continue;
+          child[nextIndex] = values[v];
+          values.RemoveAt(v);
+          indices.RemoveAt(i);
+          break;
+        }
+        if (ball > 0) {
+          var v = values.Count - 1;
+          child[nextIndex] = values[v];
+          values.RemoveAt(v);
+          indices.RemoveAt(i);
+        }
+      }
+      child[indices[0]] = values[0];
+      return child;
+    }
+
+    public static UnivariateAbsoluteModel CreateUnbiased(IRandom random, IList<Encodings.PermutationEncoding.Permutation> pop, int N) {
+      var model = new double[N, N];
+      var factor = 1.0 / pop.Count;
+      for (var i = 0; i < pop.Count; i++) {
+        for (var j = 0; j < N; j++) {
+          model[j, pop[i][j]] += factor;
+        }
+      }
+      return new UnivariateAbsoluteModel(random, model);
+    }
+
+    public static UnivariateAbsoluteModel CreateWithRankBias(IRandom random, bool maximization, IList<Encodings.PermutationEncoding.Permutation> population, IEnumerable<double> qualities, int N) {
+      var popSize = 0;
+      var model = new double[N, N];
+
+      var pop = population.Zip(qualities, (b, q) => new { Solution = b, Fitness = q });
+      foreach (var ind in maximization ? pop.OrderBy(x => x.Fitness) : pop.OrderByDescending(x => x.Fitness)) {
+        // from worst to best, worst solution has 1 vote, best solution N votes
+        popSize++;
+        for (var j = 0; j < N; j++) {
+          model[j, ind.Solution[j]] += popSize;
+        }
+      }
+      // normalize to [0;1]
+      var factor = 2.0 / (popSize + 1);
+      for (var i = 0; i < N; i++) {
+        for (var j = 0; j < N; j++)
+          model[i, j] *= factor / popSize;
+      }
+      if (popSize == 0) throw new ArgumentException("Cannot train model from empty population.");
+      return new UnivariateAbsoluteModel(random, model);
+    }
+
+    public static UnivariateAbsoluteModel CreateWithFitnessBias(IRandom random, bool maximization, IList<Encodings.PermutationEncoding.Permutation> population, IEnumerable<double> qualities, int N) {
+      var proportions = Util.Auxiliary.PrepareProportional(qualities, true, !maximization);
+      var factor = 1.0 / proportions.Sum();
+      var model = new double[N, N];
+
+      foreach (var ind in population.Zip(proportions, (p, q) => new { Solution = p, Proportion = q })) {
+        for (var x = 0; x < model.Length; x++) {
+          for (var j = 0; j < N; j++) {
+            model[j, ind.Solution[j]] += ind.Proportion * factor;
+          }
+        }
+      }
+      return new UnivariateAbsoluteModel(random, model);
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/SolutionModel/Univariate/UnivariateRelativeModel.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/SolutionModel/Univariate/UnivariateRelativeModel.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Permutation/SolutionModel/Univariate/UnivariateRelativeModel.cs	(revision 14666)
@@ -0,0 +1,201 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Encodings.PermutationEncoding;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Random;
+
+namespace HeuristicLab.Algorithms.MemPR.Permutation.SolutionModel.Univariate {
+  [Item("Univariate solution model (Permutation.Relative)", "")]
+  [StorableClass]
+  public sealed class UnivariateRelativeModel : Item, ISolutionModel<Encodings.PermutationEncoding.Permutation> {
+    [Storable]
+    public DoubleMatrix Probabilities { get; set; }
+
+    [Storable]
+    public IRandom Random { get; set; }
+
+    [Storable]
+    public PermutationTypes PermutationType { get; set; }
+
+    [StorableConstructor]
+    private UnivariateRelativeModel(bool deserializing) : base(deserializing) { }
+    private UnivariateRelativeModel(UnivariateRelativeModel original, Cloner cloner)
+      : base(original, cloner) {
+      Probabilities = cloner.Clone(original.Probabilities);
+      Random = cloner.Clone(original.Random);
+      PermutationType = original.PermutationType;
+    }
+    public UnivariateRelativeModel(IRandom random, double[,] probabilities, PermutationTypes permutationType) {
+      Probabilities = new DoubleMatrix(probabilities);
+      Random = random;
+      PermutationType = permutationType;
+    }
+    public UnivariateRelativeModel(IRandom random, DoubleMatrix probabilties, PermutationTypes permutationType) {
+      Probabilities = probabilties;
+      Random = random;
+      PermutationType = permutationType;
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new UnivariateRelativeModel(this, cloner);
+    }
+
+    public Encodings.PermutationEncoding.Permutation Sample() {
+      var N = Probabilities.Rows;
+      var next = Random.Next(N);
+      var child = new Encodings.PermutationEncoding.Permutation(PermutationType, N);
+      child[0] = next;
+      var open = Enumerable.Range(0, N).Where(x => x != next).Shuffle(Random).ToList();
+      for (var i = 1; i < N - 1; i++) {
+        var total = 0.0;
+        for (var j = 0; j < open.Count; j++) {
+          total += Probabilities[next, open[j]] + 1.0 / N;
+        }
+        var ball = Random.NextDouble() * total;
+        for (var j = 0; j < open.Count; j++) {
+          ball -= Probabilities[next, open[j]] + 1.0 / N;
+          if (ball <= 0.0) {
+            child[i] = open[j];
+            next = open[j];
+            open.RemoveAt(j);
+            break;
+          }
+        }
+      }
+      child[N - 1] = open[0];
+      return child;
+    }
+
+    public static UnivariateRelativeModel CreateDirected(IRandom random, IList<Encodings.PermutationEncoding.Permutation> pop, int N) {
+      var model = new double[N, N];
+      for (var i = 0; i < pop.Count; i++) {
+        for (var j = 0; j < N - 1; j++) {
+          for (var k = j + 1; k < N; k++) {
+            model[pop[i][j], pop[i][k]]++;
+          }
+          model[pop[i][N - 1], pop[i][0]]++;
+        }
+      }
+      return new UnivariateRelativeModel(random, model, PermutationTypes.RelativeDirected);
+    }
+
+    public static UnivariateRelativeModel CreateDirectedWithRankBias(IRandom random, bool maximization, IList<Encodings.PermutationEncoding.Permutation> population, IEnumerable<double> qualities, int N) {
+      var popSize = 0;
+      var model = new double[N, N];
+
+      var pop = population.Zip(qualities, (b, q) => new { Solution = b, Fitness = q });
+      foreach (var ind in maximization ? pop.OrderBy(x => x.Fitness) : pop.OrderByDescending(x => x.Fitness)) {
+        // from worst to best, worst solution has 1 vote, best solution N votes
+        popSize++;
+        for (var j = 0; j < N - 1; j++) {
+          for (var k = j + 1; k < N; k++) {
+            model[ind.Solution[j], ind.Solution[k]] += popSize;
+          }
+          model[ind.Solution[N - 1], ind.Solution[0]] += popSize;
+        }
+      }
+      if (popSize == 0) throw new ArgumentException("Cannot train model from empty population.");
+      return new UnivariateRelativeModel(random, model, PermutationTypes.RelativeDirected);
+    }
+
+    public static UnivariateRelativeModel CreateDirectedWithFitnessBias(IRandom random, bool maximization, IList<Encodings.PermutationEncoding.Permutation> population, IEnumerable<double> qualities, int N) {
+      var proportions = Util.Auxiliary.PrepareProportional(qualities, true, !maximization);
+      var factor = 1.0 / proportions.Sum();
+      var model = new double[N, N];
+
+      foreach (var ind in population.Zip(proportions, (p, q) => new { Solution = p, Proportion = q })) {
+        for (var x = 0; x < model.Length; x++) {
+          for (var j = 0; j < N - 1; j++) {
+            for (var k = j + 1; k < N; k++) {
+              model[ind.Solution[j], ind.Solution[k]] += ind.Proportion * factor;
+            }
+            model[ind.Solution[N - 1], ind.Solution[0]] += ind.Proportion * factor;
+          }
+        }
+      }
+      return new UnivariateRelativeModel(random, model, PermutationTypes.RelativeDirected);
+    }
+
+    public static UnivariateRelativeModel CreateUndirected(IRandom random, IList<Encodings.PermutationEncoding.Permutation> pop, int N) {
+      var model = new double[N, N];
+      for (var i = 0; i < pop.Count; i++) {
+        for (var j = 0; j < N - 1; j++) {
+          for (var k = j + 1; k < N; k++) {
+            model[pop[i][j], pop[i][k]]++;
+            model[pop[i][k], pop[i][j]]++;
+          }
+          model[pop[i][0], pop[i][N - 1]]++;
+          model[pop[i][N - 1], pop[i][0]]++;
+        }
+      }
+      return new UnivariateRelativeModel(random, model, PermutationTypes.RelativeUndirected);
+    }
+
+    public static UnivariateRelativeModel CreateUndirectedWithRankBias(IRandom random, bool maximization, IList<Encodings.PermutationEncoding.Permutation> population, IEnumerable<double> qualities, int N) {
+      var popSize = 0;
+      var model = new double[N, N];
+
+      var pop = population.Zip(qualities, (b, q) => new { Solution = b, Fitness = q });
+      foreach (var ind in maximization ? pop.OrderBy(x => x.Fitness) : pop.OrderByDescending(x => x.Fitness)) {
+        // from worst to best, worst solution has 1 vote, best solution N votes
+        popSize++;
+        for (var j = 0; j < N - 1; j++) {
+          for (var k = j + 1; k < N; k++) {
+            model[ind.Solution[j], ind.Solution[k]] += popSize;
+            model[ind.Solution[k], ind.Solution[j]] += popSize;
+          }
+          model[ind.Solution[0], ind.Solution[N - 1]] += popSize;
+          model[ind.Solution[N - 1], ind.Solution[0]] += popSize;
+        }
+      }
+      if (popSize == 0) throw new ArgumentException("Cannot train model from empty population.");
+      return new UnivariateRelativeModel(random, model, PermutationTypes.RelativeUndirected);
+    }
+
+    public static UnivariateRelativeModel CreateUndirectedWithFitnessBias(IRandom random, bool maximization, IList<Encodings.PermutationEncoding.Permutation> population, IEnumerable<double> qualities, int N) {
+      var proportions = Util.Auxiliary.PrepareProportional(qualities, true, !maximization);
+      var factor = 1.0 / proportions.Sum();
+      var model = new double[N, N];
+
+      foreach (var ind in population.Zip(proportions, (p, q) => new { Solution = p, Proportion = q })) {
+        for (var x = 0; x < model.Length; x++) {
+          for (var j = 0; j < N - 1; j++) {
+            for (var k = j + 1; k < N; k++) {
+              model[ind.Solution[j], ind.Solution[k]] += ind.Proportion * factor;
+              model[ind.Solution[k], ind.Solution[j]] += ind.Proportion * factor;
+            }
+            model[ind.Solution[0], ind.Solution[N - 1]] += ind.Proportion * factor;
+            model[ind.Solution[N - 1], ind.Solution[0]] += ind.Proportion * factor;
+          }
+        }
+      }
+      return new UnivariateRelativeModel(random, model, PermutationTypes.RelativeUndirected);
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Plugin.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Plugin.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Plugin.cs	(revision 14666)
@@ -0,0 +1,46 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using HeuristicLab.PluginInfrastructure;
+
+namespace HeuristicLab.Algorithms.MemPR {
+
+  [Plugin("HeuristicLab.Algorithms.MemPR", "Provides the MemPR (MEMetic Path Relinking) algorithm.", "3.3.14.0")]
+  [PluginFile("HeuristicLab.Algorithms.MemPR-3.3.dll", PluginFileType.Assembly)]
+  [PluginDependency("HeuristicLab.Algorithms.DataAnalysis", "3.4")]
+  [PluginDependency("HeuristicLab.Analysis", "3.3")]
+  [PluginDependency("HeuristicLab.Collections", "3.3")]
+  [PluginDependency("HeuristicLab.Common", "3.3")]
+  [PluginDependency("HeuristicLab.Common.Resources", "3.3")]
+  [PluginDependency("HeuristicLab.Core", "3.3")]
+  [PluginDependency("HeuristicLab.Data", "3.3")]
+  [PluginDependency("HeuristicLab.Encodings.BinaryVectorEncoding", "3.3")]
+  [PluginDependency("HeuristicLab.Encodings.LinearLinkageEncoding", "3.4")]
+  [PluginDependency("HeuristicLab.Encodings.PermutationEncoding", "3.3")]
+  [PluginDependency("HeuristicLab.Operators", "3.3")]
+  [PluginDependency("HeuristicLab.Optimization", "3.3")]
+  [PluginDependency("HeuristicLab.Parameters", "3.3")]
+  [PluginDependency("HeuristicLab.Persistence", "3.3")]
+  [PluginDependency("HeuristicLab.Random", "3.3")]
+  public class HeuristicLabAlgorithmsMemPRPlugin : PluginBase {
+  }
+}
+
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Properties/AssemblyInfo.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Properties/AssemblyInfo.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Properties/AssemblyInfo.cs	(revision 14666)
@@ -0,0 +1,36 @@
+﻿using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("HeuristicLab.Algorithms.MemPR")]
+[assembly: AssemblyDescription("Plugin that provides the MemPR (memetic path relinking) algorithm.")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("HeuristicLab")]
+[assembly: AssemblyCopyright("(c) 2002-2016 HEAL")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("9d274421-6332-4fbc-aae4-467ace27c368")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("3.3.0.0")]
+[assembly: AssemblyFileVersion("3.3.14.0")]
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/SingleObjectiveSolutionScope.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/SingleObjectiveSolutionScope.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/SingleObjectiveSolutionScope.cs	(revision 14666)
@@ -0,0 +1,143 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Drawing;
+using HeuristicLab.Algorithms.MemPR.Interfaces;
+using HeuristicLab.Collections;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Algorithms.MemPR {
+  [Item("Solution Scope", "Scope for an individual/solution of a single-objective single-encoded problem.")]
+  [StorableClass]
+  public sealed class SingleObjectiveSolutionScope<T> : NamedItem, ISingleObjectiveSolutionScope<T> where T : class, IItem {
+    public new static Image StaticItemImage {
+      get { return HeuristicLab.Common.Resources.VSImageLibrary.OrgChart; }
+    }
+
+    [Storable]
+    private IScope parent;
+    public IScope Parent {
+      get { return parent; }
+      set {
+        if (parent != value) {
+          parent = value;
+        }
+      }
+    }
+
+    [Storable]
+    private VariableCollection variables;
+    public VariableCollection Variables {
+      get { return variables; }
+    }
+
+    [Storable]
+    private ScopeList subScopes;
+    public ScopeList SubScopes {
+      get { return subScopes; }
+    }
+
+    [Storable]
+    private Variable solution;
+    public T Solution {
+      get { return (T)solution.Value; }
+      set { solution.Value = value; }
+    }
+
+    [Storable]
+    private Variable fitness;
+    public double Fitness {
+      get { return ((DoubleValue)fitness.Value).Value; }
+      set { ((DoubleValue)fitness.Value).Value = value; }
+    }
+
+    [StorableConstructor]
+    private SingleObjectiveSolutionScope(bool deserializing) : base(deserializing) { }
+    private SingleObjectiveSolutionScope(SingleObjectiveSolutionScope<T> original, Cloner cloner)
+      : base(original, cloner) {
+      // the parent will not be deep-cloned
+      parent = original.parent;
+      variables = cloner.Clone(original.variables);
+      subScopes = cloner.Clone(original.subScopes);
+      foreach (var child in SubScopes)
+        child.Parent = this;
+      solution = cloner.Clone(original.solution);
+      fitness = cloner.Clone(original.fitness);
+
+      RegisterSubScopesEvents();
+    }
+    public SingleObjectiveSolutionScope(T code, string codeName, double fitness, string fitnessName) {
+      this.solution = new Variable(codeName, code);
+      this.fitness = new Variable(fitnessName, new DoubleValue(fitness));
+      variables = new VariableCollection(2) { this.solution, this.fitness };
+      subScopes = new ScopeList(2);
+
+      RegisterSubScopesEvents();
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new SingleObjectiveSolutionScope<T>(this, cloner);
+    }
+
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      RegisterSubScopesEvents();
+    }
+
+    public void Clear() {
+      variables.Clear();
+      subScopes.Clear();
+    }
+
+    #region SubScopes Events
+    private void RegisterSubScopesEvents() {
+      if (subScopes != null) {
+        subScopes.ItemsAdded += SubScopes_ItemsAdded;
+        subScopes.ItemsRemoved += SubScopes_ItemsRemoved;
+        subScopes.ItemsReplaced += SubScopes_ItemsReplaced;
+        subScopes.CollectionReset += SubScopes_CollectionReset;
+      }
+    }
+    private void SubScopes_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<IScope>> e) {
+      foreach (var item in e.Items) item.Value.Parent = this;
+    }
+    private void SubScopes_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<IScope>> e) {
+      foreach (var item in e.Items) item.Value.Parent = null;
+    }
+    private void SubScopes_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<IScope>> e) {
+      foreach (var oldItem in e.OldItems) oldItem.Value.Parent = null;
+      foreach (var item in e.Items) item.Value.Parent = this;
+    }
+    private void SubScopes_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<IScope>> e) {
+      foreach (var oldItem in e.OldItems) oldItem.Value.Parent = null;
+      foreach (var item in e.Items) item.Value.Parent = this;
+    }
+    #endregion
+
+    public void Adopt(ISingleObjectiveSolutionScope<T> orphan) {
+      Solution = orphan.Solution;
+      Fitness = orphan.Fitness;
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Util/Auxiliary.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Util/Auxiliary.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Util/Auxiliary.cs	(revision 14666)
@@ -0,0 +1,62 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace HeuristicLab.Algorithms.MemPR.Util {
+  public static class Auxiliary {
+    public static double[] PrepareProportional(IEnumerable<double> weights, bool windowing, bool inverseProportional) {
+      double maxValue = double.MinValue, minValue = double.MaxValue;
+      double[] valueArray = weights.ToArray();
+
+      for (int i = 0; i < valueArray.Length; i++) {
+        if (valueArray[i] > maxValue) maxValue = valueArray[i];
+        if (valueArray[i] < minValue) minValue = valueArray[i];
+      }
+      if (minValue == maxValue) {  // all values are equal
+        for (int i = 0; i < valueArray.Length; i++) {
+          valueArray[i] = 1.0;
+        }
+      } else {
+        if (windowing) {
+          if (inverseProportional) InverseProportionalScale(valueArray, maxValue);
+          else ProportionalScale(valueArray, minValue);
+        } else {
+          if (minValue < 0.0) throw new InvalidOperationException("Proportional selection without windowing does not work with values < 0.");
+          if (inverseProportional) InverseProportionalScale(valueArray, 2 * maxValue);
+        }
+      }
+      return valueArray;
+    }
+    private static void ProportionalScale(double[] values, double minValue) {
+      for (int i = 0; i < values.Length; i++) {
+        values[i] = values[i] - minValue;
+      }
+    }
+    private static void InverseProportionalScale(double[] values, double maxValue) {
+      for (int i = 0; i < values.Length; i++) {
+        values[i] = maxValue - values[i];
+      }
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Util/CkMeans1D.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Util/CkMeans1D.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Util/CkMeans1D.cs	(revision 14666)
@@ -0,0 +1,109 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using HeuristicLab.Common;
+
+namespace HeuristicLab.Algorithms.MemPR.Util {
+  /// <summary>
+  /// Implements the Ckmeans.1d.dp method. It is described in the paper:
+  /// Haizhou Wang and Mingzhou Song. 2011.
+  /// Ckmeans.1d.dp: Optimal k-means Clustering in One Dimension by Dynamic Programming
+  /// The R Journal Vol. 3/2, pp. 29-33.
+  /// available at https://journal.r-project.org/archive/2011-2/RJournal_2011-2_Wang+Song.pdf
+  /// </summary>
+  public class CkMeans1D {
+    /// <summary>
+    /// Clusters the 1-dimensional data given in <paramref name="estimations"/>.
+    /// </summary>
+    /// <param name="estimations">The 1-dimensional data that should be clustered.</param>
+    /// <param name="k">The maximum number of clusters.</param>
+    /// <param name="clusterValues">A vector of the same length as estimations that assigns to each point a cluster id.</param>
+    /// <returns>A sorted list of cluster centroids and corresponding cluster ids.</returns>
+    public static SortedList<double, int> Cluster(double[] estimations, int k, out int[] clusterValues) {
+      int nPoints = estimations.Length;
+      var distinct = estimations.Distinct().OrderBy(x => x).ToArray();
+      var max = distinct.Max();
+      if (distinct.Length <= k) {
+        var dict = distinct.Select((v, i) => new { Index = i, Value = v }).ToDictionary(x => x.Value, y => y.Index);
+        for (int i = distinct.Length; i < k; i++)
+          dict.Add(max + i - distinct.Length + 1, i);
+
+        clusterValues = new int[nPoints];
+        for (int i = 0; i < nPoints; i++)
+          if (!dict.ContainsKey(estimations[i])) clusterValues[i] = 0;
+          else clusterValues[i] = dict[estimations[i]];
+
+        return new SortedList<double, int>(dict);
+      }
+
+      var n = distinct.Length;
+      var D = new double[n, k];
+      var B = new int[n, k];
+
+      for (int m = 0; m < k; m++) {
+        for (int j = m; j <= n - k + m; j++) {
+          if (m == 0)
+            D[j, m] = SumOfSquaredDistances(distinct, 0, j + 1);
+          else {
+            var minD = double.MaxValue;
+            var minI = 0;
+            for (int i = 1; i <= j; i++) {
+              var d = D[i - 1, m - 1] + SumOfSquaredDistances(distinct, i, j + 1);
+              if (d < minD) {
+                minD = d;
+                minI = i;
+              }
+            }
+            D[j, m] = minD;
+            B[j, m] = minI;
+          }
+        }
+      }
+
+      var centers = new SortedList<double, int>();
+      var upper = B[n - 1, k - 1];
+      var c = Mean(distinct, upper, n);
+      centers.Add(c, k - 1);
+      for (int i = k - 2; i >= 0; i--) {
+        var lower = B[upper - 1, i];
+        var c2 = Mean(distinct, lower, upper);
+        centers.Add(c2, i);
+        upper = lower;
+      }
+
+      clusterValues = new int[nPoints];
+      for (int i = 0; i < estimations.Length; i++) {
+        clusterValues[i] = centers.MinItems(x => Math.Abs(estimations[i] - x.Key)).First().Value;
+      }
+
+      return centers;
+    }
+
+    private static double SumOfSquaredDistances(double[] x, int start, int end) {
+      if (start == end) throw new InvalidOperationException();
+      if (start + 1 == end) return 0.0;
+      double mean = 0.0;
+      for (int i = start; i < end; i++) {
+        mean += x[i];
+      }
+      mean /= (end - start);
+      var sum = 0.0;
+      for (int i = start; i < end; i++) {
+        sum += (x[i] - mean) * (x[i] - mean);
+      }
+      return sum;
+    }
+
+    private static double Mean(double[] x, int start, int end) {
+      if (start == end) throw new InvalidOperationException();
+      double mean = 0.0;
+      for (int i = start; i < end; i++) {
+        mean += x[i];
+      }
+      mean /= (end - start);
+      return mean;
+    }
+  }
+}
Index: branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Util/FitnessComparer.cs
===================================================================
--- branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Util/FitnessComparer.cs	(revision 14666)
+++ branches/PerformanceComparison/HeuristicLab.Algorithms.MemPR/3.3/Util/FitnessComparer.cs	(revision 14666)
@@ -0,0 +1,30 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+namespace HeuristicLab.Algorithms.MemPR.Util {
+  public static class FitnessComparer {
+    public static bool IsBetter(bool maximization, double a, double b) {
+      return double.IsNaN(b) && !double.IsNaN(a)
+        || maximization && a > b
+        || !maximization && a < b;
+    }
+  }
+}
Index: branches/PerformanceComparison/PerformanceComparison.sln
===================================================================
--- branches/PerformanceComparison/PerformanceComparison.sln	(revision 14665)
+++ branches/PerformanceComparison/PerformanceComparison.sln	(revision 14666)
@@ -1,6 +1,6 @@
 ﻿
 Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2013
-VisualStudioVersion = 12.0.40629.0
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeuristicLab.Analysis-3.3", "HeuristicLab.Analysis\3.3\HeuristicLab.Analysis-3.3.csproj", "{887425B4-4348-49ED-A457-B7D2C26DDBF9}"
@@ -19,4 +19,6 @@
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeuristicLab.Encodings.PermutationEncoding-3.3", "HeuristicLab.Encodings.PermutationEncoding\3.3\HeuristicLab.Encodings.PermutationEncoding-3.3.csproj", "{DBECB8B0-B166-4133-BAF1-ED67C3FD7FCA}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeuristicLab.Algorithms.MemPR-3.3", "HeuristicLab.Algorithms.MemPR\3.3\HeuristicLab.Algorithms.MemPR-3.3.csproj", "{9D274421-6332-4FBC-AAE4-467ACE27C368}"
 EndProject
 Global
@@ -126,4 +128,16 @@
 		{DBECB8B0-B166-4133-BAF1-ED67C3FD7FCA}.Release|x86.ActiveCfg = Release|x86
 		{DBECB8B0-B166-4133-BAF1-ED67C3FD7FCA}.Release|x86.Build.0 = Release|x86
+		{9D274421-6332-4FBC-AAE4-467ACE27C368}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{9D274421-6332-4FBC-AAE4-467ACE27C368}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{9D274421-6332-4FBC-AAE4-467ACE27C368}.Debug|x64.ActiveCfg = Debug|x64
+		{9D274421-6332-4FBC-AAE4-467ACE27C368}.Debug|x64.Build.0 = Debug|x64
+		{9D274421-6332-4FBC-AAE4-467ACE27C368}.Debug|x86.ActiveCfg = Debug|x86
+		{9D274421-6332-4FBC-AAE4-467ACE27C368}.Debug|x86.Build.0 = Debug|x86
+		{9D274421-6332-4FBC-AAE4-467ACE27C368}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{9D274421-6332-4FBC-AAE4-467ACE27C368}.Release|Any CPU.Build.0 = Release|Any CPU
+		{9D274421-6332-4FBC-AAE4-467ACE27C368}.Release|x64.ActiveCfg = Release|x64
+		{9D274421-6332-4FBC-AAE4-467ACE27C368}.Release|x64.Build.0 = Release|x64
+		{9D274421-6332-4FBC-AAE4-467ACE27C368}.Release|x86.ActiveCfg = Release|x86
+		{9D274421-6332-4FBC-AAE4-467ACE27C368}.Release|x86.Build.0 = Release|x86
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
