package vroom.trsp.optimization.matheuristic; import gurobi.GRB; import gurobi.GRB.DoubleAttr; import java.util.Collection; import java.util.List; import vroom.common.utilities.IDisposable; import vroom.common.utilities.Stopwatch; import vroom.common.utilities.Stopwatch.ReadOnlyStopwatch; import vroom.common.utilities.lp.SolverStatus; import vroom.trsp.datamodel.ITRSPTour; import vroom.trsp.datamodel.TRSPInstance; import vroom.trsp.datamodel.TRSPRequest; import vroom.trsp.datamodel.TRSPSolution; import vroom.trsp.datamodel.TRSPTour; import vroom.trsp.util.TRSPGlobalParameters; import vroom.trsp.util.TRSPLogging; /** * <code>SCSolverBase</code> is the base class for solvers that solve set covering model for the TRSP. * <p> * Creation date: Aug 17, 2011 - 10:10:59 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 abstract class SCSolverBase implements IDisposable { /** Zero tolerance for the variable values reported by the solve */ public static final double ZERO_TOLERANCE = 1e-10; public abstract SolverStatus solve(); /** * Define an incumbent solution for the current model. * <p> * This method will attempt to find the variables corresponding to the tours of the given <code>incumbent</code> and * set their {@link DoubleAttr#Start} value to 1. All other variables are left to 0 if the solution is feasible, or * {@link GRB#UNDEFINED} if not * </p> * * @param incumbent * the incumbent solution * @return <code>true</code> if the incumbent was defined correctly, <code>false</code> if an exception was caught */ public abstract boolean setIncumbent(TRSPSolution incumbent); /** * Add a set of columns (tours) to this model. * * @param tours * the tours to be added * @return <code>true</code> if columns were added correctly, <code>false</code> if an exception was caught */ public abstract boolean addColumns(Collection<ITRSPTour> tours); /** * Add covering constraints for all the given requests. * <p> * Please note that this is done by default for all the requests present in the instance given to constructor * {@link #SCGurobiSolver(TRSPInstance)} * * @param requests * the requests to be covered * @param forceEqual * <code>true</code> if constraints should be of form <code>=1</code> (set partitioning formulation) * @return <code>true</code> if constraints were added correctly, <code>false</code> if an exception was caught */ public abstract boolean addCoveringConstraints(List<TRSPRequest> requests, boolean equal); /** A timer for the solver wall clock */ final Stopwatch mTimer; /** The Instance. */ private final TRSPInstance mInstance; /** * Returns the timer for the solver wall clock * * @return the timer for the solver wall clock */ public ReadOnlyStopwatch getTimer() { return mTimer.getReadOnlyStopwatch(); } /** The solution found during the last run */ private TRSPSolution mSolution; /** * Returns the solution found during the last run. * * @return the solution found during the last run */ public TRSPSolution getSolution() { return mSolution; } /** * Sets the current solution found by the solver * * @param solution * the current solution found by the solver */ void setSolution(TRSPSolution solution) { mSolution = solution; } /** * Gets the single instance of SCGurobiSolver. * * @return single instance of SCGurobiSolver */ public TRSPInstance getInstance() { return mInstance; } /** the global parameters used in this instance **/ private final TRSPGlobalParameters mParameters; /** * Getter for the global parameters used in this instance * * @return the value of name */ public TRSPGlobalParameters getParameters() { return this.mParameters; } /** The status returned by the solver during the last call to {@link #solve()} */ protected SolverStatus mStatus; public SCSolverBase(TRSPInstance instance, TRSPGlobalParameters parameters) { super(); mTimer = new Stopwatch(); mInstance = instance; mParameters = parameters; } /** * The status returned by the solver during the last call to {@link #solve()} * * @return the status returned by the solver during the last call to {@link #solve()} */ public SolverStatus getStatus() { return mStatus; } /** * Repair the current solution by ensuring that each client is visited exactly once * * @return <code>true</code> if the solution was initially infeasible, <code>false</code> if it was feasible */ public boolean repairSolution() { if (getSolution() == null) return false; boolean r = false; boolean[] visitedNodes = new boolean[getInstance().getMaxId()]; TRSPTour[] visitingTour = new TRSPTour[getInstance().getMaxId()]; for (int t = 0; t < getSolution().getTourCount(); t++) { TRSPTour tour = getSolution().getTour(t); tour.setAutoUpdated(true); for (int node : tour) { if (!getInstance().isRequest(node)) continue; if (visitedNodes[node]) { r = true; // Found a node that is already visited // Evaluate detour cost in both tours double prevCost = getSolution().getObjectiveValue(); int tour1pred = visitingTour[node].getPred(node); int tour1succ = visitingTour[node].getSucc(node); int tour2pred = tour.getPred(node); int tour2succ = tour.getSucc(node); double detour1 = getSolution().getCostDelegate().evaluateDetour(// visitingTour[node], tour1pred, node, tour1succ, true); double detour2 = getSolution().getCostDelegate().evaluateDetour(// tour, tour2pred, node, tour2succ, true); TRSPTour removedTour = null; TRSPTour selectedTour = null; double savings; if (detour1 > detour2) { removedTour = visitingTour[node]; selectedTour = tour; savings = detour1; } else { removedTour = tour; selectedTour = visitingTour[node]; savings = detour2; } removedTour.removeNode(node); visitingTour[node] = selectedTour; TRSPLogging .getOptimizationLogger() .info("SCSolverBase.repairSolution: removed duplicate visit of %s from tour %s (savings: %.3f)", node, removedTour.getTechnicianId(), savings); if (getSolution().getObjectiveValue() > prevCost) { TRSPLogging .getOptimizationLogger() .warn("SCSolverBase.repairSolution: total cost increased after removing visit of %s from tour %s (was:%s is:%s)", node, removedTour.getTechnicianId(), prevCost, getSolution().getObjectiveValue()); } } else { visitedNodes[node] = true; visitingTour[node] = tour; } } } return r; } }