Index: /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/Evolutionary/ESContext.cs
===================================================================
--- /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/Evolutionary/ESContext.cs	(revision 15563)
+++ /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/Evolutionary/ESContext.cs	(revision 15564)
@@ -20,5 +20,4 @@
 #endregion
 
-using System.Collections.Generic;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
@@ -50,8 +49,4 @@
       get { return normalRand.Value; }
       set { normalRand.Value = value; }
-    }
-
-    public void ReplacePopulation(IEnumerable<ISingleObjectiveSolutionScope<ESGQAPSolution>> replacement) {
-      Scope.SubScopes.Replace(replacement);
     }
     
Index: /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/Evolutionary/EvolutionStrategy.cs
===================================================================
--- /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/Evolutionary/EvolutionStrategy.cs	(revision 15563)
+++ /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/Evolutionary/EvolutionStrategy.cs	(revision 15564)
@@ -69,4 +69,9 @@
       get { return selectionParameter; }
     }
+    [Storable]
+    private FixedValueParameter<BoolValue> useRecombinationParameter;
+    public IFixedValueParameter<BoolValue> UseRecombinationParameter {
+      get { return useRecombinationParameter; }
+    }
 
     public int Lambda {
@@ -81,4 +86,8 @@
       get { return selectionParameter.Value.Value; }
       set { selectionParameter.Value.Value = value; }
+    }
+    public bool UseRecombination {
+      get { return useRecombinationParameter.Value.Value; }
+      set { useRecombinationParameter.Value.Value = value; }
     }
 
@@ -90,4 +99,5 @@
       muParameter = cloner.Clone(original.muParameter);
       selectionParameter = cloner.Clone(original.selectionParameter);
+      useRecombinationParameter = cloner.Clone(original.useRecombinationParameter);
     }
     public EvolutionStrategy() {
@@ -95,5 +105,6 @@
       Parameters.Add(muParameter = new FixedValueParameter<IntValue>("Mu", "(μ) The population size.", new IntValue(1)));
       Parameters.Add(selectionParameter= new FixedValueParameter<EnumValue<ESSelection>>("Selection", "The selection strategy: elitist (plus) or generational (comma).", new EnumValue<ESSelection>(ESSelection.Plus)));
-      
+      Parameters.Add(useRecombinationParameter = new FixedValueParameter<BoolValue>("Use Recombination", "Whether to create an \"intermediate\" solution to perform the mutation from.", new BoolValue(false)));
+
       Problem = new GQAP();
     }
@@ -116,5 +127,9 @@
         Context.EvaluatedSolutions++;
 
-        var ind = new ESGQAPSolution(assign, eval, Context.Random.NextDouble() * 2 - 1);
+        var min = (1.0 / assign.Length) * 2 - 1; // desired min prob
+        var max = (4.0 / assign.Length) * 2 - 1; // desired max prob
+        min = 0.5 * (Math.Log(1 + min) - Math.Log(1 - min)); // arctanh
+        max = 0.5 * (Math.Log(1 + max) - Math.Log(1 - max));
+        var ind = new ESGQAPSolution(assign, eval, min + Context.Random.NextDouble() * (max - min));
         var fit = Problem.ProblemInstance.ToSingleObjective(eval);
         Context.AddToPopulation(Context.ToScope(ind, fit));
