#region License Information
/* HeuristicLab
* Copyright (C) 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 HEAL.Attic;
using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Data;
using HeuristicLab.Parameters;
using HeuristicLab.Problems.VehicleRouting.Interfaces;
namespace HeuristicLab.Problems.VehicleRouting.ProblemInstances {
[Item("CVRPPDTWProblemInstance", "Represents a single depot CVRPPDTW instance.")]
[StorableType("6DC3F907-9CDC-4CDA-8C84-AC9ED248DB3B")]
public class CVRPPDTWProblemInstance : CVRPTWProblemInstance, IPickupAndDeliveryProblemInstance {
protected IValueParameter PickupDeliveryLocationParameter {
get { return (IValueParameter)Parameters["PickupDeliveryLocation"]; }
}
protected IValueParameter PickupViolationPenaltyParameter {
get { return (IValueParameter)Parameters["EvalPickupViolationPenalty"]; }
}
public IntArray PickupDeliveryLocation {
get { return PickupDeliveryLocationParameter.Value; }
set { PickupDeliveryLocationParameter.Value = value; }
}
protected IValueParameter CurrentPickupViolationPenaltyParameter {
get { return (IValueParameter)Parameters["CurrentPickupViolationPenalty"]; }
}
public DoubleValue PickupViolationPenalty {
get {
DoubleValue currentPickupViolationPenalty = CurrentPickupViolationPenaltyParameter.Value;
if (currentPickupViolationPenalty != null)
return currentPickupViolationPenalty;
else
return PickupViolationPenaltyParameter.Value;
}
}
DoubleValue IPickupAndDeliveryProblemInstance.CurrentPickupViolationPenalty {
get { return CurrentOverloadPenaltyParameter.Value; }
set { CurrentPickupViolationPenaltyParameter.Value = value; }
}
public int GetPickupDeliveryLocation(int city) {
return PickupDeliveryLocation[city];
}
public override IEnumerable FilterOperators(IEnumerable operators) {
return base.FilterOperators(operators)
.Where(x => !(x is INotPickupAndDeliveryOperator))
.Union(operators.Where(x => x is IPickupAndDeliveryOperator));
}
protected override VRPEvaluation CreateTourEvaluation() {
return new CVRPPDTWEvaluation();
}
protected override void EvaluateTour(VRPEvaluation eval, Tour tour, IVRPEncodedSolution solution) {
TourInsertionInfo tourInfo = new TourInsertionInfo(solution.GetVehicleAssignment(solution.GetTourIndex(tour)));
eval.InsertionInfo.AddTourInsertionInfo(tourInfo);
double originalQuality = eval.Quality;
double capacity = Capacity.Value;
double time = 0.0;
double waitingTime = 0.0;
double serviceTime = 0.0;
double tardiness = 0.0;
double overweight = 0.0;
double distance = 0.0;
double currentLoad = 0.0;
Dictionary stops = new Dictionary();
int pickupViolations = 0;
double tourStartTime = ReadyTime[0];
time = tourStartTime;
//simulate a tour, start and end at depot
for (int i = 0; i <= tour.Stops.Count; i++) {
int start = 0;
if (i > 0)
start = tour.Stops[i - 1];
int end = 0;
if (i < tour.Stops.Count)
end = tour.Stops[i];
//drive there
double currentDistace = GetDistance(start, end, solution);
time += currentDistace;
distance += currentDistace;
double arrivalTime = time;
//check if it was serviced on time
if (time > DueTime[end])
tardiness += time - DueTime[end];
//wait
double currentWaitingTime = 0.0;
if (time < ReadyTime[end])
currentWaitingTime = ReadyTime[end] - time;
double waitTime = ReadyTime[end] - time;
waitingTime += currentWaitingTime;
time += currentWaitingTime;
double spareTime = DueTime[end] - time;
//service
double currentServiceTime = ServiceTime[end];
serviceTime += currentServiceTime;
time += currentServiceTime;
//Pickup / deliver
double arrivalSpareCapacity = capacity - currentLoad;
bool validPickupDelivery =
validPickupDelivery =
((Demand[end] >= 0) ||
(stops.ContainsKey(PickupDeliveryLocation[end])));
if (validPickupDelivery) {
currentLoad += Demand[end];
} else {
pickupViolations++;
}
if (currentLoad > capacity)
overweight += currentLoad - capacity;
double spareCapacity = capacity - currentLoad;
CVRPPDTWInsertionInfo stopInfo = new CVRPPDTWInsertionInfo(start, end, spareCapacity, tourStartTime,
arrivalTime, time, spareTime, waitTime, new List(stops.Keys), arrivalSpareCapacity);
tourInfo.AddStopInsertionInfo(stopInfo);
stops.Add(end, true);
}
eval.Quality += FleetUsageFactor.Value;
eval.Quality += DistanceFactor.Value * distance;
eval.Distance += distance;
eval.VehicleUtilization += 1;
(eval as CVRPEvaluation).Overload += overweight;
double tourPenalty = 0;
double penalty = overweight * OverloadPenalty.Value;
eval.Penalty += penalty;
eval.Quality += penalty;
tourPenalty += penalty;
(eval as CVRPTWEvaluation).Tardiness += tardiness;
(eval as CVRPTWEvaluation).TravelTime += time;
penalty = tardiness * TardinessPenalty.Value;
eval.Penalty += penalty;
eval.Quality += penalty;
tourPenalty += penalty;
(eval as CVRPPDTWEvaluation).PickupViolations += pickupViolations;
penalty = pickupViolations * PickupViolationPenalty.Value;
eval.Penalty += penalty;
tourPenalty += penalty;
eval.Quality += penalty;
eval.Quality += time * TimeFactor.Value;
tourInfo.Penalty = tourPenalty;
tourInfo.Quality = eval.Quality - originalQuality;
eval.IsFeasible = overweight == 0 && tardiness == 0 && pickupViolations == 0;
}
protected override double GetTourInsertionCosts(IVRPEncodedSolution solution, TourInsertionInfo tourInsertionInfo, int index, int customer,
out bool feasible) {
CVRPPDTWInsertionInfo insertionInfo = tourInsertionInfo.GetStopInsertionInfo(index) as CVRPPDTWInsertionInfo;
double costs = 0;
feasible = tourInsertionInfo.Penalty < double.Epsilon;
bool tourFeasible = true;
double overloadPenalty = OverloadPenalty.Value;
double tardinessPenalty = TardinessPenalty.Value;
double pickupPenalty = PickupViolationPenalty.Value;
double distance = GetDistance(insertionInfo.Start, insertionInfo.End, solution);
double newDistance =
GetDistance(insertionInfo.Start, customer, solution) +
GetDistance(customer, insertionInfo.End, solution);
costs += DistanceFactor.Value * (newDistance - distance);
double demand = Demand[customer];
if (demand > insertionInfo.ArrivalSpareCapacity) {
tourFeasible = feasible = false;
if (insertionInfo.ArrivalSpareCapacity >= 0)
costs += (demand - insertionInfo.ArrivalSpareCapacity) * overloadPenalty;
else
costs += demand * overloadPenalty;
}
int destination = PickupDeliveryLocation[customer];
bool validPickup = true;
if (demand < 0 && !insertionInfo.Visited.Contains(destination)) {
tourFeasible = feasible = false;
validPickup = false;
costs += pickupPenalty;
}
double time = 0;
double tardiness = 0;
if (index > 0)
time = (tourInsertionInfo.GetStopInsertionInfo(index - 1) as CVRPTWInsertionInfo).LeaveTime;
else
time = insertionInfo.TourStartTime;
time += GetDistance(insertionInfo.Start, customer, solution);
if (time > DueTime[customer]) {
tardiness += time - DueTime[customer];
}
if (time < ReadyTime[customer])
time += ReadyTime[customer] - time;
time += ServiceTime[customer];
time += GetDistance(customer, insertionInfo.End, solution);
double additionalTime = time - (tourInsertionInfo.GetStopInsertionInfo(index) as CVRPTWInsertionInfo).ArrivalTime;
for (int i = index; i < tourInsertionInfo.GetStopCount(); i++) {
CVRPTWInsertionInfo nextStop = tourInsertionInfo.GetStopInsertionInfo(i) as CVRPTWInsertionInfo;
if (demand >= 0) {
if (nextStop.End == destination) {
demand = 0;
costs -= pickupPenalty;
if (tourInsertionInfo.Penalty == pickupPenalty && tourFeasible)
feasible = true;
} else if (nextStop.SpareCapacity < 0) {
costs += demand * overloadPenalty;
} else if (nextStop.SpareCapacity < demand) {
tourFeasible = feasible = false;
costs += (demand - nextStop.SpareCapacity) * overloadPenalty;
}
} else if (validPickup) {
if (nextStop.SpareCapacity < 0) {
costs += Math.Max(demand, nextStop.SpareCapacity) * overloadPenalty;
}
}
if (additionalTime < 0) {
//arrive earlier than before
//wait probably
if (nextStop.WaitingTime < 0) {
double wait = nextStop.WaitingTime - additionalTime;
if (wait > 0)
additionalTime += wait;
} else {
additionalTime = 0;
}
//check due date, decrease tardiness
if (nextStop.SpareTime < 0) {
costs += Math.Max(nextStop.SpareTime, additionalTime) * tardinessPenalty;
}
} else {
//arrive later than before, probably don't have to wait
if (nextStop.WaitingTime > 0) {
additionalTime -= Math.Min(additionalTime, nextStop.WaitingTime);
}
//check due date
if (nextStop.SpareTime > 0) {
double spare = nextStop.SpareTime - additionalTime;
if (spare < 0)
tardiness += -spare;
} else {
tardiness += additionalTime;
}
}
}
costs += additionalTime * TimeFactor.Value;
if (tardiness > 0) {
tourFeasible = feasible = false;
}
costs += tardiness * tardinessPenalty;
return costs;
}
[StorableConstructor]
protected CVRPPDTWProblemInstance(StorableConstructorFlag _) : base(_) { }
public CVRPPDTWProblemInstance() {
Parameters.Add(new ValueParameter("PickupDeliveryLocation", "The pickup and delivery location for each customer.", new IntArray()));
Parameters.Add(new ValueParameter("EvalPickupViolationPenalty", "The pickup violation penalty considered in the evaluation.", new DoubleValue(100)));
Parameters.Add(new OptionalValueParameter("CurrentPickupViolationPenalty", "The current pickup violation penalty considered in the evaluation.") { Hidden = true });
AttachEventHandlers();
}
public override IDeepCloneable Clone(Cloner cloner) {
return new CVRPPDTWProblemInstance(this, cloner);
}
protected CVRPPDTWProblemInstance(CVRPPDTWProblemInstance original, Cloner cloner)
: base(original, cloner) {
AttachEventHandlers();
}
[StorableHook(HookType.AfterDeserialization)]
private void AfterDeserialization() {
AttachEventHandlers();
}
private void AttachEventHandlers() {
PickupDeliveryLocationParameter.ValueChanged += PickupDeliveryLocationParameter_ValueChanged;
PickupDeliveryLocation.Reset += PickupDeliveryLocation_Changed;
PickupDeliveryLocation.ItemChanged += PickupDeliveryLocation_Changed;
}
public override void InitializeState() {
base.InitializeState();
CurrentPickupViolationPenaltyParameter.Value = null;
}
#region Event handlers
void PickupDeliveryLocationParameter_ValueChanged(object sender, EventArgs e) {
PickupDeliveryLocation.Reset += PickupDeliveryLocation_Changed;
PickupDeliveryLocation.ItemChanged += PickupDeliveryLocation_Changed;
EvalBestKnownSolution();
}
private void PickupDeliveryLocation_Changed(object sender, EventArgs e) {
EvalBestKnownSolution();
}
#endregion
}
}