#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 . */ #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.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, Encodings.PermutationEncoding.Permutation, PermutationMemPRPopulationContext, PermutationMemPRSolutionContext> { private const double UncommonBitSubsetMutationProbabilityMagicConst = 0.05; #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>()) SolutionModelTrainerParameter.ValidValues.Add(trainer); foreach (var localSearch in ApplicationManager.Manager.GetInstances>()) { LocalSearchParameter.ValidValues.Add(localSearch); } } public override IDeepCloneable Clone(Cloner cloner) { return new PermutationMemPR(this, cloner); } protected override bool Eq(ISingleObjectiveSolutionScope a, ISingleObjectiveSolutionScope b) { return new PermutationEqualityComparer().Equals(a.Solution, b.Solution); } protected override double Dist(ISingleObjectiveSolutionScope a, ISingleObjectiveSolutionScope b) { return Dist(a.Solution, b.Solution); } private static double Dist(Encodings.PermutationEncoding.Permutation a, Encodings.PermutationEncoding.Permutation b) { return 1.0 - PermutationSimilarityCalculator.CalculateSimilarity(a, b); } protected override ISingleObjectiveSolutionScope 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(code, creator.PermutationParameter.ActualName, fitness, Problem.Evaluator.QualityParameter.ActualName) { Parent = Context.Scope }; } protected override ISolutionSubspace CalculateSubspace(IEnumerable solutions, bool inverse = false) { var subspace = new bool[Problem.Encoding.Length, Problem.Encoding.PermutationTypeParameter.Value.Value == PermutationTypes.Absolute ? 1 : Problem.Encoding.Length]; switch (Problem.Encoding.PermutationTypeParameter.Value.Value) { case PermutationTypes.Absolute: { if (inverse) { for (var i = 0; i < subspace.GetLength(0); i++) subspace[i, 0] = true; } var first = solutions.First(); foreach (var s in solutions.Skip(1)) { 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 first = solutions.First(); var placedFirst = new int[first.Length]; for (var i = 0; i < first.Length; i++) { placedFirst[first[i]] = i; } foreach (var s in solutions.Skip(1)) { 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 first = solutions.First(); var placedFirst = new int[first.Length]; for (var i = 0; i < first.Length; i++) { placedFirst[first[i]] = i; } foreach (var s in solutions.Skip(1)) { 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}", Problem.Encoding.PermutationTypeParameter.Value.Value)); } return new PermutationSolutionSubspace(subspace); } protected override void TabuWalk(ISingleObjectiveSolutionScope scope, int steps, CancellationToken token, ISolutionSubspace subspace = null) { var wrapper = new EvaluationWrapper(Context.Problem, scope); var quality = scope.Fitness; try { TabuWalk(Context.Random, scope.Solution, wrapper.Evaluate, ref quality, Context.HcSteps, subspace != null ? ((PermutationSolutionSubspace)subspace).Subspace : null); } finally { scope.Fitness = quality; } } public static void TabuWalk(IRandom random, Encodings.PermutationEncoding.Permutation perm, Func eval, ref double quality, int maxIterations = int.MaxValue, bool[,] noTouch = null) { switch (perm.PermutationType) { case PermutationTypes.Absolute: TabuWalkSwap(random, perm, eval, ref quality, maxIterations, noTouch); break; case PermutationTypes.RelativeDirected: TabuWalkShift(random, perm, eval, ref quality, maxIterations, noTouch); break; case PermutationTypes.RelativeUndirected: TabuWalkOpt(random, perm, eval, ref quality, maxIterations, noTouch); 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 static void TabuWalkSwap(IRandom random, Encodings.PermutationEncoding.Permutation perm, Func eval, ref double quality, int maxIterations = int.MaxValue, bool[,] noTouch = null) { if (double.IsNaN(quality)) quality = eval(perm, random); Encodings.PermutationEncoding.Permutation bestOfTheWalk = null; double bestOfTheWalkF = double.NaN; var current = (Encodings.PermutationEncoding.Permutation)perm.Clone(); var currentF = quality; var overallImprovement = false; //Console.WriteLine("Current {0}", string.Join(", ", current)); 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[i, current[i]] = currentF; } for (var iter = 0; iter < maxIterations; iter++) { var allTabu = true; var bestOfTheRestF = double.NaN; Swap2Move bestOfTheRest = null; var improved = false; //LogAlways("TabuWalk ({0}/{2}): {1} ({3})", iter, currentF, maxIterations, string.Join(", ", current)); foreach (var swap in ExhaustiveSwap2MoveGenerator.Generate(current).Shuffle(random)) { if (noTouch != null && (noTouch[swap.Index1, 0] || noTouch[swap.Index2, 0])) continue; var h = current[swap.Index1]; current[swap.Index1] = current[swap.Index2]; current[swap.Index2] = h; var q = eval(current, random); if (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 = q >= tabu[swap.Index1, current[swap.Index1]] && q >= tabu[swap.Index2, current[swap.Index2]]; if (!isTabu) allTabu = false; if (q < currentF && !isTabu) { if (double.IsNaN(bestOfTheWalkF) || bestOfTheWalkF > q) { bestOfTheWalk = (Encodings.PermutationEncoding.Permutation)current.Clone(); bestOfTheWalkF = q; } 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]] = Math.Min(q, tabu[swap.Index1, current[swap.Index2]]); //LogAlways("Making Tabu [{0},{1}] = {2}", swap.Index1, current[swap.Index2], tabu[swap.Index1, current[swap.Index2]]); tabu[swap.Index2, current[swap.Index1]] = Math.Min(q, tabu[swap.Index2, current[swap.Index1]]); //LogAlways("Making Tabu [{0},{1}] = {2}", swap.Index2, current[swap.Index1], tabu[swap.Index2, current[swap.Index1]]); } else { // undo the move if (!isTabu && (double.IsNaN(bestOfTheRestF) || q < bestOfTheRestF)) { bestOfTheRest = swap; bestOfTheRestF = q; } current[swap.Index2] = current[swap.Index1]; current[swap.Index1] = h; } } if (!allTabu && !improved) { tabu[bestOfTheRest.Index1, current[bestOfTheRest.Index1]] = Math.Min(currentF, tabu[bestOfTheRest.Index1, current[bestOfTheRest.Index1]]); //LogAlways("Making Tabu [{0},{1}] = {2}", bestOfTheRest.Index1, current[bestOfTheRest.Index1], tabu[bestOfTheRest.Index1, current[bestOfTheRest.Index1]]); tabu[bestOfTheRest.Index2, current[bestOfTheRest.Index2]] = Math.Min(currentF, tabu[bestOfTheRest.Index2, current[bestOfTheRest.Index2]]); //LogAlways("Making Tabu [{0},{1}] = {2}", bestOfTheRest.Index2, current[bestOfTheRest.Index2], tabu[bestOfTheRest.Index2, current[bestOfTheRest.Index2]]); var h = current[bestOfTheRest.Index1]; current[bestOfTheRest.Index1] = current[bestOfTheRest.Index2]; current[bestOfTheRest.Index2] = h; currentF = bestOfTheRestF; } else if (allTabu) break; } if (!overallImprovement && bestOfTheWalk != null) { quality = bestOfTheWalkF; for (var i = 0; i < current.Length; i++) perm[i] = bestOfTheWalk[i]; } } public static void TabuWalkShift(IRandom random, Encodings.PermutationEncoding.Permutation perm, Func eval, ref double quality, int maxIterations = int.MaxValue, bool[,] noTouch = null) { return; } public static void TabuWalkOpt(IRandom random, Encodings.PermutationEncoding.Permutation perm, Func eval, ref double quality, int maxIterations = int.MaxValue, bool[,] noTouch = null) { if (double.IsNaN(quality)) quality = eval(perm, random); Encodings.PermutationEncoding.Permutation bestOfTheWalk = null; double bestOfTheWalkF = double.NaN; var current = (Encodings.PermutationEncoding.Permutation)perm.Clone(); var currentF = quality; var overallImprovement = false; //Console.WriteLine("Current {0}", string.Join(", ", current)); 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; } for (var iter = 0; iter < maxIterations; 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 (noTouch != null && ((noTouch[current[prev], current[opt.Index1]]) || (noTouch[current[opt.Index2], current[next]]))) continue; InversionManipulator.Apply(current, opt.Index1, opt.Index2); var q = eval(current, random); if (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 = q >= tabu[current[prev], current[opt.Index1]] && q >= tabu[current[opt.Index2], current[next]]; if (!isTabu) allTabu = false; if (q < currentF && !isTabu) { if (double.IsNaN(bestOfTheWalkF) || bestOfTheWalkF > q) { bestOfTheWalk = (Encodings.PermutationEncoding.Permutation)current.Clone(); bestOfTheWalkF = q; } improved = true; // perform the move currentF = q; // mark that to move them to their previous position requires to make an improvement 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 && (double.IsNaN(bestOfTheRestF) || q < bestOfTheRestF)) { bestOfTheRest = opt; bestOfTheRestF = q; } InversionManipulator.Apply(current, opt.Index1, opt.Index2); } } if (!allTabu && !improved) { var prev = bestOfTheRest.Index1 - 1; var next = (bestOfTheRest.Index2 + 1) % current.Length; if (prev < 0) prev += current.Length; 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]]); InversionManipulator.Apply(current, bestOfTheRest.Index1, bestOfTheRest.Index2); currentF = bestOfTheRestF; } else if (allTabu) break; } if (!overallImprovement && bestOfTheWalk != null) { quality = bestOfTheWalkF; for (var i = 0; i < current.Length; i++) perm[i] = bestOfTheWalk[i]; } } protected override ISingleObjectiveSolutionScope Cross(ISingleObjectiveSolutionScope p1, ISingleObjectiveSolutionScope p2, CancellationToken token) { Encodings.PermutationEncoding.Permutation child = null; if (p1.Solution.PermutationType == PermutationTypes.Absolute) { child = CyclicCrossover.Apply(Context.Random, p1.Solution, p2.Solution); } else if (p1.Solution.PermutationType == PermutationTypes.RelativeDirected) { child = PartiallyMatchedCrossover.Apply(Context.Random, p1.Solution, p2.Solution); } else if (p1.Solution.PermutationType == PermutationTypes.RelativeUndirected) { child = EdgeRecombinationCrossover.Apply(Context.Random, p1.Solution, p2.Solution); } else throw new ArgumentException(string.Format("Unknown permutation type {0}", p1.Solution.PermutationType)); if (VALIDATE && !child.Validate()) throw new ArgumentException("Cross produced invalid child"); return ToScope(child); } protected override void Mutate(ISingleObjectiveSolutionScope offspring, CancellationToken token, ISolutionSubspace subspace = null) { Mutate(Context.Random, offspring.Solution, UncommonBitSubsetMutationProbabilityMagicConst, subspace != null ? ((PermutationSolutionSubspace)subspace).Subspace : null); } public static void Mutate(IRandom random, Encodings.PermutationEncoding.Permutation perm, double p, bool[,] subspace) { switch (perm.PermutationType) { case PermutationTypes.Absolute: MutateSwap(random, perm, p, subspace); break; case PermutationTypes.RelativeDirected: MutateShift(random, perm, p, subspace); break; case PermutationTypes.RelativeUndirected: MutateOpt(random, perm, p, subspace); break; default: throw new ArgumentException(string.Format("Permutation type {0} is not known", perm.PermutationType)); } if (VALIDATE && !perm.Validate()) throw new ArgumentException("Mutate produced invalid child"); } public static void MutateSwap(IRandom random, Encodings.PermutationEncoding.Permutation perm, double p, bool[,] subspace) { //Log("BEFOR: {0}", string.Join(", ", lle)); // The goal of the mutation is to disrupt crossover when it's in an agreeing position var options = new List(Enumerable.Range(0, perm.Length).Where(x => subspace == null || !subspace[x, 0])); if (options.Count < 1) return; for (var i = options.Count - 1; i > 0; i--) { if (random.NextDouble() < p) { var j = random.Next(0, i); var h = perm[options[i]]; perm[options[i]] = perm[options[j]]; perm[options[j]] = h; if (subspace != null) { subspace[options[i], 0] = true; subspace[options[j], 0] = true; } } } } public static void MutateShift(IRandom random, Encodings.PermutationEncoding.Permutation perm, double p, bool[,] subspace) { //Log("BEFOR: {0}", string.Join(", ", lle)); // The goal of the mutation is to disrupt crossover when it's in an agreeing position foreach (var shift in ExhaustiveInsertionMoveGenerator.Generate(perm).Shuffle(random)) { var prev1 = shift.Index1 - 1; var next1 = (shift.Index1 + 1) % perm.Length; if (prev1 < 0) prev1 += perm.Length; var prev3 = shift.Index3 - 1; var next3 = (shift.Index3 + 1) % perm.Length; if (prev3 < 0) prev3 += perm.Length; if (subspace == null || !(subspace[perm[prev1], perm[shift.Index1]] && subspace[perm[shift.Index1], perm[next1]] && subspace[perm[prev3], perm[shift.Index3]] && subspace[perm[shift.Index3], perm[next3]])) { if (random.NextDouble() < p) { if (subspace != null) { subspace[perm[prev1], perm[shift.Index1]] = true; subspace[perm[shift.Index1], perm[next1]] = true; subspace[perm[prev3], perm[shift.Index3]] = true; subspace[perm[shift.Index3], perm[next3]] = true; } TranslocationManipulator.Apply(perm, shift.Index1, shift.Index2, shift.Index3); return; } } } } public static void MutateOpt(IRandom random, Encodings.PermutationEncoding.Permutation perm, double p, bool[,] subspace) { //Log("BEFOR: {0}", string.Join(", ", lle)); // The goal of the mutation is to disrupt crossover when it's in an agreeing position foreach (var opt in ExhaustiveInversionMoveGenerator.Generate(perm).Shuffle(random)) { var prev = opt.Index1 - 1; var next = (opt.Index2 + 1) % perm.Length; if (prev < 0) prev += perm.Length; if (subspace == null || !(subspace[perm[prev], perm[opt.Index1]] && subspace[perm[opt.Index2], perm[next]])) { if (random.NextDouble() < p) { if (subspace != null) { subspace[perm[prev], perm[opt.Index1]] = true; subspace[perm[opt.Index1], perm[prev]] = true; subspace[perm[opt.Index2], perm[next]] = true; subspace[perm[next], perm[opt.Index2]] = true; } InversionManipulator.Apply(perm, opt.Index1, opt.Index2); return; } } } } protected override ISingleObjectiveSolutionScope Relink(ISingleObjectiveSolutionScope a, ISingleObjectiveSolutionScope b, CancellationToken token) { if (double.IsNaN(a.Fitness)) Evaluate(a, token); if (double.IsNaN(b.Fitness)) Evaluate(b, token); if (Context.Random.NextDouble() < 0.5) return IsBetter(a, b) ? Relink(a, b, token, false) : Relink(b, a, token, true); else return IsBetter(a, b) ? Relink(b, a, token, true) : Relink(a, b, token, false); } protected virtual ISingleObjectiveSolutionScope Relink(ISingleObjectiveSolutionScope betterScope, ISingleObjectiveSolutionScope worseScope, CancellationToken token, bool fromWorseToBetter) { var wrapper = new EvaluationWrapper(Problem, betterScope); double quality; return ToScope(Relink(Context.Random, betterScope.Solution, worseScope.Solution, wrapper.Evaluate, out quality)); } public static Encodings.PermutationEncoding.Permutation Relink(IRandom random, Encodings.PermutationEncoding.Permutation p1, Encodings.PermutationEncoding.Permutation p2, Func eval, 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); case PermutationTypes.RelativeDirected: return RelinkShift(random, p1, p2, eval, out best); case PermutationTypes.RelativeUndirected: return RelinkOpt(random, p1, p2, eval, out best); default: throw new ArgumentException(string.Format("Unknown permutation type {0}", p1.PermutationType)); } } public static Encodings.PermutationEncoding.Permutation RelinkSwap(IRandom random, Encodings.PermutationEncoding.Permutation p1, Encodings.PermutationEncoding.Permutation p2, Func eval, out double best) { 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; //Log(string.Join(", ", child)); 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, random); if (double.IsNaN(bestChange) || 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; //Log(string.Join(", ", child)); if (double.IsNaN(best) || bestChange < best) { if (Dist(child, p2) > 0) { best = bestChange; bestChild = (Encodings.PermutationEncoding.Permutation)child.Clone(); } } options.RemoveAt(bestOption); } } //Log(string.Join(", ", p2)); 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"); if (bestChild == null) best = eval(child, random); return bestChild ?? child; } public static Encodings.PermutationEncoding.Permutation RelinkShift(IRandom random, Encodings.PermutationEncoding.Permutation p1, Encodings.PermutationEncoding.Permutation p2, Func eval, out double best) { 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, random); if (double.IsNaN(bestChange) || 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 (double.IsNaN(best) || bestChange < best) { best = bestChange; bestChild = (Encodings.PermutationEncoding.Permutation)child.Clone(); } } } while (!double.IsNaN(bestChange)); if (VALIDATE && !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; } public static Encodings.PermutationEncoding.Permutation RelinkOpt(IRandom random, Encodings.PermutationEncoding.Permutation p1, Encodings.PermutationEncoding.Permutation p2, Func eval, out double best) { 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>(); var undoStack = new Stack>(); do { Queue> bestQueue = null; bestChange = double.NaN; //Log("{0}", string.Join(" ", child)); 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, random); if (double.IsNaN(bestChange) || moveF < bestChange) { bestChange = moveF; bestQueue = new Queue>(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(); //Log("Applying opt {0} {1}", m.Item1, m.Item2); //Log("{0}", string.Join(" ", child)); Opt(child, m.Item1, m.Item2); //Log("{0}", string.Join(" ", child)); } for (var i = 0; i < child.Length; i++) invChild[child[i]] = i; if (double.IsNaN(best) || bestChange < best) { best = bestChange; bestChild = (Encodings.PermutationEncoding.Permutation)child.Clone(); } } } while (!double.IsNaN(bestChange)); //Log("{0}", string.Join(" ", p2)); if (VALIDATE && !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; } 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 Move(Encodings.PermutationEncoding.Permutation child, int from, int to) { if (child.PermutationType == PermutationTypes.Absolute) { // swap Swap(child, from, to); } else if (child.PermutationType == PermutationTypes.RelativeDirected) { // shift Shift(child, from, to); } else if (child.PermutationType == PermutationTypes.RelativeUndirected) { // opt Opt(child, from, to); } else throw new ArgumentException(string.Format("Unknown permutation type {0}", child.PermutationType)); } private static void Move(PermutationTypes type, bool[] arr, int from, int to) { switch (type) { case PermutationTypes.Absolute: { /*var h = arr[from]; arr[from] = arr[to]; arr[to] = h;*/ arr[from] = false; arr[to] = false; break; } case PermutationTypes.RelativeDirected: { var original = (bool[])arr.Clone(); var number = original[from]; int i = 0; // index in new permutation int j = 0; // index in old permutation while (i < original.Length) { if (j == from) { j++; } if (i == to) { arr[i] = number; i++; } if ((i < original.Length) && (j < original.Length)) { arr[i] = original[j]; i++; j++; } } break; } case PermutationTypes.RelativeUndirected: { if (from > to) { var hu = from; from = to; to = hu; } for (int i = 0; i <= (to - from) / 2; i++) { // invert permutation between breakpoints var temp = arr[from + i]; arr[from + i] = arr[to - i]; arr[to - i] = temp; } break; } default: throw new ArgumentException(string.Format("Unknown permutation type {0}", type)); } } 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); } private static void Opt(Encodings.PermutationEncoding.Permutation child, int from, int to) { if (from > to) { var h = from; from = to; to = h; } InversionManipulator.Apply(child, from, to); } } }