#region License Information
/* HeuristicLab
* Copyright (C) 2002-2019 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 HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Data;
using HeuristicLab.Optimization;
using HeuristicLab.Optimization.Operators;
using HeuristicLab.Parameters;
using HEAL.Attic;
using HeuristicLab.Problems.VehicleRouting.Encodings.Potvin;
using HeuristicLab.Problems.VehicleRouting.Interfaces;
using HeuristicLab.Problems.VehicleRouting.ProblemInstances;
using HeuristicLab.Problems.VehicleRouting.Variants;
namespace HeuristicLab.Problems.VehicleRouting {
///
/// An operator which relinks paths between VRP solutions.
///
[Item("VRPPathRelinker", "An operator which relinks paths between VRP solutions.")]
[StorableType("C0C17982-BC36-4DF9-8C33-2B6F9A19CA53")]
public sealed class VRPPathRelinker : SingleObjectivePathRelinker, IGeneralVRPOperator, IStochasticOperator {
#region Parameter properties
public IValueParameter IterationsParameter {
get { return (IValueParameter)Parameters["Iterations"]; }
}
public ILookupParameter ProblemInstanceParameter {
get { return (ILookupParameter)Parameters["ProblemInstance"]; }
}
public ILookupParameter RandomParameter {
get { return (ILookupParameter)Parameters["Random"]; }
}
public IValueParameter SampleSizeParameter {
get { return (IValueParameter)Parameters["SampleSize"]; }
}
#endregion
[StorableConstructor]
private VRPPathRelinker(StorableConstructorFlag _) : base(_) { }
private VRPPathRelinker(VRPPathRelinker original, Cloner cloner) : base(original, cloner) { }
public VRPPathRelinker()
: base() {
Parameters.Add(new ValueParameter("Iterations", "The number of iterations the operator should perform.", new IntValue(50)));
Parameters.Add(new LookupParameter("ProblemInstance", "The VRP problem instance"));
Parameters.Add(new LookupParameter("Random", "The pseudo random number generator which should be used for stochastic manipulation operators."));
Parameters.Add(new ValueParameter("SampleSize", "The number of moves that should be executed.", new IntValue(10)));
}
public override IDeepCloneable Clone(Cloner cloner) {
return new VRPPathRelinker(this, cloner);
}
public static ItemArray Apply(PotvinEncoding initiator, PotvinEncoding guide, PercentValue n, int sampleSize, int iterations, IRandom rand, IVRPProblemInstance problemInstance) {
if (initiator == null || guide == null)
throw new ArgumentException("Cannot relink path because one of the provided solutions or both are null.");
double sigma = 1.5;
double minPenalty = 0.001;
double maxPenalty = 1000000000;
var originalOverloadPenalty = new DoubleValue();
if (problemInstance is IHomogenousCapacitatedProblemInstance)
originalOverloadPenalty.Value = (problemInstance as IHomogenousCapacitatedProblemInstance).OverloadPenalty.Value;
var originalTardinessPenalty = new DoubleValue();
if (problemInstance is ITimeWindowedProblemInstance)
originalTardinessPenalty.Value = (problemInstance as ITimeWindowedProblemInstance).TardinessPenalty.Value;
PotvinEncoding current = MatchTours(initiator, guide, problemInstance);
double currentSimilarity = VRPSimilarityCalculator.CalculateSimilarity(current, guide);
IList solutions = new List();
int i = 0;
while (i < iterations && !currentSimilarity.IsAlmost(1.0)) {
var currentEval = problemInstance.Evaluate(current);
currentSimilarity = VRPSimilarityCalculator.CalculateSimilarity(current, guide);
if (currentSimilarity < 1.0) {
for (int sample = 0; sample < sampleSize; sample++) {
var next = current.Clone() as PotvinEncoding;
int neighborhood = rand.Next(3);
switch (neighborhood) {
case 0: next = RouteBasedXOver(next, guide, rand,
problemInstance);
break;
case 1: next = SequenceBasedXOver(next, guide, rand,
problemInstance);
break;
case 2: GuidedRelocateMove(next, guide, rand);
break;
}
next = MatchTours(next, guide, problemInstance);
var nextEval = problemInstance.Evaluate(next);
if ((nextEval.Quality < currentEval.Quality)) {
current = next;
solutions.Add(current);
break;
}
}
if (problemInstance is IHomogenousCapacitatedProblemInstance) {
if (((CVRPEvaluation)currentEval).Overload > 0) {
(problemInstance as IHomogenousCapacitatedProblemInstance).OverloadPenalty.Value =
Math.Min(maxPenalty,
(problemInstance as IHomogenousCapacitatedProblemInstance).OverloadPenalty.Value * sigma);
} else {
(problemInstance as IHomogenousCapacitatedProblemInstance).OverloadPenalty.Value =
Math.Max(minPenalty,
(problemInstance as IHomogenousCapacitatedProblemInstance).OverloadPenalty.Value * sigma);
}
}
if (problemInstance is ITimeWindowedProblemInstance) {
if (((CVRPTWEvaluation)currentEval).Tardiness > 0) {
(problemInstance as ITimeWindowedProblemInstance).TardinessPenalty.Value =
Math.Min(maxPenalty,
(problemInstance as ITimeWindowedProblemInstance).TardinessPenalty.Value * sigma);
} else {
(problemInstance as ITimeWindowedProblemInstance).TardinessPenalty.Value =
Math.Max(minPenalty,
(problemInstance as ITimeWindowedProblemInstance).TardinessPenalty.Value / sigma);
}
}
i++;
}
}
if (problemInstance is IHomogenousCapacitatedProblemInstance)
(problemInstance as IHomogenousCapacitatedProblemInstance).OverloadPenalty.Value = originalOverloadPenalty.Value;
if (problemInstance is ITimeWindowedProblemInstance)
(problemInstance as ITimeWindowedProblemInstance).TardinessPenalty.Value = originalTardinessPenalty.Value;
return new ItemArray(ChooseSelection(solutions, n));
}
private static IList ChooseSelection(IList solutions, PercentValue n) {
IList selection = new List();
if (solutions.Count > 0) {
int noSol = (int)(solutions.Count * n.Value);
if (noSol <= 0) noSol++;
double stepSize = (double)solutions.Count / (double)noSol;
for (int i = 0; i < noSol; i++)
selection.Add(solutions.ElementAt((int)((i + 1) * stepSize - stepSize * 0.5)));
}
return selection;
}
protected override ItemArray Relink(ItemArray parents, PercentValue n) {
if (parents.Length != 2)
throw new ArgumentException("The number of parents is not equal to 2.");
if (!(parents[0] is PotvinEncoding))
parents[0] = PotvinEncoding.ConvertFrom(parents[0] as IVRPEncoding, ProblemInstanceParameter.ActualValue);
if (!(parents[1] is PotvinEncoding))
parents[1] = PotvinEncoding.ConvertFrom(parents[1] as IVRPEncoding, ProblemInstanceParameter.ActualValue);
return Apply(parents[0] as PotvinEncoding, parents[1] as PotvinEncoding, n,
SampleSizeParameter.Value.Value, IterationsParameter.Value.Value, RandomParameter.ActualValue, ProblemInstanceParameter.ActualValue);
}
private static int MatchingCities(Tour tour1, Tour tour2) {
return tour1.Stops.Intersect(tour2.Stops).Count();
}
private static PotvinEncoding MatchTours(PotvinEncoding initiator, PotvinEncoding guide, IVRPProblemInstance problemInstance) {
var result = new PotvinEncoding(problemInstance);
var used = new List();
for (int i = 0; i < initiator.Tours.Count; i++) {
used.Add(false);
}
for (int i = 0; i < guide.Tours.Count; i++) {
int bestMatch = -1;
int bestTour = -1;
for (int j = 0; j < initiator.Tours.Count; j++) {
if (!used[j]) {
int match = MatchingCities(guide.Tours[i], initiator.Tours[j]);
if (match > bestMatch) {
bestMatch = match;
bestTour = j;
}
}
}
if (bestTour != -1) {
result.Tours.Add(initiator.Tours[bestTour]);
used[bestTour] = true;
}
}
for (int i = 0; i < initiator.Tours.Count; i++) {
if (!used[i])
result.Tours.Add(initiator.Tours[i]);
}
return result;
}
#region moves
public static PotvinEncoding RouteBasedXOver(PotvinEncoding initiator, PotvinEncoding guide, IRandom random, IVRPProblemInstance problemInstance) {
return PotvinRouteBasedCrossover.Apply(random, initiator, guide, problemInstance, false);
}
public static PotvinEncoding SequenceBasedXOver(PotvinEncoding initiator, PotvinEncoding guide, IRandom random, IVRPProblemInstance problemInstance) {
return PotvinSequenceBasedCrossover.Apply(random, initiator, guide, problemInstance, false);
}
public static void GuidedRelocateMove(PotvinEncoding initiator, PotvinEncoding guide, IRandom random) {
List cities = new List();
foreach (Tour tour in initiator.Tours) {
foreach (int city in tour.Stops) {
Tour guideTour = guide.Tours.First(t => t.Stops.Contains(city));
if (guide.Tours.IndexOf(guideTour) != initiator.Tours.IndexOf(tour)) {
cities.Add(city);
}
}
}
if (cities.Count == 0) {
RelocateMove(initiator, random);
} else {
int city = cities[random.Next(cities.Count)];
Tour tour = initiator.Tours.First(t => t.Stops.Contains(city));
tour.Stops.Remove(city);
Tour guideTour = guide.Tours.First(t => t.Stops.Contains(city));
int guideTourIndex = guide.Tours.IndexOf(guideTour);
if (guideTourIndex < initiator.Tours.Count) {
Tour tour2 = initiator.Tours[guideTourIndex];
int guideIndex = guideTour.Stops.IndexOf(city);
if (guideIndex == 0) {
tour2.Stops.Insert(0, city);
} else {
int predecessor = guideTour.Stops[guideIndex - 1];
int initIndex = tour2.Stops.IndexOf(predecessor);
if (initIndex != -1) {
tour2.Stops.Insert(initIndex + 1, city);
} else {
if (guideIndex == guideTour.Stops.Count - 1) {
tour2.Stops.Insert(tour2.Stops.Count, city);
} else {
int sucessor = guideTour.Stops[guideIndex + 1];
initIndex = tour2.Stops.IndexOf(sucessor);
if (initIndex != -1) {
tour2.Stops.Insert(initIndex, city);
} else {
tour2.Stops.Insert(random.Next(tour2.Stops.Count + 1), city);
}
}
}
}
} else {
Tour tour2 = new Tour();
tour2.Stops.Add(city);
initiator.Tours.Add(tour2);
}
if (tour.Stops.Count == 0)
initiator.Tours.Remove(tour);
}
}
public static void RelocateMove(PotvinEncoding individual, IRandom random) {
int cities = individual.Cities;
int city = 1 + random.Next(cities);
Tour originalTour = individual.Tours.Find(t => t.Stops.Contains(city));
//consider creating new route
individual.Tours.Add(new Tour());
int position = 1 + random.Next(cities + individual.Tours.Count - 1);
if (position >= city) {
position++;
}
int originalPosition = originalTour.Stops.IndexOf(city);
originalTour.Stops.RemoveAt(originalPosition);
Tour insertionTour;
int insertionPosition;
if (position <= cities) {
insertionTour = individual.Tours.Find(t => t.Stops.Contains(position));
insertionPosition = insertionTour.Stops.IndexOf(position) + 1;
} else {
insertionTour = individual.Tours[position - cities - 1];
insertionPosition = 0;
}
insertionTour.Stops.Insert(insertionPosition, city);
individual.Tours.RemoveAll(t => t.Stops.Count == 0);
}
#endregion
}
}