@@ -135,4 +150,5 @@
     protected override void Run(CancellationToken cancellationToken) {
       var lastUpdate = ExecutionTime;
+      var eq = new IntegerVectorEqualityComparer();
 
       while (!StoppingCriterion()) {
@@ -140,14 +156,25 @@
 
         for (var l = 0; l < Lambda; l++) {
-          var m = Context.AtRandomPopulation();
-
-          var offspring = (ESGQAPSolution)m.Solution.Clone();
-          var count = Mutate(m, offspring);
-          offspring.SParam += 0.7071 * Context.NormalRand.NextDouble(); //((1.0 / count) - offspring.SParam) / 10.0;
-
-          offspring.Evaluation = Problem.ProblemInstance.Evaluate(offspring.Assignment);
+          IntegerVector child = null;
+          var sParam = 0.0;
+          if (UseRecombination) {
+            child = DiscreteLocationCrossover.Apply(Context.Random, new ItemArray<IntegerVector>(Context.Population.Select(x => x.Solution.Assignment)), Problem.ProblemInstance.Demands, Problem.ProblemInstance.Capacities);
+            sParam = Context.Population.Select(x => x.Solution.SParam).Average();
+          } else {
+            var m = Context.AtRandomPopulation();
+            child = (IntegerVector)m.Solution.Assignment.Clone();
+            sParam = m.Solution.SParam;
+          }
+          sParam += 0.7071 * Context.NormalRand.NextDouble();
+          RelocateEquipmentManipluator.Apply(Context.Random, child,
+            Problem.ProblemInstance.Capacities.Length, (Math.Tanh(sParam) + 1) / 2.0);
+          var eval = Problem.ProblemInstance.Evaluate(child);
           Context.EvaluatedSolutions++;
+
+          var offspring = new ESGQAPSolution(child, eval, sParam);
+
           var fit = Problem.ProblemInstance.ToSingleObjective(offspring.Evaluation);
-          nextGen.Add(Context.ToScope(offspring, fit));
+          if (Selection == ESSelection.Comma || Context.Population.Select(x => x.Solution.Assignment).All(x => !eq.Equals(child, x)))
+            nextGen.Add(Context.ToScope(offspring, fit));
 
           if (fit < Context.BestQuality) {
@@ -160,5 +187,5 @@
           Context.ReplacePopulation(nextGen.OrderBy(x => x.Fitness).Take(Mu));
         } else if (Selection == ESSelection.Plus) {
-          var best = nextGen.Concat(Context.Population).OrderBy(x => x.Fitness).Take(Mu).ToList();
+          var best = Context.Population.Concat(nextGen).OrderBy(x => x.Fitness).Take(Mu).ToList();
           Context.ReplacePopulation(best);
         } else throw new InvalidOperationException("Unknown Selection strategy: " + Selection);
@@ -181,5 +208,7 @@
         else Results.Add(new Result("BestSolution", Context.BestSolution));
 
-        Context.RunOperator(Analyzer, Context.Scope, cancellationToken);
+        try {
+          Context.RunOperator(Analyzer, Context.Scope, cancellationToken);
+        } catch (OperationCanceledException) { }
 
         Context.Iterations++;
@@ -194,40 +223,4 @@
       else Results.Add(new Result("EvaluatedSolutions", new IntValue(Context.EvaluatedSolutions)));
     }
-
-    private int Mutate(ISingleObjectiveSolutionScope<ESGQAPSolution> m, ESGQAPSolution offspring) {
-      var stopProb = (Math.Tanh(m.Solution.SParam) + 1) / 2.0; // squash strategy parameter to ]0;1[
-      var offspringFeasible = offspring.Evaluation.IsFeasible;
-      double[] slack = null;
-      if (offspringFeasible) slack = offspring.Evaluation.Slack.ToArray();
-      var count = 1;
-      foreach (var equip in Enumerable.Range(0, Problem.ProblemInstance.Demands.Length).Shuffle(Context.Random)) {
-        var currentLoc = offspring.Assignment[equip];
-        if (offspringFeasible) {
-          var demand = Problem.ProblemInstance.Demands[equip];
-          var feasibleLoc = slack.Select((v, i) => new { Index = i, Value = v })
-            .Where(x => x.Index != currentLoc
-            && x.Value >= demand).ToList();
-          int newLoc = -1;
-          if (feasibleLoc.Count == 0) {
-            newLoc = Context.Random.Next(Problem.ProblemInstance.Capacities.Length - 1);
-            if (newLoc >= currentLoc) newLoc++; // don't reassign to current loc
-            offspringFeasible = false;
-          } else {
-            newLoc = feasibleLoc.SampleRandom(Context.Random).Index;
-          }
-          offspring.Assignment[equip] = newLoc;
-          slack[currentLoc] += demand;
-          slack[newLoc] -= demand;
-        } else {
-          var newLoc = Context.Random.Next(Problem.ProblemInstance.Capacities.Length - 1);
-          if (newLoc >= currentLoc) newLoc++; // don't reassign to current loc
-          offspring.Assignment[equip] = newLoc;
-        }
-        if (Context.Random.NextDouble() < stopProb) break;
-        count++;
-      }
-
-      return count;
-    }
   }
 }
