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