#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 . */ #endregion using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Remoting.Contexts; using System.Threading; using HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Data; using HeuristicLab.Encodings.PermutationEncoding; using HeuristicLab.Optimization; using HeuristicLab.Optimization.Operators; using HeuristicLab.Parameters; using HeuristicLab.Persistence.Default.CompositeSerializers.Storable; namespace HeuristicLab.Problems.TravelingSalesman { [Item("InversionPathRelinker", "An operator that relinks solutions.")] [StorableClass] public sealed class InversionPathRelinker : SingleObjectivePathRelinker, IStochasticOperator { public IValueLookupParameter DistanceMatrixParameter { get { return (IValueLookupParameter)Parameters["DistanceMatrix"]; } } public ILookupParameter RandomParameter { get { return (ILookupParameter) Parameters["Random"]; } } [StorableConstructor] private InversionPathRelinker(bool deserializing) : base(deserializing) { } private InversionPathRelinker(InversionPathRelinker original, Cloner cloner) : base(original, cloner) { } public InversionPathRelinker() : base() { Parameters.Add(new ValueLookupParameter("DistanceMatrix")); Parameters.Add(new LookupParameter("Random")); } public override IDeepCloneable Clone(Cloner cloner) { return new InversionPathRelinker(this, cloner); } protected override ItemArray Relink(ItemArray parents, PercentValue n) { if (parents.Length != 2) throw new ArgumentException("The number of parents is not equal to 2."); var random = RandomParameter.ActualValue; var distances = DistanceMatrixParameter.ActualValue; Func eval = (p) => { double length = 0; for (int i = 0; i < p.Length - 1; i++) length += distances[p[i], p[i + 1]]; length += distances[p[p.Length - 1], p[0]]; return length; }; return new ItemArray(RelinkOpt(random, (Permutation)parents[0], (Permutation)parents[1], eval)); } public IEnumerable RelinkOpt(IRandom random, Permutation p1, Permutation p2, Func eval) { var evaluations = 0; var child = (Permutation)p1.Clone(); 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; 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); evaluations++; 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(); Opt(child, m.Item1, m.Item2); for (var i = m.Item1; i <= m.Item2; i++) invChild[child[i]] = i; } yield return (Permutation) child.Clone(); } } while (!double.IsNaN(bestChange)); } 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 Opt(Permutation child, int from, int to) { if (from > to) child.Reverse(to, from - to + 1); else child.Reverse(from, to - from + 1); } } }