Index: /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/Evolutionary/OSGA.cs
===================================================================
--- /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/Evolutionary/OSGA.cs	(revision 15564)
+++ /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/Evolutionary/OSGA.cs	(revision 15564)
@@ -0,0 +1,227 @@
+﻿#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.Linq;
+using System.Threading;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Encodings.IntegerVectorEncoding;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms.Evolutionary {
+  [Item("OSGA (GQAP)", "The algorithm implements a strict offspring selection genetic algorithm (OSGA).")]
+  [Creatable(CreatableAttribute.Categories.PopulationBasedAlgorithms)]
+  [StorableClass]
+  public sealed class OSGA : StochasticAlgorithm<OSGAContext> {
+
+    public override bool SupportsPause {
+      get { return true; }
+    }
+
+    public override Type ProblemType {
+      get { return typeof(GQAP); }
+    }
+
+    public new GQAP Problem {
+      get { return (GQAP)base.Problem; }
+      set { base.Problem = value; }
+    }
+
+    [Storable]
+    private FixedValueParameter<IntValue> populationSizeParameter;
+    public IFixedValueParameter<IntValue> PopulationSizeParameter {
+      get { return populationSizeParameter; }
+    }
+    [Storable]
+    private FixedValueParameter<PercentValue> mutationProbabilityParameter;
+    public IFixedValueParameter<PercentValue> MutationProbabilityParameter {
+      get { return mutationProbabilityParameter; }
+    }
+
+    public int PopulationSize {
+      get { return populationSizeParameter.Value.Value; }
+      set { populationSizeParameter.Value.Value = value; }
+    }
+    public double MutationProbability {
+      get { return mutationProbabilityParameter.Value.Value; }
+      set { mutationProbabilityParameter.Value.Value = value; }
+    }
+
+    [StorableConstructor]
+    private OSGA(bool deserializing) : base(deserializing) { }
+    private OSGA(OSGA original, Cloner cloner)
+      : base(original, cloner) {
+      populationSizeParameter = cloner.Clone(original.populationSizeParameter);
+      mutationProbabilityParameter = cloner.Clone(original.mutationProbabilityParameter);
+    }
+    public OSGA() {
+      Parameters.Add(populationSizeParameter = new FixedValueParameter<IntValue>("Population Size", "(μ) The population size.", new IntValue(500)));
+      Parameters.Add(mutationProbabilityParameter = new FixedValueParameter<PercentValue>("Mutation Probability", "The chance for an offspring to get mutated.", new PercentValue(0.05)));
+
+      Problem = new GQAP();
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new OSGA(this, cloner);
+    }
+
+    protected override void Initialize(CancellationToken cancellationToken) {
+      base.Initialize(cancellationToken);
+      
+      Context.Problem = Problem;
+      Context.BestQuality = double.NaN;
+      Context.BestSolution = null;
+
+      for (var m = 0; m < PopulationSize; m++) {
+        var assign = new IntegerVector(Problem.ProblemInstance.Demands.Length, Context.Random, 0, Problem.ProblemInstance.Capacities.Length);
+        var eval = Problem.ProblemInstance.Evaluate(assign);
+        Context.EvaluatedSolutions++;
+        
+        var ind = new GQAPSolution(assign, eval);
+        var fit = Problem.ProblemInstance.ToSingleObjective(eval);
+        Context.AddToPopulation(Context.ToScope(ind, fit));
+        if (double.IsNaN(Context.BestQuality) || fit < Context.BestQuality) {
+          Context.BestQuality = fit;
+          Context.BestSolution = (GQAPSolution)ind.Clone();
+        }
+      }
+
+      Context.SelectionPressure = 0;
+      Context.Attempts = 0;
+      Context.NextGeneration = new ItemList<ISingleObjectiveSolutionScope<GQAPSolution>>();
+
+      Results.Add(new Result("Iterations", new IntValue(Context.Iterations)));
+      Results.Add(new Result("EvaluatedSolutions", new IntValue(Context.EvaluatedSolutions)));
+      Results.Add(new Result("BestQuality", new DoubleValue(Context.BestQuality)));
+      Results.Add(new Result("BestSolution", Context.BestSolution));
+
+      Context.RunOperator(Analyzer, Context.Scope, cancellationToken);
+    }
+
+    protected override void Run(CancellationToken cancellationToken) {
+      var lastUpdate = ExecutionTime;
+
+      while (!StoppingCriterion()) {
+        
+        while (!StoppingCriterion() && Context.NextGeneration.Count < PopulationSize
+          && Context.SelectionPressure < 1000) {
+
+          var idx1 = Context.Random.Next(PopulationSize);
+          var idx2 = (idx1 + Context.Random.Next(1, PopulationSize)) % PopulationSize;
+
+          var p1 = Context.AtPopulation(idx1);
+          var p2 = Context.AtPopulation(idx2);
+
+          var assign = DiscreteLocationCrossover.Apply(Context.Random,
+            new ItemArray<IntegerVector>(new[] { p1.Solution.Assignment, p2.Solution.Assignment }),
+            Problem.ProblemInstance.Demands, Problem.ProblemInstance.Capacities);
+
+          if (Context.Random.NextDouble() < MutationProbability) {
+            RelocateEquipmentManipluator.Apply(Context.Random, assign, Problem.ProblemInstance.Capacities.Length, 4.0 / assign.Length);
+          }
+
+          var eval = Problem.ProblemInstance.Evaluate(assign);
+          Context.EvaluatedSolutions++;
+
+          var offspring = new GQAPSolution(assign, eval);
+
+          var fit = Problem.ProblemInstance.ToSingleObjective(offspring.Evaluation);
+          if (fit < p1.Fitness && fit < p2.Fitness) { // strict OS
+            Context.NextGeneration.Add(Context.ToScope(offspring, fit));
+
+            if (fit < Context.BestQuality) {
+              Context.BestQuality = fit;
+              Context.BestSolution = (GQAPSolution)offspring.Clone();
+            }
+          }
+
+          Context.SelectionPressure += 1.0 / PopulationSize;
+          Context.Attempts++;
+          if (Context.SelectionPressure > 10
+            && Context.NextGeneration.Count / (double)PopulationSize < Context.SelectionPressure / 1000)
+            break;
+          if (cancellationToken.IsCancellationRequested) return;
+        }
+        
+        var restart = Context.NextGeneration.Count < PopulationSize;
+
+        if (restart) {
+          var best = Context.Population.Concat(Context.NextGeneration)
+            .OrderBy(x => x.Fitness).Take(PopulationSize).ToList();
+          Context.ReplacePopulation(best);
+        } else {
+          Context.ReplacePopulation(Context.NextGeneration);
+        }
+        Context.NextGeneration.Clear();
+
+        IResult result;
+        if (ExecutionTime - lastUpdate > TimeSpan.FromSeconds(1)) {
+          if (Results.TryGetValue("Iterations", out result))
+            ((IntValue)result.Value).Value = Context.Iterations;
+          else Results.Add(new Result("Iterations", new IntValue(Context.Iterations)));
+          if (Results.TryGetValue("EvaluatedSolutions", out result))
+            ((IntValue)result.Value).Value = Context.EvaluatedSolutions;
+          else Results.Add(new Result("EvaluatedSolutions", new IntValue(Context.EvaluatedSolutions)));
+          lastUpdate = ExecutionTime;
+        }
+        if (Results.TryGetValue("BestQuality", out result))
+          ((DoubleValue)result.Value).Value = Context.BestQuality;
+        else Results.Add(new Result("BestQuality", new DoubleValue(Context.BestQuality)));
+        if (Results.TryGetValue("BestSolution", out result))
+          result.Value = Context.BestSolution;
+        else Results.Add(new Result("BestSolution", Context.BestSolution));
+
+        try {
+          Context.RunOperator(Analyzer, Context.Scope, cancellationToken);
+        } catch (OperationCanceledException) { }
+
+        Context.Iterations++;
+
+        if (restart) {
+          var seed = Context.Population.Select(x => (IntegerVector)x.Solution.Assignment.Clone()).ToList();
+          for (var s = 0; s < seed.Count; s++) {
+            RelocateEquipmentManipluator.Apply(Context.Random, seed[s], Problem.ProblemInstance.Capacities.Length, 0.0);
+            var eval = Problem.ProblemInstance.Evaluate(seed[s]);
+            Context.EvaluatedSolutions++;
+            var fit = Problem.ProblemInstance.ToSingleObjective(eval);
+            Context.NextGeneration.Add(Context.ToScope(new GQAPSolution(seed[s], eval), fit));
+          }
+          Context.ReplacePopulation(Context.NextGeneration);
+          Context.NextGeneration.Clear();
+        }
+        Context.SelectionPressure = 0;
+
+        if (cancellationToken.IsCancellationRequested) break;
+      }
+      IResult result2;
+      if (Results.TryGetValue("Iterations", out result2))
+        ((IntValue)result2.Value).Value = Context.Iterations;
+      else Results.Add(new Result("Iterations", new IntValue(Context.Iterations)));
+      if (Results.TryGetValue("EvaluatedSolutions", out result2))
+        ((IntValue)result2.Value).Value = Context.EvaluatedSolutions;
+      else Results.Add(new Result("EvaluatedSolutions", new IntValue(Context.EvaluatedSolutions)));
+    }
+  }
+}
Index: /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/Evolutionary/OSGAContext.cs
===================================================================
--- /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/Evolutionary/OSGAContext.cs	(revision 15564)
+++ /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/Evolutionary/OSGAContext.cs	(revision 15564)
@@ -0,0 +1,107 @@
+﻿#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.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms.Evolutionary {
+  [Item("OSGA (GQAP) Context", "Context for OSGA (GQAP).")]
+  [StorableClass]
+  public sealed class OSGAContext : SingleObjectivePopulationContext<ISingleObjectiveSolutionScope<GQAPSolution>> {
+    
+    [Storable]
+    private IValueParameter<GQAP> problem;
+    public GQAP Problem {
+      get { return problem.Value; }
+      set { problem.Value = value; }
+    }
+
+    [Storable]
+    private IValueParameter<GQAPSolution> bestSolution;
+    public GQAPSolution BestSolution {
+      get { return bestSolution.Value; }
+      set { bestSolution.Value = value; }
+    }
+
+    [Storable]
+    private IValueParameter<ItemList<ISingleObjectiveSolutionScope<GQAPSolution>>> nextGeneration;
+    public ItemList<ISingleObjectiveSolutionScope<GQAPSolution>> NextGeneration {
+      get { return nextGeneration.Value; }
+      set { nextGeneration.Value = value; }
+    }
+
+    [Storable]
+    private IValueParameter<DoubleValue> selectionPressure;
+    public double SelectionPressure {
+      get { return selectionPressure.Value.Value; }
+      set { selectionPressure.Value.Value = value; }
+    }
+
+    [Storable]
+    private IValueParameter<IntValue> attempts;
+    public int Attempts {
+      get { return attempts.Value.Value; }
+      set { attempts.Value.Value = value; }
+    }
+
+    public void SortPopulation() {
+      Scope.SubScopes.Replace(Scope.SubScopes.OfType<ISingleObjectiveSolutionScope<GQAPSolution>>().OrderBy(x => Problem.Maximization ? -x.Fitness : x.Fitness).ToList());
+    }
+    
+    [StorableConstructor]
+    private OSGAContext(bool deserializing) : base(deserializing) { }
+    private OSGAContext(OSGAContext original, Cloner cloner)
+      : base(original, cloner) {
+      problem = cloner.Clone(original.problem);
+      bestSolution = cloner.Clone(original.bestSolution);
+      nextGeneration = cloner.Clone(original.nextGeneration);
+      selectionPressure = cloner.Clone(original.selectionPressure);
+      attempts = cloner.Clone(original.attempts);
+    }
+    public OSGAContext() : this("OSGA (GQAP) Context") { }
+    public OSGAContext(string name) : base(name) {
+      Parameters.Add(problem = new ValueParameter<GQAP>("Problem"));
+      Parameters.Add(bestSolution = new ValueParameter<GQAPSolution>("BestFoundSolution"));
+      Parameters.Add(nextGeneration = new ValueParameter<ItemList<ISingleObjectiveSolutionScope<GQAPSolution>>>("NextGeneration"));
+      Parameters.Add(selectionPressure = new ValueParameter<DoubleValue>("SelectionPressure", new DoubleValue(0)));
+      Parameters.Add(attempts = new ValueParameter<IntValue>("Attempts", new IntValue(0)));
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new OSGAContext(this, cloner);
+    }
+
+    public ISingleObjectiveSolutionScope<GQAPSolution> ToScope(GQAPSolution code, double fitness = double.NaN) {
+      var name = Problem.Encoding.Name;
+      var scope = new SingleObjectiveSolutionScope<GQAPSolution>(code,
+        name + "Solution", fitness, Problem.Evaluator.QualityParameter.ActualName) {
+        Parent = Scope
+      };
+      scope.Variables.Add(new Variable(name, code.Assignment));
+      scope.Variables.Add(new Variable("Evaluation", code.Evaluation));
+      return scope;
+    }
+  }
+}
Index: /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/GRASP/GRASP.cs
===================================================================
--- /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/GRASP/GRASP.cs	(revision 15563)
+++ /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/GRASP/GRASP.cs	(revision 15564)
@@ -167,5 +167,9 @@
       while (!StoppingCriterion()) { // line 2 in Algorithm 1
         // next: line 3 in Algorithm 1
-        var pi_prime_vec = GreedyRandomizedSolutionCreator.CreateSolution(Context.Random, Problem.ProblemInstance, 1000, false, cancellationToken);
+        IntegerVector pi_prime_vec = null;
+        try {
+          pi_prime_vec = GreedyRandomizedSolutionCreator.CreateSolution(Context.Random, Problem.ProblemInstance, 1000, false, cancellationToken);
+        } catch (OperationCanceledException) { break; }
+
         if (Context.PopulationCount >= MinimumEliteSetSize) { // line 4 in Algorithm 1
           GQAPSolution pi_prime;
@@ -234,5 +238,7 @@
         else Results.Add(new Result("BestSolution", Context.BestSolution));
 
-        Context.RunOperator(Analyzer, Context.Scope, cancellationToken);
+        try {
+          Context.RunOperator(Analyzer, Context.Scope, cancellationToken);
+        } catch (OperationCanceledException) { }
 
         Context.Iterations++;
Index: /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms-3.3.csproj
===================================================================
--- /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms-3.3.csproj	(revision 15563)
+++ /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms-3.3.csproj	(revision 15564)
@@ -102,4 +102,6 @@
     <Compile Include="Evolutionary\ESGQAPSolution.cs" />
     <Compile Include="Evolutionary\EvolutionStrategy.cs" />
+    <Compile Include="Evolutionary\OSGAContext.cs" />
+    <Compile Include="Evolutionary\OSGA.cs" />
     <Compile Include="Infrastructure\Algorithms\StochasticAlgorithm.cs" />
     <Compile Include="Infrastructure\Contexts\SingleObjectiveSingleSolutionContext.cs" />
Index: /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/Infrastructure/Contexts/PopulationContext.cs
===================================================================
--- /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/Infrastructure/Contexts/PopulationContext.cs	(revision 15563)
+++ /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/Infrastructure/Contexts/PopulationContext.cs	(revision 15564)
@@ -40,4 +40,7 @@
       Scope.SubScopes[index] = solScope;
     }
+    public void ReplacePopulation(IEnumerable<TSolutionScope> replacement) {
+      Scope.SubScopes.Replace(replacement);
+    }
     public TSolutionScope AtPopulation(int index) {
       return Scope.SubScopes[index] as TSolutionScope;
Index: /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/LAHC/LAHC.cs
===================================================================
--- /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/LAHC/LAHC.cs	(revision 15563)
+++ /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/LAHC/LAHC.cs	(revision 15564)
@@ -156,6 +156,8 @@
         else Results.Add(new Result("BestSolution", Context.BestSolution));
 
-        Context.RunOperator(Analyzer, Context.Scope, cancellationToken);
-        
+        try {
+          Context.RunOperator(Analyzer, Context.Scope, cancellationToken);
+        } catch (OperationCanceledException) { }
+
         if (cancellationToken.IsCancellationRequested) break;
       }
Index: /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/LAHC/pLAHC.cs
===================================================================
--- /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/LAHC/pLAHC.cs	(revision 15563)
+++ /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/LAHC/pLAHC.cs	(revision 15564)
@@ -187,5 +187,8 @@
             result.Value = Context.BestSolution;
           else Results.Add(new Result("BestSolution", Context.BestSolution));
-          Context.RunOperator(Analyzer, Context.Scope, cancellationToken);
+
+          try {
+            Context.RunOperator(Analyzer, Context.Scope, cancellationToken);
+          } catch (OperationCanceledException) { }
 
           if (cancellationToken.IsCancellationRequested) break;
Index: /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/LocalSearch/IteratedLS.cs
===================================================================
--- /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/LocalSearch/IteratedLS.cs	(revision 15563)
+++ /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/LocalSearch/IteratedLS.cs	(revision 15564)
@@ -25,6 +25,6 @@
 using HeuristicLab.Core;
 using HeuristicLab.Data;
-using HeuristicLab.Encodings.IntegerVectorEncoding;
 using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
 
@@ -48,4 +48,15 @@
     }
 
