package vroom.trsp.datamodel; import java.util.Arrays; import vroom.common.utilities.Constants; import vroom.trsp.datamodel.TRSPTour.TRSPTourIterator; /** * The Class <code>TRSPDetailedSolutionChecker</code> is a utility class that checks the coherence of a * {@link TRSPSolution} and {@link TRSPTour}, including the stored information * <p> * Creation date: Mar 16, 2011 - 4:49:29 PM. * * @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 TRSPDetailedSolutionChecker extends TRSPSolutionCheckerBase { public static final TRSPDetailedSolutionChecker INSTANCE = new TRSPDetailedSolutionChecker(); /** * Creates a new <code>TRSPDetailedSolutionChecker</code> */ public TRSPDetailedSolutionChecker() { super(true); } /** * Creates a new <code>TRSPDetailedSolutionChecker</code> * * @param checkUnserved * <code>true</code> if unserved reauests should be checked, <code>false</code> otherwise */ public TRSPDetailedSolutionChecker(boolean checkUnserved) { super(checkUnserved); } /** * Check the coherence of a tour * * @param mytour * the tour to be checked * @param startEndDepot * <code>true</code> if all tours must start and end at a depot * @return a string describing the <code>tour</code> incoherences or <code>null</code> if none was found */ @Override public String checkTour(ITRSPTour tour) { if (!TRSPTour.class.isAssignableFrom(tour.getClass())) { return TRSPSolutionChecker.INSTANCE.checkTour(tour); } TRSPTour mytour = (TRSPTour) tour; StringBuilder err = new StringBuilder(); TRSPInstance instance = mytour.getInstance(); Technician tech = instance.getFleet().getVehicle(mytour.getTechnicianId()); // --------------------------------------------------------------- // Check if the tour starts and end at the technician home int home = tech.getHome().getID(); int homeEnd = instance.getHomeDuplicate(home); if (mytour.getFirstNode() != home || mytour.getLastNode() != homeEnd) err.append("Tour does not start/end at the technician home"); // --------------------------------------------------------------- int length = 0; boolean[] tools = mytour.getTechnicianToolSet(); int[] spare = mytour.getTechnicianSpareParts(); double earliest = Long.MIN_VALUE; double[][] wait = new double[mytour.getInstance().getMaxId()][mytour.getInstance() .getMaxId()]; double val = mytour.getCostDelegate().evaluateTour(mytour, false); if (Math.abs(val - mytour.getTotalCost()) > 1e-3) { err.append(String.format("Bad total cost (expected %.3f is %.3f)", val, mytour.getTotalCost())); } TRSPTourIterator fwdit = mytour.iterator(); boolean checkSpare = true, checkTools = true, checkTime = true, checkWait = true, checkLateness = true, checkLatestFeas = true, checkSpareReq = true; int pred = -1; while (fwdit.hasNext()) { int node = fwdit.next(); if (pred == -1) earliest = 0; else { earliest = mytour.getTimeWindow(pred).getEarliestStartOfService(earliest); earliest += mytour.getServiceTime(pred); earliest += mytour.getTravelTime(pred, node); } if (checkTime && mytour.getEarliestArrivalTime(node) != earliest) { err.append(String.format("Bad earliest time at node %s (expected %.3f is %.3f) ", node, earliest, mytour.getEarliestArrivalTime(node))); checkTime = false; } if (checkTime && !mytour.getTimeWindow(node).isFeasible(earliest)) { err.append(String.format("Time window violated at node %s (@.3f@%s) ", node, earliest, mytour.getTimeWindow(node))); checkTime = false; } wait[node][node] = Math.max(0, mytour.getTimeWindow(node).startAsDouble() - earliest); if (checkWait && mytour.getWaitingTime(node) != wait[node][node]) { err.append(String.format("Bad waiting time at node %s (expected %.3f is %.3f) ", node, wait[node][node], mytour.getWaitingTime(node))); checkTime = false; } // double lateness = mytour.getTimeWindow(node).getViolation(earliest); // if (checkLateness && mytour.getLateness(node) != lateness) { // err.append(String.format("Bad lateness at node %s (expected %.3f is %.3f) ", node, lateness, // mytour.getLateness(node))); // checkLateness = false; // } length++; if (instance.isMainDepot(node)) { Arrays.fill(tools, true); Arrays.fill(spare, Integer.MAX_VALUE); } else if (!instance.isDepot(node)) { for (int i = 0; i < spare.length; i++) { if (spare[i] != Integer.MAX_VALUE) spare[i] -= instance.getRequest(node).getSparePartRequirement(i); } } for (int i = 0; i < spare.length && checkSpare; i++) { if (spare[i] != mytour.getAvailableSpareParts(node, i)) { err.append(String.format( "Bad number of spare parts of type %s at node %s (expected %s is %s) ", i, node, spare[i], mytour.getAvailableSpareParts(node, i))); checkSpare = false; } } for (int i = 0; i < tools.length && checkTools; i++) { if (tools[i] != mytour.isToolAvailable(node, i)) { err.append(String.format( "Bad availability for tool %s at node %s (expected %s is %s) ", i, node, tools[i], mytour.isToolAvailable(node, i))); checkTools = false; } } pred = node; // System.out.printf("%3s %6.1f@%s\t%s\n",node,earliest,mytour.getTimeWindow(node),spare[0]); } fwdit = mytour.iterator(); boolean checkFwdSlack = true; while (fwdit.hasNext()) { int node = fwdit.next(); TRSPTourIterator sucIt = mytour.iterator(node); double cumWait = 0; if (!sucIt.hasNext()) continue; sucIt.next(); while (sucIt.hasNext()) { int succ = sucIt.next(); wait[node][succ] = cumWait; cumWait += wait[succ][succ]; if (!instance.isCVRPTW() && checkWait && !Constants.equals(mytour.getWaitingTime(node, succ), wait[node][succ])) { err.append(String .format("Bad cumulated slack time between node %s and %s (expected %.3f is %.3f) ", node, succ, wait[node][succ], mytour.getWaitingTime(node, succ))); checkTime = false; } double slack = evaluateFwdSlackTime(mytour, node, succ); if (!instance.isCVRPTW() && checkFwdSlack && !Constants.equals(mytour.getFwdSlackTime(node, succ), slack)) { err.append(String .format("Bad forward slack time between node %s and %s (expected %.3f is %.3f) ", node, succ, slack, mytour.getFwdSlackTime(node, succ))); checkFwdSlack = false; } } } TRSPTourIterator bkwdit = mytour.iterator(mytour.getLastNode()); double lft = Double.POSITIVE_INFINITY; int[] spareReq = new int[instance.getSpareCount()]; int succ = ITRSPTour.UNDEFINED; while (bkwdit.hasPrevious() && (checkLatestFeas || checkSpareReq)) { int node = bkwdit.previous(); if (succ == ITRSPTour.UNDEFINED) { lft = mytour.getTimeWindow(node).endAsDouble(); if (instance.isRequest(node)) spareReq = instance.getRequest(node).getSparePartRequirements(); } else { lft = Math.min(mytour.getTimeWindow(node).endAsDouble(), lft - mytour.getServiceTime(node) - mytour.getTravelTime(node, succ)); if (instance.isRequest(node)) for (int i = 0; i < spareReq.length; i++) { spareReq[i] += instance.getRequest(node).getSparePartRequirement(i); } // (Do not) Reset the requirement (see TRSPTour#getRequiredSpareParts(int,int) // else if (instance.isMainDepot(node)) // Arrays.fill(spareReq, 0); } succ = node; // Check latest feasible arrival time if (lft != mytour.getLatestFeasibleArrivalTime(node)) { checkLatestFeas = false; err.append(String.format("Bad latest arrival time at node %s (expected %s is %s)", node, lft, mytour.getLatestFeasibleArrivalTime(node))); } // FIXME Check required spare parts for (int i = 0; i < spareReq.length; i++) { if (spareReq[i] != mytour.getRequiredSpareParts(node, i)) { err.append(String.format( "Bad spare part requirement of type %s at node %s (expected %s is %s)", i, node, spareReq[i], mytour.getRequiredSpareParts(node, i))); checkSpareReq = false; } } } if (tour.getInstance().getSimulator() != null) { append(err, checkServedAssignedRequests(tour)); } return err.toString(); } }