#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);
}
}
}