+    [Storable]
+    private IFixedValueParameter<PercentValue> perturbationStrengthParameter;
+    public IFixedValueParameter<PercentValue> PerturbationStrengthParameter {
+      get { return perturbationStrengthParameter; }
+    }
+
+    public double PerturbationStrength {
+      get { return perturbationStrengthParameter.Value.Value; }
+      set { perturbationStrengthParameter.Value.Value = value; }
+    }
+
     [StorableConstructor]
     private IteratedLS(bool deserializing) : base(deserializing) { }
@@ -54,4 +65,5 @@
     }
     public IteratedLS() {
+      Parameters.Add(perturbationStrengthParameter = new FixedValueParameter<PercentValue>("PerturbationStrength", "The expected length of the random walk relative to the size of the solution vector.", new PercentValue(0.5)));
 
       Problem = new GQAP();
@@ -97,5 +109,5 @@
         var lsevaluations = 0;
         var candidate = (GQAPSolution)Context.Incumbent.Solution.Clone();
-        RandomWalk(Context.Random, candidate.Assignment, Problem.ProblemInstance.Capacities.Length, candidate.Assignment.Length);
+        RelocateEquipmentManipluator.Apply(Context.Random, candidate.Assignment, Problem.ProblemInstance.Capacities.Length, 1.0 / (PerturbationStrength * candidate.Assignment.Length));
         candidate.Evaluation = Problem.ProblemInstance.Evaluate(candidate.Assignment);
         Context.EvaluatedSolutions++;
@@ -127,5 +139,7 @@
         else Results.Add(new Result("BestSolution", Context.BestSolution));
 
-        Context.RunOperator(Analyzer, Context.Scope, cancellationToken);
+        try {
+          Context.RunOperator(Analyzer, Context.Scope, cancellationToken);
+        } catch (OperationCanceledException) { }
 
         Context.Iterations++;
@@ -140,12 +154,4 @@
       else Results.Add(new Result("EvaluatedSolutions", new IntValue(Context.EvaluatedSolutions)));
     }
