using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Encodings.RealVectorEncoding; using HeuristicLab.Parameters; using HeuristicLab.PDPSimulation.DomainModel; using HeuristicLab.PDPSimulation.Operators; using HeuristicLab.Persistence.Default.CompositeSerializers.Storable; namespace HeuristicLab.PDPSimulation { [Item("PriorityDispatching", "")] [StorableClass] public sealed class PriorityDispatching : DynamicPDPOptimization { private bool IsIdle(Vehicle v) { return v.CurrentOrder == Guid.Empty; } protected override bool OptimizationRequired(ChangeInformation changeInformation) { // either there are vehicles waiting and there are orders to dispatch // or there are new orders and waiting vehicles return (GetVehicles().Any(x => IsIdle(x)) && GetOrders().Any(x => x.AssignedVehicle == Guid.Empty || GetVehicles().Any(y => IsIdle(y) && x.AssignedVehicle == y.Id))); } public IValueLookupParameter WeightsParameter { get { return (IValueLookupParameter)Parameters["Weights"]; } } [StorableConstructor] private PriorityDispatching(bool deserializing) : base(deserializing) { } private PriorityDispatching(PriorityDispatching original, Cloner cloner) : base(original, cloner) { } public PriorityDispatching() { Parameters.Add(new ValueLookupParameter("Weights", "The weights for the individual priorities.")); WeightsParameter.Value = new RealVector(new double[] { 100.000, 100.000, 22.349, 74.573, 18.424, 28.913, 0.331, 91.323, 36.969, 44.992, 64.892, 30.736, 23.113, 36.458, 6.178, 99.065, 100.0, 100.0 }); } public override IDeepCloneable Clone(Cloner cloner) { return new PriorityDispatching(this, cloner); } protected override bool PerformOptimization(DynamicPDProblemInstance instance, ChangeInformation changeInformation) { var waitingVehicles = GetVehicles().Where(x => IsIdle(x)).ToList(); var waitingOrders = GetOrders().Where(x => x.AssignedVehicle == Guid.Empty).ToList(); while (waitingVehicles.Any() && (waitingOrders.Any() || GetOrders().Any(x => x.OrderState == OrderState.PickedUp && waitingVehicles.Any(y => y.Id == x.AssignedVehicle)))) { var tmp = new Dictionary>(); foreach (var vehicle in waitingVehicles) { var pickedupOrders = GetOrders().Where(x => x.OrderState == OrderState.PickedUp && x.AssignedVehicle == vehicle.Id); var orders = waitingOrders.Concat(pickedupOrders); if (orders.Any()) { Order selectedOrder; double priority; GetHighestPriorityOrder((DynPDPProblemInstance)instance.StaticInstance, vehicle, orders, out selectedOrder, out priority); tmp[vehicle] = Tuple.Create(selectedOrder, priority); } } var assignment = tmp.GroupBy(x => x.Value.Item1, x => new { Vehicle = x.Key, Priority = x.Value.Item2 }); foreach (var a in assignment) { var bestVehicle = a.MaxItems(x => x.Priority).Select(x => x.Vehicle).First(); var order = a.Key; bool first = true; XY target; MoveVehicleAction action; if (order.OrderState == OrderState.Waiting) { target = new XY(order.PickupXCoord, order.PickupYCoord); action = new MoveVehicleAction(bestVehicle.Id, order.PickupXCoord, order.PickupYCoord, VehicleAction.Pickup, order, scenario.AllowDiversion, scenario.DistanceMeasure); } else { target = new XY(order.DeliveryXCoord, order.DeliveryYCoord); action = new MoveVehicleAction(bestVehicle.Id, order.DeliveryXCoord, order.DeliveryYCoord, VehicleAction.Deliver, order, scenario.AllowDiversion, scenario.DistanceMeasure); } var pickedupOrders = GetOrders().Where(x => x.OrderState == OrderState.PickedUp && x.AssignedVehicle == bestVehicle.Id); var pickedupOrdersAtDestination = pickedupOrders.Where(o => o.DeliveryXCoord == target.X && o.DeliveryYCoord == target.Y); List deliveryActions = new List(); foreach (var pickedupOrder in pickedupOrdersAtDestination) { if (pickedupOrder != order) { MoveVehicleAction deliveryAction = new MoveVehicleAction(bestVehicle.Id, pickedupOrder.DeliveryXCoord, pickedupOrder.DeliveryYCoord, VehicleAction.Deliver, pickedupOrder, scenario.AllowDiversion, scenario.DistanceMeasure); if (first) { SetAction(deliveryAction); first = false; } else { AppendAction(deliveryAction); } } } if (first) { SetAction(action); first = false; } else { AppendAction(action); } waitingVehicles.Remove(bestVehicle); waitingOrders.Remove(order); foreach (var assignedOrder in GetOrders().Where(o => o.AssignedVehicle == bestVehicle.Id && o.OrderState == OrderState.Waiting)) assignedOrder.AssignedVehicle = Guid.Empty; order.AssignedVehicle = bestVehicle.Id; } } return true; } private void GetHighestPriorityOrder(DynPDPProblemInstance instance, Vehicle vehicle, IEnumerable orders, out Order order, out double priority) { var weights = WeightsParameter.Value; Order best = null; double bestPriority = double.MinValue; var pickupOrdersByLocation = (from o in orders where IsPickup(o, instance) group o by new { X = o.PickupXCoord, Y = o.PickupYCoord }) .ToDictionary(x => new XY(x.Key.X, x.Key.Y), y => y.ToArray(), new XYEqComparer()); var deliveryOrdersByLocation = (from o in orders where !IsPickup(o, instance) && o.Vehicle == vehicle.Id group o by new { X = o.DeliveryXCoord, Y = o.DeliveryYCoord }) .ToDictionary(x => new XY(x.Key.X, x.Key.Y), y => y.ToArray(), new XYEqComparer()); var destinations = deliveryOrdersByLocation.Keys.ToArray(); foreach (var o in orders) { if (IsPickup(o, instance) && o.Demand > vehicle.Capacity) continue; double prio; if (!IsPickup(o, instance) && vehicle.TargetPositionX == o.DeliveryXCoord && vehicle.TargetPositionY == o.DeliveryYCoord) { prio = double.MaxValue; } else { var target = new XY(o.PickupXCoord, o.PickupYCoord); if (!IsPickup(o, instance)) target = new XY(o.DeliveryXCoord, o.DeliveryYCoord); var destinationDifference = destinations.Select(x => scenario.DistanceMeasure.GetDistance(o.DeliveryXCoord, o.DeliveryYCoord, x.X, x.Y)).ToArray(); var courierDifference = GetVehicles().Where(x => x != vehicle).Select(x => new XY(x.TargetPositionX, x.TargetPositionY)) .Select(x => scenario.DistanceMeasure.GetDistance(target.X, target.Y, x.X, x.Y)).ToArray(); var commonDestinations = orders.Where(x => !IsPickup(x, instance) && x.Vehicle == vehicle.Id && x.DeliveryXCoord == o.PickupXCoord && x.DeliveryYCoord == o.PickupYCoord); var variables = new Dictionary(); variables.Add("Distance", scenario.DistanceMeasure.GetDistance(vehicle.TargetPositionX, vehicle.TargetPositionY, target.X, target.Y)); variables.Add("EarliestTimeOfArrival", vehicle.ReadyTime + variables["Distance"]); variables.Add("DueDate", o.DeliveryDueTime - GetSimulationTime()); variables.Add("StartDate", IsPickup(o, instance) ? o.PickupReadyTime - GetSimulationTime() : 0); variables.Add("PickupOrdersAtTarget", pickupOrdersByLocation.ContainsKey(target) ? pickupOrdersByLocation[target].Count() : 0); variables.Add("PickupOrderItemsAtTarget", pickupOrdersByLocation.ContainsKey(target) ? pickupOrdersByLocation[target].Sum(x => x.Demand) : 0); variables.Add("DeliveryOrdersAtTarget", deliveryOrdersByLocation.ContainsKey(target) ? deliveryOrdersByLocation[target].Count() : 0); variables.Add("DeliveryOrderItemsAtTarget", deliveryOrdersByLocation.ContainsKey(target) ? deliveryOrdersByLocation[target].Sum(x => x.Demand) : 0); variables.Add("AverageDistanceToDestinations", destinationDifference.Any() ? destinationDifference.Average() : 0); variables.Add("MinimumDistanceToDestinations", destinationDifference.Any() ? destinationDifference.Min() : 0); variables.Add("MaximumDistanceToDestinations", destinationDifference.Any() ? destinationDifference.Max() : 0); variables.Add("NumberOfOtherCouriersToTarget", GetVehicles().Count > 2 ? GetVehicles().Where(x => x != vehicle).Count(x => target.X == x.TargetPositionX && target.Y == x.TargetPositionY) : 0); variables.Add("AverageDistanceToOtherCouriers", courierDifference.Any() ? courierDifference.Average() : 0); variables.Add("MinimumDistanceToOtherCouriers", courierDifference.Any() ? courierDifference.Min() : 0); variables.Add("MaximumDistanceToOtherCouriers", courierDifference.Any() ? courierDifference.Max() : 0); variables.Add("DemandSize", o.Demand); variables.Add("Slack", o.DeliveryDueTime - GetSimulationTime() - (vehicle.ReadyTime + variables["Distance"] + (IsPickup(o, instance) ? (o.PickupServiceTime + scenario.DistanceMeasure.GetDistance(o.PickupXCoord, o.PickupYCoord, o.DeliveryXCoord, o.DeliveryYCoord)) : 0))); variables.Add("EDD", commonDestinations.Any() ? commonDestinations.Min(d => d.DeliveryDueTime - GetSimulationTime()) : 0); prio = weights[0] * variables["DueDate"] + weights[1] * variables["StartDate"] + weights[2] * variables["PickupOrdersAtTarget"] + weights[3] * variables["Distance"] + weights[4] * variables["AverageDistanceToDestinations"] + weights[5] * variables["MinimumDistanceToDestinations"] + weights[6] * variables["MaximumDistanceToDestinations"] + weights[7] * variables["NumberOfOtherCouriersToTarget"] + weights[8] * variables["AverageDistanceToOtherCouriers"] + weights[9] * variables["EarliestTimeOfArrival"] + weights[10] * variables["DeliveryOrdersAtTarget"] + weights[11] * variables["PickupOrderItemsAtTarget"] + weights[12] * variables["DeliveryOrderItemsAtTarget"] + weights[13] * variables["MinimumDistanceToOtherCouriers"] + weights[14] * variables["MaximumDistanceToOtherCouriers"] + weights[15] * variables["DemandSize"] + weights[16] * variables["Slack"] + weights[17] * variables["EDD"]; } if (prio > bestPriority) { bestPriority = prio; best = o; } } order = best != null ? best : orders.First(); priority = bestPriority; } private bool IsPickup(Order o, DynPDPProblemInstance instance) { return instance.Demand[Instance.GetStops(o.Id).First()] > 0; } private struct XY { public double X { get; private set; } public double Y { get; private set; } public XY(double x, double y) : this() { X = x; Y = y; } } private class XYEqComparer : EqualityComparer { public override bool Equals(XY x, XY y) { return x.X == y.X && x.Y == y.Y; } public override int GetHashCode(XY obj) { return obj.X.GetHashCode() ^ obj.Y.GetHashCode(); } } } }