#region License Information
/* HeuristicLab
* Copyright (C) 2002-2010 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.Core;
using HeuristicLab.Data;
using HeuristicLab.Encodings.PermutationEncoding;
using HeuristicLab.Optimization;
using HeuristicLab.Parameters;
using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
using HeuristicLab.Common;
using HeuristicLab.Problems.VehicleRouting.Interfaces;
using HeuristicLab.Problems.VehicleRouting.ProblemInstances;
using HeuristicLab.Problems.VehicleRouting.Variants;
using HeuristicLab.Problems.VehicleRouting.Encodings.Potvin;
using HeuristicLab.Problems.VehicleRouting;
namespace HeuristicLab.PDPSimulation.Operators {
[Item("DynPushForwardInsertionCreator", "Creates a randomly initialized VRP solution.")]
[StorableClass]
public sealed class DynPushForwardInsertionCreator : 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 DynPushForwardInsertionCreator(bool deserializing) : base(deserializing) { }
public DynPushForwardInsertionCreator()
: 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 DynPushForwardInsertionCreator(this, cloner);
}
private DynPushForwardInsertionCreator(DynPushForwardInsertionCreator 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, PotvinEncoding solution) {
double distance = 0.0;
if (problemInstance is DynPDPProblemInstance) {
distance = (problemInstance as DynPDPProblemInstance).GetDistance(start, end);
}
else if (problemInstance.UseDistanceMatrix.Value && problemInstance.DistanceMatrix != null) {
distance = problemInstance.DistanceMatrix[start, end];
} else {
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 double GetCosts(int customer, int vehicle, IVRPProblemInstance problemInstance,
Dictionary> vehicles, Dictionary vehicleTours, List vehicleUsed, PotvinEncoding solution,
double alpha, double beta, double gamma, out bool feasible, out bool existingRoute) {
int depotCount = 1;
IMultiDepotProblemInstance mdp = problemInstance as IMultiDepotProblemInstance;
if (mdp != null) {
depotCount = mdp.Depots.Value;
}
feasible = true;
double distance = GetDistance(vehicle, customer + depotCount - 1, problemInstance, solution);
if (vehicleUsed != null) {
existingRoute = vehicleUsed[vehicle];
} else {
existingRoute = true;
}
double dueTime = 0;
if (problemInstance is ITimeWindowedProblemInstance) {
ITimeWindowedProblemInstance vrptw = problemInstance as ITimeWindowedProblemInstance;
dueTime = vrptw.DueTime[customer + depotCount - 1];
}
if (vehicleTours.ContainsKey(vehicle)) {
Tour tour = vehicleTours[vehicle];
IPickupAndDeliveryProblemInstance pdp = problemInstance as IPickupAndDeliveryProblemInstance;
if (pdp != null && pdp.GetPickupDeliveryLocation(customer) > 0) {
solution.InsertPair(tour, customer, pdp.GetPickupDeliveryLocation(customer), problemInstance);
} else {
tour.Stops.Insert(solution.FindBestInsertionPlace(tour, customer), customer);
}
feasible = problemInstance.TourFeasible(tour, solution);
tour.Stops.Remove(customer);
if (pdp != null && pdp.GetPickupDeliveryLocation(customer) > 0) {
tour.Stops.Remove(pdp.GetPickupDeliveryLocation(customer));
}
}
else if (problemInstance is ITimeWindowedProblemInstance) {
IPickupAndDeliveryProblemInstance pdp = problemInstance as IPickupAndDeliveryProblemInstance;
ITimeWindowedProblemInstance vrptw = problemInstance as ITimeWindowedProblemInstance;
double time = vrptw.ReadyTime[vehicle];
time += distance;
if (time > dueTime) {
feasible = false;
} else {
time += vrptw.ServiceTime[customer - 1];
if (pdp != null && pdp.GetPickupDeliveryLocation(customer) > 0) {
int targetCustomer = pdp.GetPickupDeliveryLocation(customer);
time += GetDistance(customer + depotCount - 1, targetCustomer + depotCount - 1, problemInstance, solution);
if (time > vrptw.DueTime[targetCustomer + depotCount - 1]) {
feasible = false;
}
time += vrptw.ServiceTime[targetCustomer - 1];
time += GetDistance(targetCustomer + depotCount - 1, vehicle, problemInstance, solution);
if (time > vrptw.DueTime[vehicle]) {
feasible = false;
}
} else {
time += GetDistance(customer + depotCount - 1, vehicle, problemInstance, solution);
if (time > vrptw.DueTime[vehicle]) {
feasible = false;
}
}
}
}
if (problemInstance is IHeterogenousCapacitatedProblemInstance) {
IHeterogenousCapacitatedProblemInstance cvrp = problemInstance as IHeterogenousCapacitatedProblemInstance;
double demand = problemInstance.GetDemand(customer);
if (demand > cvrp.Capacity[vehicle])
feasible = false;
} else if (problemInstance is IHomogenousCapacitatedProblemInstance) {
IHomogenousCapacitatedProblemInstance cvrp = problemInstance as IHomogenousCapacitatedProblemInstance;
double demand = problemInstance.GetDemand(customer);
if (demand > cvrp.Capacity.Value)
feasible = false;
}
double x0 = problemInstance.Coordinates[vehicle, 0];
double y0 = problemInstance.Coordinates[vehicle, 1];
double dist;
if (problemInstance.Coordinates[customer + depotCount - 1, 0] < x0)
dist = -distance;
else
dist = distance;
double polarAngle = (dist != 0 ? (Math.Asin((problemInstance.Coordinates[customer + depotCount - 1, 1] - y0) / dist) / 360 * dist) : 0);
if (problemInstance is ITimeWindowedProblemInstance) {
ITimeWindowedProblemInstance vrptw = problemInstance as ITimeWindowedProblemInstance;
if (dueTime == double.MaxValue)
dueTime = 0;
else {
dueTime -= vrptw.ReadyTime[vehicle];
}
}
double cost = alpha * distance + // distance 0 <-> City[i]
-beta * dueTime + // latest arrival time
-gamma * polarAngle; // polar angle
if (vehicleUsed != null) {
if (!vehicleUsed[vehicle])
cost += problemInstance.FleetUsageFactor.Value;
}
return cost;
}
private static int GetNearestDepot(IVRPProblemInstance problemInstance, List depots, int customer,
Dictionary> vehicles, Dictionary vehicleTours, List vehicleUsed, PotvinEncoding solution,
double alpha, double beta, double gamma, out double minCost, out bool bound, out bool bestExistingRoute) {
int nearest = -1;
minCost = double.MaxValue;
bestExistingRoute = false;
bool bestFeasible = false;
int feasibleCount = 0;
foreach (int depot in depots) {
int vehicle = vehicles[depot][0];
bool existingRoute;
bool feasible;
double cost = GetCosts(
customer, vehicle,
problemInstance, vehicles, vehicleTours, vehicleUsed,
solution, alpha, beta, gamma, out feasible, out existingRoute);
if (feasible)
feasibleCount++;
if ((feasible && !bestFeasible) ||
(feasible == bestFeasible && existingRoute && !bestExistingRoute) ||
(feasible == bestFeasible && existingRoute == bestExistingRoute && cost < minCost)) {
minCost = cost;
nearest = depot;
bestFeasible = feasible;
bestExistingRoute = existingRoute;
}
}
bound = (feasibleCount == 1);
return nearest;
}
private static List SortCustomers(IVRPProblemInstance problemInstance, List customers, List depots,
Dictionary depotAssignment, Dictionary customerBinding,
Dictionary> vehicles, Dictionary vehicleTours, List vehicleUsed, PotvinEncoding solution,
double alpha, double beta, double gamma) {
List sortedCustomers = new List();
depotAssignment.Clear();
customerBinding.Clear();
List boundList = new List();
List existingRouteList = new List();
List costList = new List();
for (int i = 0; i < customers.Count; i++) {
double cost;
bool bound;
bool existingRoute;
int depot = GetNearestDepot(problemInstance, depots, customers[i], vehicles, vehicleTours, vehicleUsed, solution,
alpha, beta, gamma,
out cost, out bound, out existingRoute);
depotAssignment[customers[i]] = depot;
if (bound) {
customerBinding.Add(customers[i], depot);
}
int index = 0;
while (index < costList.Count &&
((!bound && boundList[index]) ||
(bound == boundList[index] && !existingRoute && existingRouteList[index]) ||
(bound == boundList[index] && existingRoute == existingRouteList[index] && costList[index] < cost)))
index++;
costList.Insert(index, cost);
boundList.Insert(index, bound);
existingRouteList.Insert(index, existingRoute);
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;
}
private static Dictionary CreateInitialTours(IVRPProblemInstance problemInstance, Dictionary> vehicles, PotvinEncoding result) {
Dictionary vehicleTours = new Dictionary();
IPickupAndDeliveryProblemInstance pdp = problemInstance as IPickupAndDeliveryProblemInstance;
int currentVehicle;
for (int i = 1; i <= problemInstance.Cities.Value; i++) {
if (pdp != null && pdp.GetPickupDeliveryLocation(i) <= 0) {
currentVehicle = -pdp.GetPickupDeliveryLocation(i);
foreach (int depot in vehicles.Keys) {
if (vehicles[depot].Contains(currentVehicle)) {
vehicles[depot].Remove(currentVehicle);
vehicles[depot].Insert(0, currentVehicle);
break;
}
}
Tour tour;
if (!vehicleTours.ContainsKey(currentVehicle)) {
tour = new Tour();
result.Tours.Add(tour);
vehicleTours[currentVehicle] = tour;
result.VehicleAssignment[result.Tours.Count - 1] = currentVehicle;
} else {
tour = vehicleTours[currentVehicle];
}
tour.Stops.Add(i);
}
}
foreach (int vehicle in vehicleTours.Keys) {
Tour vehicleTour = vehicleTours[vehicle];
List customers = new List(vehicleTour.Stops);
vehicleTour.Stops.Clear();
if (problemInstance is DynPDPProblemInstance &&
(problemInstance as DynPDPProblemInstance).CurrentPlan != null) {
PotvinEncoding plan = (problemInstance as DynPDPProblemInstance).CurrentPlan;
Tour planTour = plan.Tours.Find(t => plan.GetVehicleAssignment(plan.Tours.IndexOf(t)) == vehicle);
customers.Sort((x, y) => planTour.Stops.IndexOf(x).CompareTo(planTour.Stops.IndexOf(y)));
foreach (int customer in customers)
vehicleTour.Stops.Add(customer);
} else {
for (int i = 0; i < customers.Count; i++) {
int stop = result.FindBestInsertionPlace(vehicleTour, customers[i]);
vehicleTour.Stops.Insert(stop, customers[i]);
}
}
}
return vehicleTours;
}
public static PotvinEncoding CreateSolution(IVRPProblemInstance problemInstance, IRandom random, List vehicleUsed,
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;
if (mdp != null) {
for (int i = 0; i < result.VehicleAssignment.Length; i++) {
result.VehicleAssignment[i] = -1;
}
}
double alpha, beta, gamma;
lock (random) {
alpha = N(alphaValue, Math.Sqrt(alphaVariance), random);
beta = N(betaValue, Math.Sqrt(betaVariance), random);
gamma = N(gammaValue, Math.Sqrt(gammaVariance), random);
}
#region initialization
List unroutedCustomers = new List();
for (int i = 1; i <= problemInstance.Cities.Value; i++) {
if (pdp == null || (problemInstance.GetDemand(i) >= 0) || (pdp.GetPickupDeliveryLocation(i) == i))
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);
}
}
if (vehicleUsed != null) {
vehicles[depot].Sort(delegate(int x, int y) {
if (vehicleUsed[x] && vehicleUsed[y])
return 0;
else if (vehicleUsed[x])
return -1;
else
return 1;
});
}
}
Dictionary vehicleTours = CreateInitialTours(problemInstance, vehicles, result);
RemoveUnusedDepots(depots, vehicles);
#endregion
#region route unrouted
if (unroutedCustomers.Count > 0) {
Tour tour;
int currentDepot;
int currentVehicle;
Dictionary depotAssignment = new Dictionary();
//# sort customers globally - start new tour
Dictionary customerBinding = new Dictionary();
unroutedCustomers = SortCustomers(
problemInstance, unroutedCustomers, depots, depotAssignment, customerBinding,
vehicles, vehicleTours, vehicleUsed, result,
alpha, beta, gamma);
int currentCustomer = unroutedCustomers[0];
currentDepot = depotAssignment[currentCustomer];
currentVehicle = vehicles[currentDepot][0];
vehicles[currentDepot].RemoveAt(0);
RemoveUnusedDepots(depots, vehicles);
if (!vehicleTours.ContainsKey(currentVehicle)) {
tour = new Tour();
result.Tours.Add(tour);
result.VehicleAssignment[result.Tours.Count - 1] = currentVehicle;
unroutedCustomers.Remove(currentCustomer);
tour.Stops.Add(currentCustomer);
if (pdp != null && pdp.GetPickupDeliveryLocation(currentCustomer) != currentCustomer) {
tour.Stops.Add(pdp.GetPickupDeliveryLocation(currentCustomer));
}
} else {
tour = vehicleTours[currentVehicle];
}
while (unroutedCustomers.Count > 0) {
double minimumCost = double.MaxValue;
bool bestBoundToRoute = false;
int customer = -1;
int indexOfMinimumCost = -1;
int indexOfMinimumCost2 = -1;
foreach (int unrouted in unroutedCustomers) {
bool boundToRoute = customerBinding.ContainsKey(unrouted) && customerBinding[unrouted] == currentDepot;
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 && pdp.GetPickupDeliveryLocation(unrouted) != unrouted) {
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 || (!bestBoundToRoute && boundToRoute)) && (boundToRoute || !bestBoundToRoute) && feasible) {
customer = unrouted;
minimumCost = cost;
indexOfMinimumCost = i;
indexOfMinimumCost2 = j;
bestBoundToRoute = boundToRoute;
}
}
} else {
double cost = tourCost;
bool feasible = problemInstance.Feasible(eval);
if ((cost < minimumCost || (!bestBoundToRoute && boundToRoute)) && (boundToRoute || !bestBoundToRoute) && feasible) {
customer = unrouted;
minimumCost = cost;
indexOfMinimumCost = i;
bestBoundToRoute = boundToRoute;
}
}
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 && pdp.GetPickupDeliveryLocation(customer) != customer) {
tour.Stops.Insert(indexOfMinimumCost2, pdp.GetPickupDeliveryLocation(customer));
}
unroutedCustomers.Remove(customer);
} else { // no feasible customer found
//# sort customers globally - start new tour
unroutedCustomers = SortCustomers(
problemInstance, unroutedCustomers, depots, depotAssignment, customerBinding,
vehicles, vehicleTours, vehicleUsed, result,
alpha, beta, gamma);
currentCustomer = unroutedCustomers[0];
currentDepot = depotAssignment[currentCustomer];
currentVehicle = vehicles[currentDepot][0];
vehicles[currentDepot].RemoveAt(0);
RemoveUnusedDepots(depots, vehicles);
if (!vehicleTours.ContainsKey(currentVehicle)) {
tour = new Tour();
result.Tours.Add(tour);
result.VehicleAssignment[result.Tours.Count - 1] = currentVehicle;
unroutedCustomers.Remove(currentCustomer);
tour.Stops.Add(currentCustomer);
if (pdp != null && pdp.GetPickupDeliveryLocation(currentCustomer) != currentCustomer) {
tour.Stops.Add(pdp.GetPickupDeliveryLocation(currentCustomer));
}
} else {
tour = vehicleTours[currentVehicle];
}
}
}
}
#endregion
#region assign vehicles
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);
}
}
}
#endregion
return result;
}
public override IOperation Apply() {
List vehicleUsed = null;
DynPDPProblemInstance instance = ProblemInstance as DynPDPProblemInstance;
if(instance != null) {
vehicleUsed = new List();
for (int i = 0; i < instance.VehicleUsed.Length; i++) {
vehicleUsed.Add(instance.VehicleUsed[i]);
}
}
VRPToursParameter.ActualValue = CreateSolution(ProblemInstance, RandomParameter.ActualValue, vehicleUsed,
Alpha.Value.Value, Beta.Value.Value, Gamma.Value.Value,
AlphaVariance.Value.Value, BetaVariance.Value.Value, GammaVariance.Value.Value);
return base.Apply();
}
}
}