/**
*
*/
package vroom.trsp.datamodel.costDelegates;
import static java.lang.Math.max;
import static java.lang.Math.min;
import vroom.common.utilities.Utilities;
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.TRSPSolution;
import vroom.trsp.datamodel.TRSPSolutionCheckerBase;
import vroom.trsp.datamodel.TRSPTour;
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;
import vroom.trsp.util.TRSPLogging;
/**
* <code>TRSPWorkingTime</code> is an implementation of {@link TRSPCostDelegate} that calculates and maintain the
* cumulative 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 TRSPWorkingTime extends TRSPCostDelegate {
public TRSPWorkingTime() {
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
* @return the total cost of the tour
*/
@Override
protected double evaluateTRSPTour(TRSPTour tour, int node, boolean updateTour) {
if (!tour.isAutoUpdated())
if (updateTour)
tour.updateTimeInformation();
else
throw new IllegalStateException(
"Cannot evaluate a tour that is not autoupdated with the updateFlag set to false");
// We assume that the tour contains up to date information
double duration = tour.getMinimalDuration();
if (updateTour)
tour.setTotalCost(duration);
return duration;
}
@Override
public double evaluateGenericTour(ITRSPTour tour) {
if (tour.length() == 0)
return 0;
TRSPInstance ins = tour.getInstance();
Technician tech = ins.getTechnician(tour.getTechnicianId());
// Cumulative working time
double arrivalTime = 0;
// Cumulative waiting time
double wait = 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
if (pred != tour.getFirstNode())
wait += ins.getTimeWindow(pred).getWaiting(arrivalTime);
arrivalTime = ins.getTimeWindow(pred).getEarliestStartOfService(arrivalTime);
// Add the service time of predecessor
arrivalTime += ins.getServiceTime(pred);
// Add the travel time
arrivalTime += 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
arrivalTime = ins.getTimeWindow(pred).getEarliestStartOfService(arrivalTime);
// Add the service time of the last node
arrivalTime += ins.getServiceTime(pred);
// Evaluate the slack
double tws0 = tech.getHome().getTimeWindow().startAsDouble();
double F0 = TRSPSolutionCheckerBase.evaluateFwdSlackTime(tour, tws0, tour.getFirstNode(),
tour.getLastNode());
// Evaluate the TW duration
double wt = arrivalTime - (tws0 + min(F0, wait));
return wt;
}
@Override
public double evaluateDetour(ITRSPTour itour, int i, int r, int j, boolean isRemoval) {
return evaluateDetour(itour, i, r, j, ITRSPTour.UNDEFINED, ITRSPTour.UNDEFINED,
ITRSPTour.UNDEFINED, isRemoval);
}
/**
* Evaluate the detour represented by the insertion of {@code r} between {@code m} and {@code n} and {@code q}
* between {@code i} and {@code j}
*
* @param itour
* @param m
* @param r
* @param n
* @param i
* @param q
* @param j
* @param isRemoval
* true if the detour corresponds to a removal
* @return the corresponding detour (change in cost)
*/
public double evaluateDetour(ITRSPTour itour, int m, int r, int n, int i, int q, int j,
boolean isRemoval) {
if (!TRSPTour.class.isAssignableFrom(itour.getClass()))
throw new UnsupportedOperationException("Unsupported tour type: " + itour.getClass());
TRSPTour tour = (TRSPTour) itour;
if (!tour.isAutoUpdated())
throw new IllegalStateException(
"The tour needs to have its autoUpdated flag set to true");
if (m == ITRSPTour.UNDEFINED)
throw new IllegalStateException("Insertion as first node is not supported");
final boolean simpleInsertion = q == ITRSPTour.UNDEFINED;
final int pi0 = tour.getFirstNode();
final int pil = tour.getLastNode();
// Evaluate arrival time at the predecessor
double Am = tour.getEarliestArrivalTime(m);
if (isRemoval) {
// throw new UnsupportedOperationException("Removal evaluation is not implemented yet");
TRSPLogging.getComponentsLogger().warn(
"TRSPWorkingTime.evaluateDetour: removal evaluation is very costly");
TRSPSolution clone = tour.getSolution().clone();
TRSPTour tourClone = clone.getTour(tour.getTechnicianId());
tourClone.removeNode(q);
tourClone.removeNode(r);
return evaluateTour(tour, false) - evaluateTour(tourClone, false);
} else {
// Special case : n==q - nodes are inserted next to each other
final boolean adj = n == q;
final int r1 = r;
final int r2 = adj ? q : r;
if (adj) {
// We "merge" r and q in a single node R
n = j;
}
final double An = tour.getEarliestArrivalTime(n);
// Change in the arrival time at the successor
// ----------------------------------------
// Evaluate the arrival time at the inserted node
double Ar1 = tour.getInstance().calculateArrivalTime(r1, m, Am, tour.getTechnicianId());
double Ar2 = adj ? tour.getInstance().calculateArrivalTime(r2, r1, Ar1,
tour.getTechnicianId()) : Ar1;
// Evaluate arrival time at n
final double AnPrime = tour.getInstance().calculateArrivalTime(n, r2, Ar2,
tour.getTechnicianId());
final double Deltan = AnPrime - An;
// ----------------------------------------
double Wr1 = tour.getTimeWindow(r1).getWaiting(Ar1);
double Wr2 = adj ? tour.getTimeWindow(r2).getWaiting(Ar2) : 0;
double Wr = Wr1 + Wr2;
// New forward slack time
// ----------------------------------------
final double F0m = tour.getFwdSlackTime(pi0, m);
double secondTerm;
if (adj)
// In the adj case the second term is the minimum between the expression
// applied to r1 and to r2
secondTerm = min(
tour.getTimeWindow(r1).endAsDouble() - Ar1 + tour.getWaitingTime(pi0, n),//
tour.getTimeWindow(r2).endAsDouble() - Ar2 + tour.getWaitingTime(pi0, n)
+ Wr1);
else
secondTerm = tour.getTimeWindow(r2).endAsDouble() - Ar2
+ tour.getWaitingTime(pi0, n);
// ----------------------------------------
if (simpleInsertion || adj) {
// New total waiting time
// ----------------------------------------
final double W0nPrime = tour.getWaitingTime(pi0, n) + Wr;
final double W0fPrime = tour.getWaitingTime(pi0, n) + Wr
+ max(0, tour.getWaitingTime(m, pil) - Deltan);
// ----------------------------------------
// New forward slack time
// ----------------------------------------
final double thirdTerm = tour.getFwdSlackTime(n, pil)//
+ W0nPrime + tour.getTimeWindow(n).getEarliestStartOfService(An)//
- AnPrime;
final double F0fPrime = Utilities.Math.minDouble(F0m, secondTerm, thirdTerm);
// ----------------------------------------
// Final cost of the insertion
// ----------------------------------------
final double deltaWT = max(0, Deltan - tour.getWaitingTime(m, pil))//
- min(F0fPrime, W0fPrime) //
+ min(tour.getFwdSlackTime(pi0), tour.getWaitingTime(pi0, pil));
// ----------------------------------------
return deltaWT;
} else {
// New arrival time at the last node
// ----------------------------------------
// New departure from i
final double DiPrime = tour.getEarliestDepartureTime(i)
+ max(0, Deltan - tour.getWaitingTime(m, j));
// Arrival at q
final double Aq = DiPrime + tour.getTravelTime(i, q);
// New arrival at j
final double AjPrime = tour.getInstance().calculateArrivalTime(j, q, Aq,
tour.getTechnicianId());
// Change in arrival time at j
final double Aj = tour.getEarliestArrivalTime(j);
final double Deltaj = AjPrime - Aj;
// ----------------------------------------
final double Wq = tour.getTimeWindow(q).getWaiting(Aq);
// New waiting time between pi0 and j (inclusive)
// ---------------------------------------
final double W0q = tour.getWaitingTime(pi0, n) + Wr
+ max(0, tour.getWaitingTime(m, j) - Deltan);
final double W0jPrime = tour.getWaitingTime(pi0, n) + Wr + Wq // Waiting time at q already takes into
// account possible absorption of the
// Deltan
+ max(0, tour.getWaitingTime(m, j) - Deltan);
// New total waiting time
// ----------------------------------------
final double W0fPrime = tour.getWaitingTime(pi0, n) //
+ Wr + max(0, tour.getWaitingTime(m, j) - Deltan)//
+ Wq + max(0, tour.getWaitingTime(i, pil) - Deltaj);
// ----------------------------------------
// New forward slack time
// ----------------------------------------
final double thirdTerm = tour.getFwdSlackTime(n, i) + tour.getWaitingTime(pi0, n)
+ Wr + max(An, tour.getTimeWindow(n).startAsDouble()) - AnPrime;
final double fourthTerm = tour.getTimeWindow(q).endAsDouble() - Aq + W0q;
final double fifthTerm = tour.getFwdSlackTime(j, pil) + W0jPrime
+ max(Aj, tour.getTimeWindow(j).startAsDouble()) - AjPrime;
final double F0fPrime = Utilities.Math.minDouble(F0m, secondTerm, thirdTerm,
fourthTerm, fifthTerm);
// ----------------------------------------
// Final cost of the insertion
// ----------------------------------------
final double deltaWT = max(0, Deltaj - tour.getWaitingTime(i, pil))//
- min(F0fPrime, W0fPrime) //
+ min(tour.getFwdSlackTime(pi0), tour.getWaitingTime(pi0, pil));
// ----------------------------------------
return deltaWT;
}
}
}
/**
* 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
public double evaluateInsMove(InsertionMove move) {
if (!TRSPTour.class.isAssignableFrom(move.getTour().getClass()))
throw new UnsupportedOperationException("Unsupported tour type: "
+ move.getTour().getClass());
TRSPTour tour = (TRSPTour) move.getTour();
if (!tour.isAutoUpdated())
throw new IllegalStateException(
"The tour needs to have its autoUpdated flag set to true");
double improvement = 0;
if (!move.isDepotTrip()) {
// Simple insertion
improvement = -evaluateDetour(tour, move.getInsertionPred(), move.getNodeId(),
move.getInsertionSucc(), false);
} else {
// Double insertion
improvement = -evaluateDetour(tour, move.getDepotPred(), move.getTour().getInstance()
.getMainDepotDuplicate(tour.getTechnicianId()), move.getDepotSucc(),
move.getInsertionPred(), move.getNodeId(), move.getInsertionSucc(), false);
}
move.setImprovement(improvement);
return improvement;
}
/**
* 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) {
throw new UnsupportedOperationException("Not updated yet");
}
/**
* 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) {
throw new UnsupportedOperationException("Not implemented yet");
}
@Override
protected double evaluateRemMove(RemoveMove move) {
// TODO implement evaluateRemMove
throw new UnsupportedOperationException("Not implemented yet");
// TRSPTour t = ((TRSPTour)move.getTour());
// double before = t.getMinimalDuration();
// int pred = t.getPred(move.getNodeId());
// t.removeNode(move.getNodeId());
}
@Override
public boolean isInsertionSeqDependent() {
return true;
}
@Override
public void nodeFrozen(TRSPTour tour, int node) {
super.nodeFrozen(tour, node);
evaluateTRSPTour(tour, node, true);
}
}