-
-    private static void RandomWalk(IRandom random, IntegerVector assignment, int locations, int walkLength) {
-      for (int i = 0; i < walkLength; i++) {
-        var equipment = random.Next(assignment.Length);
-        assignment[equipment] = random.Next(locations);
-        if (random.NextDouble() < 1.0 / walkLength) break;
-      }
-    }
   }
 }
Index: /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/LocalSearch/MultistartLS.cs
===================================================================
--- /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/LocalSearch/MultistartLS.cs	(revision 15563)
+++ /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms/3.3/LocalSearch/MultistartLS.cs	(revision 15564)
@@ -107,5 +107,7 @@
         else Results.Add(new Result("BestSolution", Context.BestSolution));
 
-        Context.RunOperator(Analyzer, Context.Scope, cancellationToken);
+        try {
+          Context.RunOperator(Analyzer, Context.Scope, cancellationToken);
+        } catch (OperationCanceledException) { }
 
         Context.Iterations++;
Index: /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment/3.3/Operators/Crossovers/DiscreteLocationCrossover.cs
===================================================================
--- /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment/3.3/Operators/Crossovers/DiscreteLocationCrossover.cs	(revision 15563)
+++ /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment/3.3/Operators/Crossovers/DiscreteLocationCrossover.cs	(revision 15564)
@@ -20,4 +20,5 @@
 #endregion
 
