/** * */ package vroom.trsp.datamodel.costDelegates; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import vroom.common.utilities.optimization.IMove; import vroom.trsp.datamodel.ITRSPTour; import vroom.trsp.datamodel.ITourIterator; import vroom.trsp.datamodel.TRSPDistanceMatrix; import vroom.trsp.datamodel.TRSPInstance; import vroom.trsp.datamodel.TRSPRequest; import vroom.trsp.datamodel.TRSPTour; import vroom.trsp.datamodel.TRSPTour.TRSPTourIterator; import vroom.trsp.datamodel.Technician; import vroom.trsp.optimization.InsertionMove; import vroom.trsp.optimization.RemoveMove; import vroom.trsp.optimization.localSearch.TRSPShift.TRSPShiftMove; import vroom.trsp.optimization.localSearch.TRSPTwoOpt.TRSPTwoOptMove; /** * <code>TRSPWorkingTime</code> is an implementation of {@link TRSPCostDelegate} that calculates and maintain the * cumulated working time at each node. * <p> * This class sums the {@linkplain TRSPRequest#getServiceTime() service time} and * {@linkplain TRSPDistanceMatrix#getTravelTime(int, int, Technician) travel time} at each node of a tour. * </p> * <p> * Creation date: Feb 28, 2011 - 9:07:52 AM * * @author Victor Pillac, <a href="http://uniandes.edu.co">Universidad de Los Andes</a>-<a * href="http://copa.uniandes.edu.co">Copa</a> <a href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a * href="http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a> * @version 1.0 */ public class TRSPWorkingTimeNoDelay extends TRSPCostDelegate { public TRSPWorkingTimeNoDelay() { super(); } /** * Evaluate a portion of the tour starting at the given <code>node</code> * * @param tour * the tour to be evaluated * @param node * the node at which reevaluation should start * @param updateTour * <code>true</code> if the tour stored costs have to be updated */ @Override protected double evaluateTRSPTour(TRSPTour tour, int node, boolean updateTour) { // Special case for singleton route if (tour.length() == 1) { node = tour.getFirstNode(); double wt = tour.getTimeWindow(node).getEarliestStartOfService( tour.getEarliestStartTime()) + tour.getServiceTime(node); if (updateTour) { tour.setCumulativeCost(node, wt); tour.setTotalCost(wt); } return wt; } // Cumulated working time double wt = node != ITRSPTour.UNDEFINED ? tour.getCumulativeCost(node) : tour .getEarliestStartTime(); // Iterator over the tour TRSPTourIterator it = node != ITRSPTour.UNDEFINED ? tour.iterator(node) : tour.iterator(); if (!it.hasNext()) { if (updateTour) tour.setTotalCost(wt); return wt; } // First node int pred = it.next(); while (it.hasNext()) { node = it.next(); // If time windows are enforced the vehicle has to wait until TW start wt = tour.getTimeWindow(pred).getEarliestStartOfService(wt); // Add the service time of predecessor wt += tour.getServiceTime(pred); // Add the travel time wt += tour.getTravelTime(pred, node); if (updateTour) // Set the cumulative cost tour.setCumulativeCost(node, wt); pred = node; } // If time windows are enforced the vehicle has to wait at the last depot until TW start wt = tour.getTimeWindow(pred).getEarliestStartOfService(wt); // Add the service time of the last node wt += tour.getServiceTime(pred); if (updateTour) // Set the total cost tour.setTotalCost(wt); return wt; } @Override protected double evaluateGenericTour(ITRSPTour tour) { if (tour.length() == 0) return 0; TRSPInstance ins = tour.getInstance(); Technician tech = ins.getTechnician(tour.getTechnicianId()); // Cumulated working time double wt = 0; // Iterator over the tour ITourIterator it = tour.iterator(); // First node int pred = it.next(); while (it.hasNext()) { int node = it.next(); // If time windows are enforced the vehicle has to wait until TW start wt = ins.getTimeWindow(pred).getEarliestStartOfService(wt); // Add the service time of predecessor wt += ins.getServiceTime(pred); // Add the travel time wt += ins.getCostDelegate().getTravelTime(pred, node, tech); pred = node; } // If time windows are enforced the vehicle has to wait at the last depot until TW start wt = ins.getTimeWindow(pred).getEarliestStartOfService(wt); // Add the service time of the last node wt += ins.getServiceTime(pred); return wt; } @Override public double evaluateDetour(ITRSPTour itour, int predecessor, int node, int successor, boolean isRemoval) { final boolean isTRSPTour = TRSPTour.class.isAssignableFrom(itour.getClass()); // Evaluate the arrival time at the inserted node // ---------------------------------- double arrivalTimeAtPred = Double.NaN; final double detourAarrivalTimeAtNode; ITourIterator tourIt = null; if (predecessor == ITRSPTour.UNDEFINED) { detourAarrivalTimeAtNode = itour.getInstance().getTechnician(itour.getTechnicianId()) .getHome().getTimeWindow().startAsDouble(); tourIt = itour.iterator(); } else { if (isTRSPTour) { // Faster implementation using stored arrival times // -- TRSPTour tour = (TRSPTour) itour; if (!tour.isAutoUpdated()) throw new IllegalStateException( "The tour must have its autoupdate flag set to true for proper working"); tourIt = tour.iterator(successor); // Evaluate arrival time at the predecessor arrivalTimeAtPred = tour.getEarliestArrivalTime(predecessor); } else { arrivalTimeAtPred = itour.getInstance().getTechnician(itour.getTechnicianId()) .getHome().getTimeWindow().startAsDouble(); // Evaluate the arrival time up to the predecessor tourIt = itour.iterator(); int pred = tourIt.next(); int n = ITRSPTour.UNDEFINED; while (tourIt.hasNext() && n != predecessor) { n = tourIt.next(); arrivalTimeAtPred = itour.getInstance().calculateArrivalTime(n, pred, arrivalTimeAtPred, itour.getTechnicianId()); pred = n; } if (n != predecessor && !tourIt.hasNext()) throw new IllegalStateException( "Reached the end of the tour without finding the predecessor"); } // Evaluate the arrival time at node detourAarrivalTimeAtNode = itour.getInstance().calculateArrivalTime(node, predecessor, arrivalTimeAtPred, itour.getTechnicianId()); } // Evaluate arrival time at the successor // ---------------------------------- final double detourArrivalTimeSucc = itour.getInstance().calculateArrivalTime(successor, node, detourAarrivalTimeAtNode, itour.getTechnicianId()); if (!isRemoval && isTRSPTour && predecessor != ITRSPTour.UNDEFINED) { // Faster implementation using stored waiting times // -- // The detour time will be compensated by the waiting time (if feasible) // FIXME check if the calculation of the insertion cost is correct return Math.max(0, detourArrivalTimeSucc - ((TRSPTour) itour).getEarliestArrivalTime(successor) - ((TRSPTour) itour).getWaitingTime(predecessor, itour.getLastNode())); } else { // Evaluate the arrival time at the last node int pred = tourIt.next();// Will return "successor" as we stopped at "predecessor" int n = ITRSPTour.UNDEFINED; double detourArrivalTimeAtLast = detourArrivalTimeSucc; double directArrivalTimeAtLast = itour.getInstance().calculateArrivalTime(successor, predecessor, arrivalTimeAtPred, itour.getTechnicianId()); while (tourIt.hasNext()) { n = tourIt.next(); detourArrivalTimeAtLast = itour.getInstance().calculateArrivalTime(n, pred, detourArrivalTimeAtLast, itour.getTechnicianId()); directArrivalTimeAtLast = itour.getInstance().calculateArrivalTime(n, pred, directArrivalTimeAtLast, itour.getTechnicianId()); pred = n; } return detourArrivalTimeAtLast - directArrivalTimeAtLast; } } /** * Evaluates the {@linkplain IMove#getImprovement() improvement} of a {@link InsertionMove} * * @param move * the move to be evaluated * @return the {@linkplain IMove#getImprovement() improvement} resulting from the execution of the specified * <code>move</code> */ @Override protected double evaluateInsMove(InsertionMove move) { if (TRSPTour.class.isAssignableFrom(move.getTour().getClass())) { TRSPTour tour = (TRSPTour) move.getTour(); double improvement = 0; if (!move.isDepotTrip()) { // Simple insertion improvement = -evaluateDetour(tour, move.getInsertionPred(), move.getNodeId(), move.getInsertionSucc(), false); } else { LinkedList<Integer> changedSequence = new LinkedList<Integer>(); changedSequence.add(0); int pred = ITRSPTour.UNDEFINED; if (move.getDepotSucc() == move.getNodeId()) { // The trip to the depot immediately precedes the new request changedSequence.add(move.getNodeId()); pred = move.getInsertionPred(); } else { // The trip to the depot is inserted before the new request TRSPTourIterator it = tour.iterator(move.getDepotSucc()); int next = it.next(); while (next != move.getInsertionSucc() && it.hasNext()) { changedSequence.add(next); next = it.next(); } // The trip to the depot immediately precedes the new request changedSequence.add(move.getNodeId()); pred = tour.getPred(move.getDepotSucc()); } // Add the rest of the route TRSPTourIterator it = tour.iterator(move.getInsertionSucc()); while (it.hasNext()) { changedSequence.add(it.next()); } // Earliest arrival time at the depot double arrivalTime = tour.getEarliestDepartureTime(pred) + tour.getTravelTime(pred, 0); Iterator<Integer> seq = changedSequence.iterator(); pred = seq.next(); while (seq.hasNext()) { int node = seq.next(); arrivalTime = tour.getTimeWindow(pred).getEarliestStartOfService(arrivalTime); arrivalTime += tour.getServiceTime(pred) + tour.getTravelTime(pred, node); pred = node; } // Evaluate end of service at the last node arrivalTime = tour.getTimeWindow(tour.getLastNode()).getEarliestStartOfService( arrivalTime) + tour.getServiceTime(tour.getLastNode()); improvement = tour.getTotalCost() - arrivalTime; } move.setImprovement(improvement); return improvement; } else { throw new UnsupportedOperationException("Unsupported tour type: " + move.getTour().getClass()); } } /** * Evaluates the {@linkplain IMove#getImprovement() improvement} of a {@link TRSPShiftMove} * * @param move * the move to be evaluated * @return the {@linkplain IMove#getImprovement() improvement} resulting from the execution of the specified * <code>move</code> */ @Override protected double evaluateShiftMove(TRSPShiftMove move) { TRSPTour tour = (TRSPTour) move.getTour(); if (!tour.isAutoUpdated()) throw new IllegalStateException( "The tour must have its autoupdate flag set to true for proper working"); // Build the changed sequence List<Integer> changedSequence = move.getChangedSequence(); // The changed sequence predecessor (unchanged) ListIterator<Integer> it = changedSequence.listIterator(); // The last node that is unchanged int lastUnchanged = it.next(); // Departure time at the last unchanged node double newEarliestDeparture = lastUnchanged != ITRSPTour.UNDEFINED ? tour .getEarliestDepartureTime(lastUnchanged) : tour.getEarliestStartTime(); int pred = lastUnchanged; while (it.hasNext()) { int n = it.next(); if (pred != ITRSPTour.UNDEFINED) // Add the travel time from predecessor newEarliestDeparture += tour.getTravelTime(pred, n); // Evaluate the new earliest departure time at node n newEarliestDeparture = tour.getTimeWindow(n).getEarliestStartOfService( newEarliestDeparture) + tour.getServiceTime(n); pred = n; } double improvement = tour.getTotalCost() - newEarliestDeparture; move.setImprovement(improvement); return improvement; } /** * Evaluates the {@linkplain IMove#getImprovement() improvement} of a {@link TRSPTwoOptMove} * * @param move * the move to be evaluated * @return the {@linkplain IMove#getImprovement() improvement} resulting from the execution of the specified * <code>move</code> */ @Override protected double evaluateTwoOptMove(TRSPTwoOptMove move) { TRSPTour tour = (TRSPTour) move.getTour(); if (!tour.isAutoUpdated()) throw new IllegalStateException( "The tour must have its autoupdate flag set to true for proper working"); int n = tour.getSucc(move.getSecond()); // The arrival time at the considered node double time = tour.getEarliestArrivalTime(move.getFirst()); TRSPTourIterator it = tour.iterator(move.getFirst()); int pred = it.next(); while (pred != n && it.hasNext()) { int node = it.next(); // Update the arrival time time = tour.getTimeWindow(pred).getEarliestStartOfService(time) + tour.getServiceTime(pred) + tour.getTravelTime(pred, node); pred = node; } it = tour.iterator(n); // Skip the first node (n) it.next(); while (it.hasNext()) { int node = it.next(); // Update the arrival time time = tour.getTimeWindow(pred).getEarliestStartOfService(time) + tour.getServiceTime(pred) + tour.getTravelTime(pred, node); pred = node; } return tour.getTotalCost() - time; } @Override protected double evaluateRemMove(RemoveMove move) { TRSPTour tour = (TRSPTour) move.getTour(); if (!tour.isAutoUpdated()) throw new IllegalStateException( "The tour must have its autoupdate flag set to true for proper working"); int pred = tour.getPred(move.getNodeId()); // The new end of service time double time = tour.getEarliestDepartureTime(pred); TRSPTourIterator it = tour.iterator(tour.getSucc(move.getNodeId())); while (it.hasNext()) { int node = it.next(); // Add the travel time time += tour.getTravelTime(pred, node); // Consider possible waiting time and service time time = tour.getTimeWindow(node).getEarliestStartOfService(time) + tour.getServiceTime(node); pred = node; } return tour.getTotalCost() - time; } @Override public boolean isInsertionSeqDependent() { return true; } }