/** * */ package vroom.trsp.optimization.split; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import vroom.trsp.datamodel.ITRSPTour; import vroom.trsp.datamodel.TRSPSimpleTour; import vroom.trsp.util.TRSPLogging; /** * <code>TRSPSplit</code> is an implementation of the split procedure that optimally splits a giant tour into a set of * feasible tours. * <p> * Creation date: Sep 26, 2011 - 4:09:20 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 TRSPSplit { private final SplitTourArcBuilder mArcBuilder; /** * Creates a new <code>TRSPSplit</code> * * @param constraints * @param costDelegate */ public TRSPSplit(SplitTourArcBuilder arcBuilder) { super(); mArcBuilder = arcBuilder; } /** * Optimally split a giant tour in a set of feasible tours * * @param giantTour * the giant tour to be split, should start at the technician home and be open-ended * @return a collection of the tours resulting from the split of <code>giantTour</code> */ public Collection<ITRSPTour> splitTour(ITRSPTour giantTour) { if (giantTour.length() == 0) return Collections.<ITRSPTour> emptySet(); // Array of labels (costs) double[] labels = new double[giantTour.length()]; Arrays.fill(labels, Double.POSITIVE_INFINITY); // Array of predecessors SplitTourArc[] arcs = new SplitTourArc[giantTour.length()]; // Main loop iterator int i = 0; labels[i] = 0; // -------------------------------------- // Shortest path // -------------------------------------- // Loop for the arc tail // TRSPLogging.getOptimizationLogger().lowDebug( // "TRSPSplit.splitTour: Splitting giant tour %s", giantTour); while (i < giantTour.length()) { if (labels[i] == Double.POSITIVE_INFINITY) { TRSPLogging .getOptimizationLogger() .warn("TRSPSplit.splitTour: request %s seems to be incompatibel with technician %s, check preprocessing (aborting)", giantTour.getNodeAt(i), giantTour.getTechnicianId()); return Collections.<ITRSPTour> emptySet(); } boolean feasible = true; int j = i + 1; SplitTourArc arc = null; // Loop for the arc head // Abort when an arc is infeasible, as longer arcs will also be infeasible while (j < giantTour.length() && feasible) { // Generate the arc arc = mArcBuilder.buildArc(giantTour, i + 1, j); if (arc == null) { // TRSPLogging.getOptimizationLogger().lowDebug( // "TRSPSplit.splitTour: ([%s]%s,[%s]%s) is infeasible", i, // giantTour.getNodeAt(i), j, giantTour.getNodeAt(j)); feasible = false; break; } // Evaluate cost double arcCost = mArcBuilder.evaluateArc(); double candLabel = labels[i] + arcCost; double oldLabel = labels[j]; if (candLabel < oldLabel) { // Promising new path // Check feasibility feasible = mArcBuilder.isFeasible(); if (feasible) { // The new path is feasible and improves the label labels[j] = candLabel; arcs[j] = arc; } } // TRSPLogging // .getOptimizationLogger() // .lowDebug( // "TRSPSplit.splitTour: ([%s]%s,[%s]%s) cost:%.3f feas:%s imp:%s oldLabel:%.3f candLabel:%.3f", // i, giantTour.getNodeAt(i), j, giantTour.getNodeAt(j), // arc.getTotalCost(), feasible, candLabel < oldLabel, oldLabel, // candLabel); j++; } i++; } // -------------------------------------- // Extract the arcs from the shortest path // -------------------------------------- LinkedList<ITRSPTour> tours = new LinkedList<ITRSPTour>(); int idx = giantTour.length() - 1; while (idx != 0) { if (arcs[idx] != null) { // Add the current arc tours.add(arcs[idx]); // Walk back to the tail idx = arcs[idx].getStart() - 1; } else { idx--; } } // boolean check = checkSplit(giantTour, tours); TRSPLogging.getOptimizationLogger().lowDebug("TRSPSplit.splitTour: Giant tour split in %s tours", tours.size()); return tours; } public static boolean checkSplit(TRSPSimpleTour giantTour, List<ITRSPTour> splittedTours) { int idx = 1; // Start from the end: assumes that the first tour was added last ListIterator<ITRSPTour> it = splittedTours.listIterator(splittedTours.size()); while (idx < giantTour.length()) { ITRSPTour splitTour = it.previous(); for (int node : splitTour) { if (giantTour.getInstance().isRequest(node)) { if (node != giantTour.getNodeAt(idx)) return false; idx++; } } } return true; } }