+using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -46,35 +47,68 @@
     }
 
-    public static IntegerVector Apply(IRandom random, ItemArray<IntegerVector> parents, DoubleArray capacities) {
+    public static IntegerVector Apply(IRandom random, ItemArray<IntegerVector> parents, DoubleArray demands, DoubleArray capacities) {
+      var locations = capacities.Length;
+      var cap = Math.Max(parents[0].Length / locations, 1);
+      var lookup = new List<int>[parents.Length][];
+      for (var p = 0; p < parents.Length; p++) {
+        lookup[p] = new List<int>[locations];
+        var assign = parents[p];
+        for (var e = 0; e < parents[p].Length; e++) {
+          var loc = assign[e];
+          if (lookup[p][loc] == null) lookup[p][loc] = new List<int>(cap);
+          lookup[p][loc].Add(e);
+        }
+      }
 
-      var groupedLocations = parents
-              .Select(p => p.Select((value, index) => new { Equipment = index, Location = value })
-              .GroupBy(x => x.Location)
-              .ToDictionary(x => x.Key, y => y.AsEnumerable())).ToArray();
-
+      var slack = capacities.ToArray();
       IntegerVector child = new IntegerVector(parents[0].Length);
-      HashSet<int> remainingEquipment = new HashSet<int>(Enumerable.Range(0, child.Length));
-      int locations = capacities.Length;
-      foreach (var i in Enumerable.Range(0, locations).Shuffle(random)) {
+      var takenEquip = new bool[child.Length];
+      foreach (var loc in Enumerable.Range(0, locations).Shuffle(random)) {
         int parent = random.Next(parents.Length);
-        if (!groupedLocations[parent].ContainsKey(i)) {
+        if (lookup[parent][loc] == null) {
           int tmp = parent;
           do {
             tmp = (tmp + 1) % parents.Length;
-          } while (tmp != parent && !groupedLocations[tmp].ContainsKey(i));
+          } while (tmp != parent && lookup[tmp][loc] == null);
           if (parent == tmp) continue;
           else parent = tmp;
         }
-        foreach (var item in groupedLocations[parent][i]) {
-          if (remainingEquipment.Contains(item.Equipment)) {
-            child[item.Equipment] = i;
-            remainingEquipment.Remove(item.Equipment);
+        foreach (var equip in lookup[parent][loc]) {
+          if (!takenEquip[equip]) {
+            child[equip] = loc;
+            takenEquip[equip] = true;
+            slack[loc] -= demands[equip];
           }
         }
       }
 
-      foreach (var equipment in remainingEquipment) {
-        int parent = random.Next(parents.Length);
-        child[equipment] = parents[parent][equipment];
+      var order = Enumerable.Range(0, takenEquip.Length).Shuffle(random); // avoid bias
+      foreach (var e in order) {
+        if (takenEquip[e]) continue;
+        var assigned = false;
+        // try 1: find a parent where equipment can be assigned feasibly
+        foreach (var p in Enumerable.Range(0, parents.Length).Shuffle(random)) {
+          if (slack[parents[p][e]] >= demands[e]) {
+            child[e] = parents[p][e];
+            slack[child[e]] -= demands[e];
+            assigned = true;
+            break;
+          }
+        }
+        // try 2: find a random feasible location
+        if (!assigned) {
+          var possible = Enumerable.Range(0, locations).Where(x => slack[x] >= demands[e]).ToList();
+          if (possible.Count > 0) {
+            var loc = possible.SampleRandom(random);
+            child[e] = loc;
+            slack[loc] -= demands[e];
+            assigned = true;
+          }
+        }
+        // try 3: insert in a random location (no feasible insert found)
+        if (!assigned) {
+          child[e] = random.Next(locations);
+          slack[child[e]] -= demands[e];
+        }
       }
 
@@ -84,5 +118,5 @@
     protected override IntegerVector Cross(IRandom random, ItemArray<IntegerVector> parents,
       GQAPInstance problemInstance) {
-      return Apply(random, parents, problemInstance.Capacities);
+      return Apply(random, parents, problemInstance.Demands, problemInstance.Capacities);
     }
   }
Index: /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment/3.3/Operators/Manipulators/RelocateEquipmentManipluator.cs
===================================================================
--- /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment/3.3/Operators/Manipulators/RelocateEquipmentManipluator.cs	(revision 15563)
+++ /branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment/3.3/Operators/Manipulators/RelocateEquipmentManipluator.cs	(revision 15564)
@@ -21,9 +21,7 @@
 
 using System;
-using System.Collections.Generic;
 using System.Linq;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
-using HeuristicLab.Data;
 using HeuristicLab.Encodings.IntegerVectorEncoding;
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
@@ -47,41 +45,19 @@
     }
 
-    public static void Apply(IRandom random, IntegerVector assignment, DoubleArray capacities, DoubleArray demands) {
-      int locations = capacities.Length;
+    public static void Apply(IRandom random, IntegerVector assignment, int locations, double stopProb) {
       if (locations < 2) throw new InvalidOperationException("There are not enough locations for relocation operations.");
 
-      Dictionary<int, double> usedCapacities = new Dictionary<int, double>();
-      Dictionary<int, List<int>> groupedLocations = new Dictionary<int, List<int>>();
-      for (int i = 0; i < locations; i++) {
-        usedCapacities[i] = 0.0;
-        groupedLocations.Add(i, new List<int>());
-      }
-      for (int i = 0; i < assignment.Length; i++) {
-        usedCapacities[assignment[i]] += demands[i];
-        groupedLocations[assignment[i]].Add(i);
-      }
-
-      var overbookedLocations = usedCapacities.Where(x => capacities[x.Key] < x.Value);
-      var freeLocations = usedCapacities.Where(x => capacities[x.Key] > x.Value);
-      if (!overbookedLocations.Any() || !freeLocations.Any()) { // perform a random relocation
-        assignment[random.Next(assignment.Length)] = random.Next(locations);
-        return;
-      }
-
-      var sourceLocation = overbookedLocations.SampleRandom(random);
-      int equipmentToRelocate = groupedLocations[sourceLocation.Key].SampleRandom(random);
-      var bestFreeLocations = freeLocations.Where(x => capacities[x.Key] > x.Value + demands[equipmentToRelocate]);
-
-      if (bestFreeLocations.Any()) { // a feasible solution will still be feasible, an infeasible solution might become feasible
-        var selected = bestFreeLocations.SampleRandom(random);
-        assignment[equipmentToRelocate] = sourceLocation.Key;
-      } else { // the solution will become infeasible
-        sourceLocation = freeLocations.SampleRandom(random);
-        assignment[equipmentToRelocate] = sourceLocation.Key;
+      var equipments = Enumerable.Range(0, assignment.Length).Shuffle(random);
+      foreach (var equipment in equipments) {
+        var oldLoc = assignment[equipment];
+        var newLoc =random.Next(locations - 1);
+        if (newLoc >= oldLoc) newLoc++; // don't reassign to current loc
+        assignment[equipment] = newLoc;
+        if (random.NextDouble() < stopProb) break;
       }
     }
 
     protected override void Manipulate(IRandom random, IntegerVector vector, GQAPInstance problemInstance) {
-      Apply(random, vector, problemInstance.Capacities, problemInstance.Demands);
+      Apply(random, vector, problemInstance.Capacities.Length, 4.0 / problemInstance.Demands.Length);
     }
   }
