Index: branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/Binary/BinaryMemPR.cs
===================================================================
--- branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/Binary/BinaryMemPR.cs	(revision 14549)
+++ branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/Binary/BinaryMemPR.cs	(revision 14550)
@@ -54,10 +54,8 @@
     }
 
-    protected override bool Eq(ISingleObjectiveSolutionScope<BinaryVector> a, ISingleObjectiveSolutionScope<BinaryVector> b) {
-      var len = a.Solution.Length;
-      var acode = a.Solution;
-      var bcode = b.Solution;
+    protected override bool Eq(BinaryVector a, BinaryVector b) {
+      var len = a.Length;
       for (var i = 0; i < len; i++)
-        if (acode[i] != bcode[i]) return false;
+        if (a[i] != b[i]) return false;
       return true;
     }
@@ -105,17 +103,15 @@
       var order = Enumerable.Range(0, N).Where(x => subset == null || subset[x]).Shuffle(Context.Random).ToArray();
 
-      var max = Context.Population.Max(x => x.Fitness);
-      var min = Context.Population.Min(x => x.Fitness);
-      var range = (max - min);
-      if (range.IsAlmost(0)) range = Math.Abs(max * 0.05);
-      //else range += range * 0.05;
-      if (range.IsAlmost(0)) { // because min = max = 0
+      var bound = Problem.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 upperBound = Problem.Maximization ? min - range : max + range;
-      //var lowerBound = Problem.Maximization ? max : min;
-      var temp = 1.0;
+      
+      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;
@@ -131,9 +127,13 @@
           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 = Problem.Maximization ? after - before : before - after;
           if (diff > 0) moved = true;
           else {
-            var prob = Math.Exp(temp * diff / range);
+            var prob = Math.Exp(diff / temp);
             if (Context.Random.NextDouble() >= prob) {
               // the move is not good enough -> undo the move
@@ -142,5 +142,5 @@
             }
           }
-          temp += 100.0 / maxEvals;
+          temp *= annealFactor;
           if (evaluations >= maxEvals) break;
         }
@@ -167,10 +167,14 @@
                                         : Math.Min(p1.Fitness, p2.Fitness);
 
-      var offspring = ToScope(null);
+
+      var cache = new HashSet<BinaryVector>(new BinaryVectorEqualityComparer());
+      cache.Add(p1.Solution);
+      cache.Add(p2.Solution);
+
+      ISingleObjectiveSolutionScope<BinaryVector> offspring = null;
       var probe = ToScope(new BinaryVector(N));
-      var order = Enumerable.Range(0, N - 1).Shuffle(Context.Random).ToList();
-      for (var x = 0; x < N - 1; x++) {
-        var b = order[x];
-        if (p1.Solution[b] == p2.Solution[b]) continue;
+      // first try all possible combinations of 1-point crossover
+      /*var order = Enumerable.Range(1, N - 2).Where(x => p1.Solution[x] != p2.Solution[x]).Shuffle(Context.Random).ToList();
+      foreach (var b in order) {
         for (var i = 0; i <= b; i++)
           probe.Solution[i] = p1.Solution[i];
@@ -178,7 +182,11 @@
           probe.Solution[i] = p2.Solution[i];
 
+        // only add to cache, because all solutions must be unique
+        if (cache.Contains(probe.Solution)) continue;
+        cache.Add(probe.Solution);
         Evaluate(probe, token);
         evaluations++;
-        if (Context.IsBetter(probe, offspring)) {
+        if (offspring == null || Context.IsBetter(probe, offspring)) {
+          // immediately return upon finding a better offspring than better parent
           if (Context.IsBetter(probe.Fitness, better)) {
             Context.IncrementEvaluatedSolutions(evaluations);
@@ -187,11 +195,19 @@
           offspring = (ISingleObjectiveSolutionScope<BinaryVector>)probe.Clone();
         }
-      }
-
-      while (evaluations < Context.LocalSearchEvaluations) {
+      }*/
+
+      var cacheHits = 0;
+      // if we got some evaluations left, try uniform crossover
+      while (evaluations < Math.Min(Context.LocalSearchEvaluations, N)) {
         probe.Solution = UniformCrossover.Apply(Context.Random, p1.Solution, p2.Solution);
+        if (cache.Contains(probe.Solution)) {
+          cacheHits++;
+          if (cacheHits > 10) break; // variability of uniform crossover is too low -> parents are too similar
+          continue;
+        } else cache.Add(probe.Solution);
         Evaluate(probe, token);
         evaluations++;
-        if (Context.IsBetter(probe, offspring)) {
+        if (offspring == null || Context.IsBetter(probe, offspring)) {
+          // immediately return upon finding a better offspring than better parent
           if (Context.IsBetter(probe.Fitness, better)) {
             Context.IncrementEvaluatedSolutions(evaluations);
@@ -202,5 +218,6 @@
       }
       Context.IncrementEvaluatedSolutions(evaluations);
-      return offspring;
+      // return best offspring found
+      return offspring ?? probe;
     }
 
Index: branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LinearLinkageMemPR.cs
===================================================================
--- branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LinearLinkageMemPR.cs	(revision 14549)
+++ branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/LinearLinkage/LinearLinkageMemPR.cs	(revision 14550)
@@ -32,5 +32,4 @@
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
 using HeuristicLab.PluginInfrastructure;
-using HeuristicLab.Random;
 
 namespace HeuristicLab.Algorithms.MemPR.Grouping {
@@ -55,10 +54,8 @@
     }
 
-    protected override bool Eq(ISingleObjectiveSolutionScope<LinearLinkage> a, ISingleObjectiveSolutionScope<LinearLinkage> b) {
-      var s1 = a.Solution;
-      var s2 = b.Solution;
-      if (s1.Length != s2.Length) return false;
-      for (var i = 0; i < s1.Length; i++)
-        if (s1[i] != s2[i]) return false;
+    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;
     }
@@ -256,5 +253,5 @@
       var evaluations = 1;
       ISingleObjectiveSolutionScope<LinearLinkage> offspring = null;
-      for (; evaluations < Context.LocalSearchEvaluations; evaluations++) {
+      for (; evaluations < p1Scope.Solution.Length; evaluations++) {
         var code = GroupCrossover.Apply(Context.Random, p1Scope.Solution, p2Scope.Solution);
         if (cache.Contains(code)) {
@@ -297,4 +294,6 @@
           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;
Index: branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/MemPRAlgorithm.cs
===================================================================
--- branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/MemPRAlgorithm.cs	(revision 14549)
+++ branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/MemPRAlgorithm.cs	(revision 14550)
@@ -230,4 +230,5 @@
           var child = Create(token);
           Context.LocalSearchEvaluations += HillClimb(child, token);
+          Context.LocalOptimaLevel += child.Fitness;
           Context.AddToPopulation(child);
           Context.BestQuality = child.Fitness;
@@ -237,4 +238,5 @@
         }
         Context.LocalSearchEvaluations /= 2;
+        Context.LocalOptimaLevel /= 2;
         Context.Initialized = true;
       }
@@ -254,5 +256,5 @@
       if (offspring != null) {
         if (Context.PopulationCount < MaximumPopulationSize)
-          HillClimb(offspring, token);
+          AdaptiveClimb(offspring, Context.LocalSearchEvaluations, token);
         var replNew = Replace(offspring, token);
         if (replNew) {
@@ -265,5 +267,5 @@
       if (offspring != null) {
         if (Context.PopulationCount < MaximumPopulationSize)
-          HillClimb(offspring, token);
+          AdaptiveClimb(offspring, Context.LocalSearchEvaluations, token);
         if (Replace(offspring, token)) {
           replaced = true;
@@ -275,5 +277,5 @@
       if (offspring != null) {
         if (Context.PopulationCount < MaximumPopulationSize)
-          HillClimb(offspring, token);
+          AdaptiveClimb(offspring, Context.LocalSearchEvaluations, token);
         if (Replace(offspring, token)) {
           replaced = true;
@@ -285,5 +287,5 @@
       if (offspring != null) {
         if (Context.PopulationCount < MaximumPopulationSize)
-          HillClimb(offspring, token);
+          AdaptiveClimb(offspring, Context.LocalSearchEvaluations, token);
         if (Replace(offspring, token)) {
           replaced = true;
@@ -294,5 +296,5 @@
       if (!replaced && offspring != null) {
         if (Context.HillclimbingSuited(offspring)) {
-          HillClimb(offspring, token);
+          AdaptiveClimb(offspring, Context.LocalSearchEvaluations, token);
           if (Replace(offspring, token)) {
             Context.ByHillclimbing++;
@@ -516,6 +518,9 @@
       return false;// -1;
     }
-    
-    protected abstract bool Eq(ISingleObjectiveSolutionScope<TSolution> a, ISingleObjectiveSolutionScope<TSolution> b);
+
+    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 ISingleObjectiveSolutionScope<TSolution> ToScope(TSolution code, double fitness = double.NaN);
@@ -599,5 +604,6 @@
         if (Context.Population.All(p => Context.IsBetter(offspring, p)))
           HillClimb(offspring, token);
-        else HillClimb(offspring, token, CalculateSubspace(new[] { p1.Solution, p2.Solution }));
+        else if (!Eq(offspring, p1) && !Eq(offspring, p2) && Context.HillclimbingSuited(offspring.Fitness))
+          HillClimb(offspring, token, CalculateSubspace(new[] { p1.Solution, p2.Solution }, inverse: false));
 
         Context.AddBreedingResult(p1, p2, offspring);
@@ -620,5 +626,18 @@
       var p2 = Context.AtPopulation(i2);
 
-      return Context.RelinkSuited(p1, p2) ? PerformRelinking(p1, p2, token, delink: false) : null;
+      if (!Context.RelinkSuited(p1, p2)) return null;
+
+      var link = PerformRelinking(p1, p2, token, delink: false);
+      if (double.IsNaN(link.Fitness)) {
+        Evaluate(link, token);
+        Context.IncrementEvaluatedSolutions(1);
+      }
+      // 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;
     }
 
@@ -630,6 +649,19 @@
       var p1 = Context.AtPopulation(i1);
       var p2 = Context.AtPopulation(i2);
-
-      return Context.DelinkSuited(p1, p2) ? PerformRelinking(p1, p2, token, delink: true) : null;
+      
+      if (!Context.DelinkSuited(p1, p2)) return null;
+
+      var link = PerformRelinking(p1, p2, token, delink: true);
+      if (double.IsNaN(link.Fitness)) {
+        Evaluate(link, token);
+        Context.IncrementEvaluatedSolutions(1);
+      }
+      // 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: false));*/
+
+      return link;
     }
 
@@ -661,15 +693,16 @@
     #region Sample
     protected virtual ISingleObjectiveSolutionScope<TSolution> Sample(CancellationToken token) {
-      if (Context.PopulationCount == MaximumPopulationSize && Context.SamplingSuited()) {
+      if (Context.PopulationCount == MaximumPopulationSize) {
         SolutionModelTrainerParameter.Value.TrainModel(Context);
         ISingleObjectiveSolutionScope<TSolution> bestSample = null;
         var tries = 1;
-        for (; tries < Context.LocalSearchEvaluations; tries++) {
+        for (; tries < 100; tries++) {
           var sample = ToScope(Context.Model.Sample());
           Evaluate(sample, token);
           if (bestSample == null || Context.IsBetter(sample, bestSample)) {
             bestSample = sample;
+            if (Context.Population.Any(x => !Context.IsBetter(x, bestSample))) break;
           }
-          if (Context.Population.Any(x => !Context.IsBetter(x, bestSample))) break;
+          if (!Context.SamplingSuited()) break;
         }
         Context.IncrementEvaluatedSolutions(tries);
Index: branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/MemPRContext.cs
===================================================================
--- branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/MemPRContext.cs	(revision 14549)
+++ branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/MemPRContext.cs	(revision 14550)
@@ -114,4 +114,11 @@
 
     [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 {
@@ -257,4 +264,5 @@
       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);
@@ -290,4 +298,5 @@
       Parameters.Add(bestSolution = new ValueParameter<TSolution>("BestSolution"));
       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)));
@@ -495,9 +504,4 @@
     }
 
-    protected double ProbabilityAccept(ISingleObjectiveSolutionScope<TSolution> scope, IList<Tuple<double, double>> data) {
-      if (double.IsNaN(scope.Fitness)) throw new ArgumentException("solution not evaluated or quality unknown", "scope");
-      return ProbabilityAccept2d(scope.Fitness, data);
-    }
-
     private double ProbabilityAccept2dModel(double a, IConfidenceRegressionModel model) {
       var ds = new Dataset(new[] { "in", "out" }, new[] { new List<double> { a }, new List<double> { double.NaN } });
@@ -509,44 +513,4 @@
       return Problem.Maximization ? 1.0 - Phi(z) /* P(X >= z) */ : Phi(z); // P(X <= z)
     }
-    protected double ProbabilityAccept2d(double startingFitness, IList<Tuple<double, double>> data) {
-      if (data.Count < 10) return 1.0;
-      var samples = 0;
-      double meanStart = 0, meanStartOld = 0, meanEnd = 0, meanEndOld = 0;
-      double varStart = 0, varStartOld = 0, varEnd = 0, varEndOld = 0;
-      for (var i = 0; i < data.Count; i++) {
-        samples++;
-        var x = data[i].Item1;
-        var y = data[i].Item2;
-
-        if (samples == 1) {
-          meanStartOld = x;
-          meanEndOld = y;
-        } else {
-          meanStart = meanStartOld + (x - meanStartOld) / samples;
-          meanEnd = meanEndOld + (y - meanEndOld) / samples;
-          varStart = varStartOld + (x - meanStartOld) * (x - meanStart) / (samples - 1);
-          varEnd = varEndOld + (y - meanEndOld) * (y - meanEnd) / (samples - 1);
-
-          meanStartOld = meanStart;
-          meanEndOld = meanEnd;
-          varStartOld = varStart;
-          varEndOld = varEnd;
-        }
-      }
-      var cov = data.Select((v, i) => new { Index = i, Value = v }).Select(x => x.Value).Sum(x => (x.Item1 - meanStart) * (x.Item2 - meanEnd)) / data.Count;
-
-      var biasedMean = meanEnd + cov / varStart * (startingFitness - meanStart);
-      var biasedStdev = Math.Sqrt(varEnd - (cov * cov) / varStart);
-
-      if (Problem.Maximization) {
-        var goal = Population.Min(x => x.Fitness);
-        var z = (goal - biasedMean) / biasedStdev;
-        return 1.0 - Phi(z); // P(X >= z)
-      } else {
-        var goal = Population.Max(x => x.Fitness);
-        var z = (goal - biasedMean) / biasedStdev;
-        return Phi(z); // P(X <= z)
-      }
-    }
 
     private double ProbabilityAccept3dModel(double a, double b, IConfidenceRegressionModel model) {
@@ -558,54 +522,4 @@
       var z = (goal - mean) / sdev;
       return Problem.Maximization ? 1.0 - Phi(z) /* P(X >= z) */ : Phi(z); // P(X <= z)
-    }
-    protected double ProbabilityAccept3d(double startingFitness1, double startingFitness2, IList<Tuple<double, double, double>> data) {
-      if (data.Count < 20) return 1.0;
-      var samples = 0;
-      double meanX1 = 0, meanX1Old = 0, meanX2 = 0, meanX2Old = 0, meanY = 0, meanYOld = 0;
-      double varX1 = 0, varX1Old = 0, varX2 = 0, varX2Old = 0, varY = 0, varYOld = 0;
-      for (var i = 0; i < data.Count; i++) {
-        samples++;
-        var x1 = data[i].Item1;
-        var x2 = data[i].Item2;
-        var y = data[i].Item3;
-
-        if (samples == 1) {
-          meanX1Old = x1;
-          meanX2Old = x2;
-          meanYOld = y;
-        } else {
-          meanX1 = meanX1Old + (x1 - meanX1Old) / samples;
-          meanX2 = meanX2Old + (x2 - meanX2Old) / samples;
-          meanY = meanYOld + (y - meanYOld) / samples;
-          varX1 = varX1Old + (x1 - meanX1Old) * (x1 - meanX1) / (samples - 1);
-          varX2 = varX2Old + (x2 - meanX2Old) * (x2 - meanX2) / (samples - 1);
-          varY = varYOld + (y - meanYOld) * (y - meanY) / (samples - 1);
-
-          meanX1Old = meanX1;
-          meanX2Old = meanX2;
-          meanYOld = meanY;
-          varX1Old = varX1;
-          varX2Old = varX2;
-          varYOld = varY;
-        }
-      }
-      double covX1X2 = 0, covX1Y = 0, covX2Y = 0;
-      for (var i = 0; i < data.Count; i++) {
-        covX1X2 += (data[i].Item1 - meanX1) * (data[i].Item2 - meanX2) / data.Count;
-        covX1Y += (data[i].Item1 - meanX1) * (data[i].Item3 - meanY) / data.Count;
-        covX2Y += (data[i].Item2 - meanX2) * (data[i].Item3 - meanY) / data.Count;
-      }
-
-      var biasedMean = meanY + ((covX1Y * varX2 - covX2Y * covX1X2) * (startingFitness1 - meanX1) - (covX1Y * covX1X2 - covX2Y * varX1) * (startingFitness2 - meanX2)) / (varX1 * varX2 - covX1X2 * covX1X2);
-      var biasedStdev = Math.Sqrt(varY - (covX1Y * covX1Y * varX2 - 2 * covX1Y * covX2Y * covX1X2 + covX2Y * covX2Y * varX1) / (varX1 * varX2 - covX1X2 * covX1X2));
-      if (Problem.Maximization) {
-        var goal = Population.Min(x => x.Fitness);
-        var z = (goal - biasedMean) / biasedStdev;
-        return 1.0 - Phi(z); // P(X >= z)
-      } else {
-        var goal = Population.Max(x => x.Fitness);
-        var z = (goal - biasedMean) / biasedStdev;
-        return Phi(z); // P(X <= z)
-      }
     }
 
Index: branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/Permutation/PermutationMemPR.cs
===================================================================
--- branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/Permutation/PermutationMemPR.cs	(revision 14549)
+++ branches/MemPRAlgorithm/HeuristicLab.Algorithms.MemPR/3.3/Permutation/PermutationMemPR.cs	(revision 14550)
@@ -61,6 +61,6 @@
     }
 
-    protected override bool Eq(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> a, ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> b) {
-      return new PermutationEqualityComparer().Equals(a.Solution, b.Solution);
+    protected override bool Eq(Encodings.PermutationEncoding.Permutation a, Encodings.PermutationEncoding.Permutation b) {
+      return new PermutationEqualityComparer().Equals(a, b);
     }
 
@@ -398,6 +398,12 @@
       var evaluations = 1;
       ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> offspring = null;
-      for (; evaluations <= Context.LocalSearchEvaluations; evaluations++) {
-        var c = CyclicCrossover2.Apply(Context.Random, p1.Solution, p2.Solution);
+      for (; evaluations <= p1.Solution.Length; evaluations++) {
+        Encodings.PermutationEncoding.Permutation c = null;
+        var xochoice = Context.Random.Next(3);
+        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++;
@@ -423,6 +429,12 @@
       var evaluations = 1;
       ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> offspring = null;
-      for (; evaluations <= Context.LocalSearchEvaluations; evaluations++) {
-        var c = PartiallyMatchedCrossover.Apply(Context.Random, p1.Solution, p2.Solution);
+      for (; evaluations <= p1.Solution.Length; evaluations++) {
+        Encodings.PermutationEncoding.Permutation c = null;
+        var xochoice = Context.Random.Next(3);
+        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++;
@@ -448,6 +460,12 @@
       var evaluations = 1;
       ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> offspring = null;
-      for (; evaluations <= Context.LocalSearchEvaluations; evaluations++) {
-        var c = EdgeRecombinationCrossover.Apply(Context.Random, p1.Solution, p2.Solution);
+      for (; evaluations <= p1.Solution.Length; evaluations++) {
+        Encodings.PermutationEncoding.Permutation c = null;
+        var xochoice = Context.Random.Next(3);
+        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++;
@@ -469,26 +487,27 @@
 
     protected override ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> Link(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> a, ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> b, CancellationToken token, bool delink = false) {
-      if (double.IsNaN(a.Fitness)) Evaluate(a, token);
-      if (double.IsNaN(b.Fitness)) Evaluate(b, token);
-      if (Context.Random.NextDouble() < 0.5)
-        return Context.IsBetter(a, b) ? Relink(a, b, token) : Relink(b, a, token);
-      else return Context.IsBetter(a, b) ? Relink(b, a, token) : Relink(a, b, token);
-    }
-
-    protected virtual ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> Relink(ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> betterScope, ISingleObjectiveSolutionScope<Encodings.PermutationEncoding.Permutation> worseScope, CancellationToken token) {
-      var wrapper = new EvaluationWrapper<Encodings.PermutationEncoding.Permutation>(Problem, betterScope);
+      if (double.IsNaN(a.Fitness)) {
+        Evaluate(a, token);
+        Context.IncrementEvaluatedSolutions(1);
+      }
+      if (double.IsNaN(b.Fitness)) {
+        Evaluate(b, token);
+        Context.IncrementEvaluatedSolutions(1);
+      }
+
+      var wrapper = new EvaluationWrapper<Encodings.PermutationEncoding.Permutation>(Problem, ToScope(null));
       double quality;
-      return ToScope(Relink(Context.Random, betterScope.Solution, worseScope.Solution, wrapper.Evaluate, out quality));
-    }
-
-    public Encodings.PermutationEncoding.Permutation Relink(IRandom random, Encodings.PermutationEncoding.Permutation p1, Encodings.PermutationEncoding.Permutation p2, Func<Encodings.PermutationEncoding.Permutation, IRandom, double> eval, out double best) {
+      return ToScope(Relink(Context.Random, a.Solution, b.Solution, wrapper.Evaluate, delink, out quality));
+    }
+
+    public Encodings.PermutationEncoding.Permutation Relink(IRandom random, Encodings.PermutationEncoding.Permutation p1, Encodings.PermutationEncoding.Permutation p2, Func<Encodings.PermutationEncoding.Permutation, IRandom, double> eval, 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 RelinkSwap(random, p1, p2, eval, out best);
+          return delink ? DelinkSwap(random, p1, p2, eval, out best) : RelinkSwap(random, p1, p2, eval, out best);
         case PermutationTypes.RelativeDirected:
-          return RelinkShift(random, p1, p2, eval, out best);
+          return RelinkShift(random, p1, p2, eval, delink, out best);
         case PermutationTypes.RelativeUndirected:
-          return RelinkOpt(random, p1, p2, eval, out best);
+          return RelinkOpt(random, p1, p2, eval, delink, out best);
         default: throw new ArgumentException(string.Format("Unknown permutation type {0}", p1.PermutationType));
       }
@@ -506,6 +525,5 @@
       var invChild = new int[child.Length];
       for (var i = 0; i < child.Length; i++) invChild[child[i]] = i;
-
-      //Log(string.Join(", ", child));
+      
       while (options.Count > 0) {
         int bestOption = -1;
@@ -534,5 +552,4 @@
           invChild[child[idx1]] = idx1;
           invChild[child[idx2]] = idx2;
-          //Log(string.Join(", ", child));
           if (FitnessComparer.IsBetter(maximization, bestChange, best)) {
             if (Dist(child, p2) > 0) {
@@ -556,5 +573,64 @@
     }
 
-    public Encodings.PermutationEncoding.Permutation RelinkShift(IRandom random, Encodings.PermutationEncoding.Permutation p1, Encodings.PermutationEncoding.Permutation p2, Func<Encodings.PermutationEncoding.Permutation, IRandom, double> eval, out double best) {
+    public Encodings.PermutationEncoding.Permutation DelinkSwap(IRandom random, Encodings.PermutationEncoding.Permutation p1, Encodings.PermutationEncoding.Permutation p2, Func<Encodings.PermutationEncoding.Permutation, IRandom, double> eval, out double best) {
+      var maximization = Context.Problem.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, random);
+            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, random);
+        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 RelinkShift(IRandom random, Encodings.PermutationEncoding.Permutation p1, Encodings.PermutationEncoding.Permutation p2, Func<Encodings.PermutationEncoding.Permutation, IRandom, double> eval, bool delink, out double best) {
       var maximization = Context.Problem.Maximization;
       var evaluations = 0;
@@ -611,5 +687,5 @@
     }
 
-    public Encodings.PermutationEncoding.Permutation RelinkOpt(IRandom random, Encodings.PermutationEncoding.Permutation p1, Encodings.PermutationEncoding.Permutation p2, Func<Encodings.PermutationEncoding.Permutation, IRandom, double> eval, out double best) {
+    public Encodings.PermutationEncoding.Permutation RelinkOpt(IRandom random, Encodings.PermutationEncoding.Permutation p1, Encodings.PermutationEncoding.Permutation p2, Func<Encodings.PermutationEncoding.Permutation, IRandom, double> eval, bool delink, out double best) {
       var maximization = Context.Problem.Maximization;
       var evaluations = 0;
Index: branches/MemPRAlgorithm/HeuristicLab.Encodings.BinaryVectorEncoding/3.3/BinaryVectorEqualityComparer.cs
===================================================================
--- branches/MemPRAlgorithm/HeuristicLab.Encodings.BinaryVectorEncoding/3.3/BinaryVectorEqualityComparer.cs	(revision 14550)
+++ branches/MemPRAlgorithm/HeuristicLab.Encodings.BinaryVectorEncoding/3.3/BinaryVectorEqualityComparer.cs	(revision 14550)
@@ -0,0 +1,43 @@
+﻿#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;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace HeuristicLab.Encodings.BinaryVectorEncoding {
+  public class BinaryVectorEqualityComparer : EqualityComparer<BinaryVector> {
+    public override bool Equals(BinaryVector first, BinaryVector second) {
+      return first.SequenceEqual(second);
+    }
+    public override int GetHashCode(BinaryVector obj) {
+      unchecked {
+        int hash = 17;
+        foreach (var bit in obj) {
+          hash = hash * 29 + (bit ? 1231 : 1237);
+        }
+        return hash;
+      }
+    }
+  }
+}
Index: branches/MemPRAlgorithm/HeuristicLab.Encodings.BinaryVectorEncoding/3.3/HeuristicLab.Encodings.BinaryVectorEncoding-3.3.csproj
===================================================================
--- branches/MemPRAlgorithm/HeuristicLab.Encodings.BinaryVectorEncoding/3.3/HeuristicLab.Encodings.BinaryVectorEncoding-3.3.csproj	(revision 14549)
+++ branches/MemPRAlgorithm/HeuristicLab.Encodings.BinaryVectorEncoding/3.3/HeuristicLab.Encodings.BinaryVectorEncoding-3.3.csproj	(revision 14550)
@@ -120,4 +120,5 @@
   <ItemGroup>
     <Compile Include="BinaryVectorEncoding.cs" />
+    <Compile Include="BinaryVectorEqualityComparer.cs" />
     <Compile Include="Creators\RandomBinaryVectorCreator.cs" />
     <Compile Include="Crossovers\MultiBinaryVectorCrossover.cs" />
