/** * */ package vroom.optimization.online.jmsa.benchmarking; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Arrays; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import vroom.common.modeling.dataModel.IVRPInstance; import vroom.common.modeling.io.NovoaPersistenceHelper.DemandDistribution; import vroom.common.utilities.BestKnownSolutions; import vroom.common.utilities.StatCollector; import vroom.common.utilities.StatCollector.Label; import vroom.common.utilities.logging.LoggerHelper; import vroom.common.utilities.lp.SolverStatus; import vroom.optimization.online.jmsa.MSAGlobalParameters; import vroom.optimization.pl.symphony.vrp.CVRPSymphonySolver; /** * <code>PerfectInformationSolver</code> * <p> * Creation date: Sep 17, 2010 - 11:25:28 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 PerfectInformationSolver { public final static LoggerHelper LOGGER = LoggerHelper .getLogger(PerfectInformationSolver.class .getSimpleName()); public static final String SOL_FILE = "data/perfinf/perf_inf.sol"; public static final String CSV_FILE = "data/perfinf/perf_inf.csv"; public static String TEMP_FOLDER = "tmp"; public static final Label<?>[] LABELS = new Label<?>[] { new Label<Integer>("run_id", Integer.class), new Label<String>("instance_name", String.class), new Label<Integer>("size", Integer.class), new Label<Integer>("vehicle_capacity", Integer.class), new Label<Double>("running_time", Double.class), new Label<Double>("perf_inf", Double.class), new Label<Integer>("num_trucks", Integer.class), new Label<String>("seeds", String.class) }; private final static BestKnownSolutions mBKS; static { mBKS = new BestKnownSolutions(SOL_FILE); mBKS.setAllOptimal(true); // save extra verbose in file } private final MSAGlobalParameters mParams; private final StatCollector mCollector; private CVRPSymphonySolver mSolver; private static final Lock sIOLock = new ReentrantLock(); /** * Creates a new <code>PerfectInformationSolver</code> */ public PerfectInformationSolver() { mParams = new MSAGlobalParameters(); NovoaRun.loadDefaultParameters(mParams); mCollector = new StatCollector(new File(CSV_FILE), true, true, "Perfect Information Solutions", LABELS); } /** * Solve all the instances described by the arguments. * * @param sizes * instance sizes * @param caps * instance capacities * @param sets * instance sets * @param firstRun * @param lastRun * @param numThreads * @param timeout * the maximum time per instance in seconds */ public static void solvePerferctInformation(int[] sizes, int[] caps, int[] sets, int firstRun, int lastRun, int numThreads, final int timeout) { LinkedBlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(); ThreadPoolExecutor executor = new ThreadPoolExecutor(numThreads, numThreads, 1, TimeUnit.MINUTES, workQueue); final LinkedBlockingQueue<PerfectInformationSolver> solvers = new LinkedBlockingQueue<PerfectInformationSolver>( numThreads); for (int s = 0; s < numThreads; s++) { solvers.add(new PerfectInformationSolver()); } for (int set : sets) { for (int size : sizes) { for (int cap : caps) { for (int num = 1; num <= 5; num++) { for (int run = firstRun; run < lastRun; run++) { final int setF = set, sizeF = size, capF = cap, numF = num, runF = run; executor.execute(new Runnable() { @Override public void run() { PerfectInformationSolver s = solvers.poll(); s.solvePerfectInformation(runF, sizeF, numF, capF, setF, timeout, false, true, DemandDistribution.UNIFORM); solvers.add(s); } }); } } } } } executor.shutdown(); try { executor.awaitTermination(7, TimeUnit.DAYS); } catch (InterruptedException e) { LOGGER.exception("PerfectInformationSolver.solvePerferctInformation", e); } } /** * Solve a single instance and save the perfect information value in the {@link #SOL_FILE} file. * * @param run * run id * @param size * instance size ( 5, 8, 20, 30, 40, 60, 100) * @param rep * instance replica (1 - 5) * @param cap * vehicle capacity (0, 1) * @param set * instance set (0,1) * @param timeout * the maximum time in seconds * @param force * <code>true</code> if the optimization should be performed even if a solution already exists * @param write * <code>true</code> if new solutions should be automatically writen to the solution file * @return the perfect information value for the specified instance */ public synchronized double solvePerfectInformation(final int run, final int size, final int rep, final int cap, final int set, final int timeout, boolean force, boolean write) { return solvePerfectInformation(run, size, rep, cap, set, timeout, force, write, DemandDistribution.UNIFORM); } /** * Solve a single instance and save the perfect information value in the {@link #SOL_FILE} file. * * @param run * run id * @param size * instance size ( 5, 8, 20, 30, 40, 60, 100) * @param rep * instance replica (1 - 5) * @param cap * vehicle capacity (0, 1) * @param set * instance set (0,1) * @param timeout * the maximum time in seconds * @param force * <code>true</code> if the optimization should be performed even if a solution already exists * @param write * <code>true</code> if new solutions should be automatically written to the solution file * @param distribution * the demand distribution to use * @return the perfect information value for the specified instance */ public synchronized double solvePerfectInformation(final int run, final int size, final int rep, final int cap, final int set, final int timeout, boolean force, boolean write, DemandDistribution distribution) { mSolver = new CVRPSymphonySolver(); mSolver.setTempFolder(TEMP_FOLDER); mSolver.setTimeLimit(timeout * 1000); String code = NovoaBenchmarking.getInstanceCode(run, size, rep, cap, set, distribution); LOGGER.info("Solving instance : %s", code); sIOLock.lock(); if (!force && mBKS.isOptimal(code)) { LOGGER.info(" -Perfect information already known: %s", mBKS.getBKS(code)); sIOLock.unlock(); return mBKS.getBKS(code); } // Look for an upper bound in the first 100 runs of the same instance double ub = -1; for (int r = 0; r < 100; r++) { Double bks = mBKS.getBKS(NovoaBenchmarking.getInstanceCode(r, size, rep, cap, set, distribution)); if (bks != null && bks > ub) { ub = bks; } } if (ub < 0) { ub = Double.POSITIVE_INFINITY; } sIOLock.unlock(); long[] seeds = NovoaBenchmarking.getSeeds(run, size, rep, cap, set); NovoaRun novoa = null; try { novoa = new NovoaRun(set, size, rep, cap, run, mParams, distribution); } catch (IOException e1) { LOGGER.exception("PerfectInformationSolver.solvePerfectInformation", e1); return -1; } IVRPInstance instance = novoa.getSimulationInstance(); SolverStatus status = SolverStatus.UNKNOWN_STATUS; try { LOGGER.debug("Setting the upper bound to %s", ub); mSolver.readInstance(instance); mSolver.setUpperBound(ub); status = mSolver.solve(); // if (status == SolverStatus.INFEASIBLE) { // LOGGER.info("Problem is infeasible, relaxing the upper bound"); // mSolver.setUpperBound(Double.POSITIVE_INFINITY); // status = mSolver.solve(); // } } catch (Exception e) { LOGGER.exception("PerfectInformationSolver.solvePerfectInformation (%s)", e, instance.getName()); } if (status == SolverStatus.OPTIMAL) { LOGGER.info(" -Perfect information value: %s (%sms)", mSolver.getObjectiveValue(), mSolver.getSolveTime()); if (write) { sIOLock.lock(); mBKS.setBestKnownSolution(code, mSolver.getObjectiveValue(), true, null, null, null); try { mBKS.save("Perfect Information, solver: " + mSolver.getClass().getSimpleName()); } catch (FileNotFoundException e) { LOGGER.exception( "PerfectInformationSolver.solvePerfectInformation-saveBKS(%s)", e, instance.getName()); } catch (IOException e) { LOGGER.exception( "PerfectInformationSolver.solvePerfectInformation-saveBKS (%s)", e, instance.getName()); } mCollector.collect(Integer.valueOf(run), instance.getName(), Integer.valueOf(size), Integer.valueOf((int) instance.getFleet().getVehicle().getCapacity()), Double.valueOf(mSolver.getSolveTime()), Double.valueOf(mSolver.getObjectiveValue()), Integer.valueOf(mSolver.getNumTrucks()), Arrays.toString(seeds)); sIOLock.unlock(); } return mSolver.getObjectiveValue(); } else { LOGGER.warn("Error, solver status: %s", status); return Double.NaN; } } public static void main(String[] args) { int[] sizes = { 5 }; int[] sets = { 1 }; int[] runs = { 0, 99 }; int numThreads = 1; if (args.length == 4) { sizes = vroom.common.utilities.Utilities.toIntArray(args[0]); sets = vroom.common.utilities.Utilities.toIntArray(args[1]); runs = vroom.common.utilities.Utilities.toIntArray(args[2]); numThreads = Integer.valueOf(args[3]); } else { System.err .println("Wrong number of arguments, usage: main [sizes] [sets] [runs] numThreads"); System.err.println("Using default values"); } LoggerHelper.setupRootLogger(LoggerHelper.LEVEL_WARN, LoggerHelper.LEVEL_INFO, true); LOGGER.setLevel(LoggerHelper.LEVEL_INFO); CVRPSymphonySolver.LOGGER.setLevel(LoggerHelper.LEVEL_WARN); solvePerferctInformation(sizes, new int[] { 0, 1 }, sets, runs[0], runs[1], numThreads, 60 * 10); } }