#region License Information
/* HeuristicLab
* Copyright (C) 2002-2013 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 HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Data;
using HeuristicLab.Optimization;
using HeuristicLab.Parameters;
using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
using HeuristicLab.Problems.VehicleRouting.Interfaces;
using HeuristicLab.Problems.VehicleRouting.ProblemInstances;
using HeuristicLab.Problems.VehicleRouting.Variants;
namespace HeuristicLab.Problems.VehicleRouting.Encodings.Potvin {
[Item("PushForwardInsertionCreator", "The push forward insertion heuristic. It is implemented as described in Sam, and Thangiah, R. (1999). A Hybrid Genetic Algorithms, Simulated Annealing and Tabu Search Heuristic for Vehicle Routing Problems with Time Windows. Practical Handbook of Genetic Algorithms, Volume III, pp 347–381.")]
[StorableClass]
public sealed class PushForwardInsertionCreator : PotvinCreator, IStochasticOperator {
#region IStochasticOperator Members
public ILookupParameter RandomParameter {
get { return (LookupParameter)Parameters["Random"]; }
}
#endregion
public IValueParameter Alpha {
get { return (IValueParameter)Parameters["Alpha"]; }
}
public IValueParameter AlphaVariance {
get { return (IValueParameter)Parameters["AlphaVariance"]; }
}
public IValueParameter Beta {
get { return (IValueParameter)Parameters["Beta"]; }
}
public IValueParameter BetaVariance {
get { return (IValueParameter)Parameters["BetaVariance"]; }
}
public IValueParameter Gamma {
get { return (IValueParameter)Parameters["Gamma"]; }
}
public IValueParameter GammaVariance {
get { return (IValueParameter)Parameters["GammaVariance"]; }
}
[StorableConstructor]
private PushForwardInsertionCreator(bool deserializing) : base(deserializing) { }
public PushForwardInsertionCreator()
: base() {
Parameters.Add(new LookupParameter("Random", "The pseudo random number generator."));
Parameters.Add(new ValueParameter("Alpha", "The alpha value.", new DoubleValue(0.7)));
Parameters.Add(new ValueParameter("AlphaVariance", "The alpha variance.", new DoubleValue(0.5)));
Parameters.Add(new ValueParameter("Beta", "The beta value.", new DoubleValue(0.1)));
Parameters.Add(new ValueParameter("BetaVariance", "The beta variance.", new DoubleValue(0.07)));
Parameters.Add(new ValueParameter("Gamma", "The gamma value.", new DoubleValue(0.2)));
Parameters.Add(new ValueParameter("GammaVariance", "The gamma variance.", new DoubleValue(0.14)));
}
public override IDeepCloneable Clone(Cloner cloner) {
return new PushForwardInsertionCreator(this, cloner);
}
private PushForwardInsertionCreator(PushForwardInsertionCreator original, Cloner cloner)
: base(original, cloner) {
}
// use the Box-Mueller transform in the polar form to generate a N(0,1) random variable out of two uniformly distributed random variables
private static double Gauss(IRandom random) {
double u = 0.0, v = 0.0, s = 0.0;
do {
u = (random.NextDouble() * 2) - 1;
v = (random.NextDouble() * 2) - 1;
s = Math.Sqrt(u * u + v * v);
} while (s < Double.Epsilon || s > 1);
return u * Math.Sqrt((-2.0 * Math.Log(s)) / s);
}
private static double N(double mu, double sigma, IRandom random) {
return mu + (sigma * Gauss(random)); // transform the random variable sampled from N(0,1) to N(mu,sigma)
}
private static double GetDistance(int start, int end, IVRPProblemInstance problemInstance) {
double distance = 0.0;
double startX = problemInstance.Coordinates[start, 0];
double startY = problemInstance.Coordinates[start, 1];
double endX = problemInstance.Coordinates[end, 0];
double endY = problemInstance.Coordinates[end, 1];
distance =
Math.Sqrt(
Math.Pow(startX - endX, 2) +
Math.Pow(startY - endY, 2));
return distance;
}
private static int GetNearestDepot(IVRPProblemInstance problemInstance, List depots, int customer,
double alpha, double beta, double gamma, out double minCost) {
int nearest = -1;
minCost = double.MaxValue;
int depotCount = 1;
IMultiDepotProblemInstance mdp = problemInstance as IMultiDepotProblemInstance;
if (mdp != null) {
depotCount = mdp.Depots.Value;
}
foreach (int depot in depots) {
double x0 = problemInstance.Coordinates[depot, 0];
double y0 = problemInstance.Coordinates[depot, 1];
double distance = GetDistance(customer + depotCount - 1, depot, problemInstance);
double dueTime = 0;
if (problemInstance is ITimeWindowedProblemInstance)
dueTime = (problemInstance as ITimeWindowedProblemInstance).DueTime[customer + depotCount - 1];
if (dueTime == double.MaxValue)
dueTime = 0;
double x = problemInstance.Coordinates[customer + depotCount - 1, 0];
double y = problemInstance.Coordinates[customer + depotCount - 1, 1];
double cost = alpha * distance + // distance 0 <-> City[i]
-beta * dueTime + // latest arrival time
-gamma * ((Math.Atan2(y - y0, x - x0) + Math.PI) / (2.0 * Math.PI) * distance); // polar angle
if (cost < minCost) {
minCost = cost;
nearest = depot;
}
}
return nearest;
}
private static List SortCustomers(IVRPProblemInstance problemInstance, List customers, List depots,
Dictionary depotAssignment,
double alpha, double beta, double gamma) {
List sortedCustomers = new List();
depotAssignment.Clear();
List costList = new List();
for (int i = 0; i < customers.Count; i++) {
double cost;
int depot = GetNearestDepot(problemInstance, depots, customers[i],
alpha, beta, gamma,
out cost);
depotAssignment[customers[i]] = depot;
int index = 0;
while (index < costList.Count && costList[index] < cost) index++;
costList.Insert(index, cost);
sortedCustomers.Insert(index, customers[i]);
}
return sortedCustomers;
}
private static bool RemoveUnusedDepots(List depots, Dictionary> vehicles) {
List toBeRemoved = new List();
foreach (int depot in depots) {
if (vehicles[depot].Count == 0)
toBeRemoved.Add(depot);
}
foreach (int depot in toBeRemoved) {
depots.Remove(depot);
vehicles.Remove(depot);
}
return toBeRemoved.Count > 0;
}
public static PotvinEncoding CreateSolution(IVRPProblemInstance problemInstance, IRandom random,
double alphaValue = 0.7, double betaValue = 0.1, double gammaValue = 0.2,
double alphaVariance = 0.5, double betaVariance = 0.07, double gammaVariance = 0.14) {
PotvinEncoding result = new PotvinEncoding(problemInstance);
IPickupAndDeliveryProblemInstance pdp = problemInstance as IPickupAndDeliveryProblemInstance;
IMultiDepotProblemInstance mdp = problemInstance as IMultiDepotProblemInstance;
double alpha, beta, gamma;
alpha = N(alphaValue, Math.Sqrt(alphaVariance), random);
beta = N(betaValue, Math.Sqrt(betaVariance), random);
gamma = N(gammaValue, Math.Sqrt(gammaVariance), random);
List unroutedCustomers = new List();
for (int i = 1; i <= problemInstance.Cities.Value; i++) {
if (pdp == null || (problemInstance.GetDemand(i) >= 0))
unroutedCustomers.Add(i);
}
List depots = new List();
if (mdp != null) {
for (int i = 0; i < mdp.Depots.Value; i++) {
depots.Add(i);
}
} else {
depots.Add(0);
}
Dictionary> vehicles = new Dictionary>();
foreach (int depot in depots) {
vehicles[depot] = new List();
int vehicleCount = problemInstance.Vehicles.Value;
if (mdp != null) {
for (int vehicle = 0; vehicle < mdp.VehicleDepotAssignment.Length; vehicle++) {
if (mdp.VehicleDepotAssignment[vehicle] == depot) {
vehicles[depot].Add(vehicle);
}
}
} else {
for (int vehicle = 0; vehicle < vehicleCount; vehicle++) {
vehicles[depot].Add(vehicle);
}
}
}
RemoveUnusedDepots(depots, vehicles);
Dictionary depotAssignment = new Dictionary();
unroutedCustomers = SortCustomers(
problemInstance, unroutedCustomers, depots, depotAssignment,
alpha, beta, gamma);
/////////
Tour tour = new Tour();
result.Tours.Add(tour);
int currentCustomer = unroutedCustomers[0];
unroutedCustomers.RemoveAt(0);
int currentDepot = depotAssignment[currentCustomer];
int currentVehicle = vehicles[currentDepot][0];
vehicles[currentDepot].RemoveAt(0);
if (RemoveUnusedDepots(depots, vehicles)) {
unroutedCustomers = SortCustomers(
problemInstance, unroutedCustomers, depots, depotAssignment,
alpha, beta, gamma);
}
result.VehicleAssignment[result.Tours.Count - 1] = currentVehicle;
tour.Stops.Add(currentCustomer);
if (pdp != null) {
tour.Stops.Add(pdp.GetPickupDeliveryLocation(currentCustomer));
}
////////
while (unroutedCustomers.Count > 0) {
double minimumCost = double.MaxValue;
int customer = -1;
int indexOfMinimumCost = -1;
int indexOfMinimumCost2 = -1;
foreach (int unrouted in unroutedCustomers) {
VRPEvaluation eval = problemInstance.EvaluateTour(tour, result);
double originalCosts = eval.Quality;
for (int i = 0; i <= tour.Stops.Count; i++) {
tour.Stops.Insert(i, unrouted);
eval = problemInstance.EvaluateTour(tour, result);
double tourCost = eval.Quality - originalCosts;
if (pdp != null) {
for (int j = i + 1; j <= tour.Stops.Count; j++) {
bool feasible;
double cost = tourCost +
problemInstance.GetInsertionCosts(eval, result, pdp.GetPickupDeliveryLocation(unrouted), 0, j, out feasible);
if (cost < minimumCost && feasible) {
customer = unrouted;
minimumCost = cost;
indexOfMinimumCost = i;
indexOfMinimumCost2 = j;
}
}
} else {
double cost = tourCost;
bool feasible = problemInstance.Feasible(eval);
if (cost < minimumCost && feasible) {
customer = unrouted;
minimumCost = cost;
indexOfMinimumCost = i;
}
}
tour.Stops.RemoveAt(i);
}
}
if (indexOfMinimumCost == -1 && vehicles.Count == 0) {
indexOfMinimumCost = tour.Stops.Count;
indexOfMinimumCost2 = tour.Stops.Count + 1;
customer = unroutedCustomers[0];
}
// insert customer if found
if (indexOfMinimumCost != -1) {
tour.Stops.Insert(indexOfMinimumCost, customer);
if (pdp != null) {
tour.Stops.Insert(indexOfMinimumCost2, pdp.GetPickupDeliveryLocation(customer));
}
unroutedCustomers.Remove(customer);
} else { // no feasible customer found
tour = new Tour();
result.Tours.Add(tour);
currentCustomer = unroutedCustomers[0];
unroutedCustomers.RemoveAt(0);
currentDepot = depotAssignment[currentCustomer];
currentVehicle = vehicles[currentDepot][0];
vehicles[currentDepot].RemoveAt(0);
if (RemoveUnusedDepots(depots, vehicles)) {
unroutedCustomers = SortCustomers(
problemInstance, unroutedCustomers, depots, depotAssignment,
alpha, beta, gamma);
}
result.VehicleAssignment[result.Tours.Count - 1] = currentVehicle;
tour.Stops.Add(currentCustomer);
if (pdp != null) {
tour.Stops.Add(pdp.GetPickupDeliveryLocation(currentCustomer));
}
}
}
if (mdp != null) {
List availableVehicles = new List();
for (int i = 0; i < mdp.Vehicles.Value; i++)
availableVehicles.Add(i);
for (int i = 0; i < result.VehicleAssignment.Length; i++) {
if (result.VehicleAssignment[i] != -1)
availableVehicles.Remove(result.VehicleAssignment[i]);
}
for (int i = 0; i < result.VehicleAssignment.Length; i++) {
if (result.VehicleAssignment[i] == -1) {
result.VehicleAssignment[i] = availableVehicles[0];
availableVehicles.RemoveAt(0);
}
}
}
return result;
}
public override IOperation InstrumentedApply() {
VRPToursParameter.ActualValue = CreateSolution(ProblemInstance, RandomParameter.ActualValue,
Alpha.Value.Value, Beta.Value.Value, Gamma.Value.Value,
AlphaVariance.Value.Value, BetaVariance.Value.Value, GammaVariance.Value.Value);
return base.InstrumentedApply();
}
}
}