/**
*
*/
package vroom.common.heuristics.vns.benchmarking;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import umontreal.iro.lecuyer.rng.MRG32k3a;
import vroom.common.heuristics.ConstraintHandler;
import vroom.common.heuristics.GenericNeighborhood;
import vroom.common.heuristics.GenericNeighborhoodHandler;
import vroom.common.heuristics.GenericNeighborhoodHandler.Strategy;
import vroom.common.heuristics.cw.CWParameters;
import vroom.common.heuristics.cw.algorithms.RandomizedSavingsHeuristic;
import vroom.common.heuristics.cw.kernel.ClarkeAndWrightHeuristic;
import vroom.common.heuristics.vns.VariableNeighborhoodSearch;
import vroom.common.heuristics.vns.VariableNeighborhoodSearch.VNSVariant;
import vroom.common.heuristics.vrp.OrOptNeighborhood;
import vroom.common.heuristics.vrp.StringExchangeNeighborhood;
import vroom.common.heuristics.vrp.SwapNeighborhood;
import vroom.common.heuristics.vrp.TwoOptNeighborhood;
import vroom.common.heuristics.vrp.VRPParameters;
import vroom.common.heuristics.vrp.constraints.CapacityConstraint;
import vroom.common.modeling.dataModel.ListRoute.ArrayListRoute;
import vroom.common.modeling.dataModel.Fleet;
import vroom.common.modeling.dataModel.IVRPInstance;
import vroom.common.modeling.dataModel.Solution;
import vroom.common.modeling.io.ChristofidesPersistenceHelper;
import vroom.common.modeling.io.FlatFilePersistenceHelper;
import vroom.common.modeling.io.NovoaPersistenceHelper;
import vroom.common.modeling.io.TSPLibPersistenceHelper;
import vroom.common.utilities.BestKnownSolutions;
import vroom.common.modeling.util.DefaultSolutionFactory;
import vroom.common.modeling.util.SolutionChecker;
import vroom.common.utilities.StatCollector;
import vroom.common.utilities.StatCollector.Label;
import vroom.common.utilities.logging.LoggerHelper;
import vroom.common.utilities.logging.Logging;
import vroom.common.utilities.optimization.INeighborhood;
import vroom.common.utilities.optimization.OptimizationSense;
/**
* <code>VNSBenchmark</code> is a test class for the VLS implementation based on Solomon instances
* <p>
* Creation date: Jun 28, 2010 - 4:51:36 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 VNSBenchmark {
public final static String SOLOMON_INSTANCES_PATH = "../Instances/vrptw/solomon";
public final static String AUGERAT_INSTANCES_PATH = "../Instances/cvrp/augerat";
public final static String AUGERAT_SOLUTION_FILE = "../Instances/cvrp/augerat.sol";
public final static String CHRISTOFIDES_INSTANCES_PATH = "../Instances/cvrp/christodifes-mingozzi-toth";
public final static String CHRISTOFIDES_SOLUTION_FILE = "../Instances/cvrp/christodifes-mingozzi-toth.sol";
public final static String NOVOA_INSTANCES_PATH = "../Instances/vrpsd/novoa";
public final static int[] NOVOA_SIZES = new int[] { 5,
8, 20, 40, 60 };
public final static int[] NOVOA_SETS = new int[] { 1 };
public final static int[] NOVOA_CAPACITIES = new int[] { 0, 1 };
public final static String OUTPUT_FORMAT = "./benchmarks/vns/%1$s-%2$ty%2$tm%2$td_%2$tk%2$tM.csv";
// private final static String INSTANCE_PATH = NOVOA_INSTANCES_PATH;
// private final static String BKS_FILE = "../Instances/vrpsd/novoa.sol";
// private final static String BENCH_NAME = "novoa";
// private final static String INSTANCE_FILTER = "i_";
// private final static boolean RESET_ON_SIZE_CHANGE = true;
// protected final static FlatFilePersistenceHelper INSTANCE_READER = new
// NovoaPersistenceHelper();
// private final static String INSTANCE_PATH = AUGERAT_INSTANCES_PATH;
// private final static String BKS_FILE = AUGERAT_SOLUTION_FILE;
// private final static String BENCH_NAME = "augerat";
// private final static String INSTANCE_FILTER = "P-";
// private final static boolean RESET_ON_SIZE_CHANGE = false;
// protected final static FlatFilePersistenceHelper INSTANCE_READER = new
// TSPLibPersistenceHelper();
private final static String INSTANCE_PATH = CHRISTOFIDES_INSTANCES_PATH;
private final static String BKS_FILE = CHRISTOFIDES_SOLUTION_FILE;
private final static String BENCH_NAME = "christofides";
private final static String INSTANCE_FILTER = "vrpnc";
private final static boolean RESET_ON_SIZE_CHANGE = false;
protected final static FlatFilePersistenceHelper INSTANCE_READER = new ChristofidesPersistenceHelper();
private static final boolean UPDATE_BKS = false;
public static final Label<?>[] STATS_LABELS = new Label<?>[] {
new Label<String>("Variant", String.class),
new Label<String>("strategy", String.class),
new Label<String>("instance", String.class), new Label<Integer>("run", Integer.class),
new Label<Integer>("size", Integer.class), new Label<Double>("obj", Double.class),
new Label<Double>("bks", Double.class), new Label<Double>("gap", Double.class),
new Label<Long>("cpu_time", Long.class), new Label<Integer>("used_veh", Integer.class),
new Label<Integer>("num_veh", Integer.class),
new Label<Boolean>("too_many_veh", Boolean.class) };
protected final static LoggerHelper LOGGER = LoggerHelper
.getLogger(VNSBenchmark.class
.getSimpleName());
private final static File INSTANCES_DIRECTORY = new File(
INSTANCE_PATH);
protected final static List<File> INSTANCES_FILES;
static {
String[] children = INSTANCES_DIRECTORY.list();
if (children == null) {
// Either dir does not exist or is not a directory
INSTANCES_FILES = null;
System.err.println("Invalid directory :" + INSTANCES_DIRECTORY);
System.exit(1);
} else {
INSTANCES_FILES = new LinkedList<File>();
for (String element : children) {
if (element.startsWith(INSTANCE_FILTER)) {
INSTANCES_FILES.add(new File(INSTANCES_DIRECTORY.getAbsolutePath()
+ File.separator + element));
}
}
}
}
@SuppressWarnings({ "unchecked", "unused", "rawtypes" })
public static void main(String[] args) {
Logging.setupRootLogger(LoggerHelper.LEVEL_WARN, LoggerHelper.LEVEL_WARN, false);
GenericNeighborhood.setCheckSolutionAfterMove(false);
BestKnownSolutions bestKnownSolutions = null;
if (BKS_FILE != null) {
bestKnownSolutions = new BestKnownSolutions(BKS_FILE);
}
File statFile = new File(String.format(OUTPUT_FORMAT, BENCH_NAME,
new Date(System.currentTimeMillis())));
StatCollector statCollector = new StatCollector(statFile, true, false, INSTANCE_PATH,
STATS_LABELS);
// # runs per instance
int runsPerInstance = 50;
// Constraint Handler
ConstraintHandler<Solution<ArrayListRoute>> ctrHandler = new ConstraintHandler<Solution<ArrayListRoute>>();
ctrHandler.addConstraint(new CapacityConstraint<Solution<ArrayListRoute>>());
// Neighborhoods
// swap
INeighborhood<Solution<ArrayListRoute>, ?> swap = new SwapNeighborhood<Solution<ArrayListRoute>>(
ctrHandler);
// 2-opt
TwoOptNeighborhood<Solution<ArrayListRoute>> twoOpt = new TwoOptNeighborhood<Solution<ArrayListRoute>>(
ctrHandler);
// Or-opt
OrOptNeighborhood<Solution<ArrayListRoute>> orOpt = new OrOptNeighborhood<Solution<ArrayListRoute>>(
ctrHandler);
// string-exchange
StringExchangeNeighborhood<Solution<ArrayListRoute>> strEx = new StringExchangeNeighborhood<Solution<ArrayListRoute>>(
ctrHandler);
List<INeighborhood<Solution<ArrayListRoute>, ?>> gvnsNeigh = new LinkedList<INeighborhood<Solution<ArrayListRoute>, ?>>();
List<INeighborhood<Solution<ArrayListRoute>, ?>> gvnsLSNeigh = new LinkedList<INeighborhood<Solution<ArrayListRoute>, ?>>();
List<INeighborhood<Solution<ArrayListRoute>, ?>> vndNeigh = new LinkedList<INeighborhood<Solution<ArrayListRoute>, ?>>();
gvnsLSNeigh.add(swap);
gvnsLSNeigh.add(twoOpt);
gvnsLSNeigh.add(orOpt);
gvnsNeigh.add(strEx);
vndNeigh.add(swap);
vndNeigh.add(twoOpt);
vndNeigh.add(orOpt);
vndNeigh.add(strEx);
// Collections.reverse(neighborhoods);
// GVNS
// VariableNeighborhoodSearch<Solution<ArrayListRoute>> gvns = new
// VariableNeighborhoodSearch<Solution<ArrayListRoute>>(
// OptimizationSense.MINIMIZATION, null, gvnsNeigh);
VariableNeighborhoodSearch<Solution<ArrayListRoute>> gvnsLS = VariableNeighborhoodSearch
.newVNS(VNSVariant.VND, OptimizationSense.MINIMIZATION, null, new MRG32k3a(),
gvnsLSNeigh);
VariableNeighborhoodSearch<Solution<ArrayListRoute>> gvns = VariableNeighborhoodSearch
.newVNS(VNSVariant.GVNS, OptimizationSense.MINIMIZATION, gvnsLS, new MRG32k3a(),
gvnsNeigh);
VariableNeighborhoodSearch<Solution<ArrayListRoute>> vnd = VariableNeighborhoodSearch
.newVNS(VNSVariant.VND, OptimizationSense.MINIMIZATION, null, new MRG32k3a(),
vndNeigh);
VariableNeighborhoodSearch[] vnsVariants = new VariableNeighborhoodSearch[] { gvns };// ,
// vnd
// };
Map<VariableNeighborhoodSearch<Solution<ArrayListRoute>>, String> vnsDescriptions = new HashMap<VariableNeighborhoodSearch<Solution<ArrayListRoute>>, String>();
vnsDescriptions.put(gvns, "GVNS");
vnsDescriptions.put(vnd, "VND");
// VNS Params
VRPParameters params = new VRPParameters(Long.MAX_VALUE, Integer.MAX_VALUE, true, true,
null);
System.out.println("Loading instances from " + INSTANCE_PATH);
List<IVRPInstance> instances = readInstances();
System.out.println("===========================================================");
System.out.println(" Best known solutions:");
for (IVRPInstance i : instances) {
System.out.printf("%s : %s %s\n", i.getName(), bestKnownSolutions.getBKS(i.getName()),
bestKnownSolutions.isOptimal(i.getName()) ? "(opt)" : "");
}
System.out.println("===========================================================");
System.out.println();
// Initial solutions
System.out.println("Generating initial solutions");
CWParameters cwParams = new CWParameters();
cwParams.set(CWParameters.SOLUTION_FACTORY_CLASS, DefaultSolutionFactory.class);
cwParams.set(CWParameters.RANDOM_SEED, 0l);
int initSol = 0;
Solution<ArrayListRoute>[] initialSolutions = (Solution<ArrayListRoute>[]) new Solution<?>[instances
.size() * runsPerInstance];
for (IVRPInstance instance : instances) {
ClarkeAndWrightHeuristic<Solution<ArrayListRoute>> cw = new ClarkeAndWrightHeuristic<Solution<ArrayListRoute>>(
cwParams, RandomizedSavingsHeuristic.class, ctrHandler);
for (int run = 0; run < runsPerInstance; run++) {
cw.initialize(instance);
cw.run();
initialSolutions[initSol++] = cw.getSolution();
}
}
System.out.println("Done");
System.out.println();
// Benchmark runs
System.out.printf("Running benchmarks with %s run per instance\n", runsPerInstance);
System.out.println("Stats printed in file " + statFile.toString());
System.out.println();
Map<VariableNeighborhoodSearch<Solution<ArrayListRoute>>, List<RunParams>> runParams = new HashMap<VariableNeighborhoodSearch<Solution<ArrayListRoute>>, List<RunParams>>();
// Setup the runs
// VNS
List<RunParams> tmpParams = new LinkedList<VNSBenchmark.RunParams>();
// for (Strategy strat : Strategy.values()) {
// for (int i = 0; i < 2; i++) {
// if (strat != Strategy.RANDOM || i == 0) {
// tmpParams.add(new RunParams(strat, strat == Strategy.SEQUENTIAL && i
// == 1,
// i == 0));
// }
// }
// }
// runParams.put(vnd, tmpParams);
// GVNS
tmpParams = new LinkedList<VNSBenchmark.RunParams>();
tmpParams.add(new RunParams(Strategy.EFFICIENCY_BASED, false, false));
runParams.put(gvns, tmpParams);
// // VND
// ((GenericNeighborhoodHandler<?>)
// gvnsLS.getNeighHandler()).setStrategy(Strategy.SEQUENTIAL);
List<RunParams> selectedVNSRunParams = null;
for (VariableNeighborhoodSearch<Solution<ArrayListRoute>> selectedVNS : vnsVariants) {
selectedVNSRunParams = runParams.get(selectedVNS);
String selectedVNSDesc = vnsDescriptions.get(selectedVNS);
System.out.println("######################################################");
System.out.println("VNS: " + selectedVNSDesc);
for (RunParams p : selectedVNSRunParams) {
((GenericNeighborhoodHandler) selectedVNS.getNeighHandler()).setStrategy(p.strat);
System.out.println("-------------------------------------------------");
System.out.println("Running benchmarks for strategy " + p.strat);
selectedVNS.getNeighHandler().reset();
if (p.strat != Strategy.SEQUENTIAL) {
if (p.reset) {
System.out.println(" with neighborhood handler reset");
} else {
System.out.println(" without neighborhood handler reset");
}
} else {
if (p.reverse) {
System.out.println(" reversed order");
Collections.reverse(selectedVNS.getNeighHandler().getComponents());
} else {
System.out.println(" normal order");
}
}
int numRuns = 0;
int rejectedCount = 0;
double costAverage = 0;
double timeAverage = 0;
double vnsTimeAverage = 0;
double gapAverage = 0;
double gapMax = 0;
double gapMin = Double.MAX_VALUE;
initSol = 0;
int prevSize = 0;
for (IVRPInstance instance : instances) {
if (RESET_ON_SIZE_CHANGE && prevSize != instance.getRequestCount()) {
// Reset if the instance size has changed
selectedVNS.getNeighHandler().reset();
}
prevSize = instance.getRequestCount();
for (int run = 0; run < runsPerInstance; run++) {
if (p.reset) {
selectedVNS.getNeighHandler().reset();
}
VNSRun bench = new VNSRun(instance, selectedVNS, ctrHandler, params);
bench.setSolution(initialSolutions[initSol++].clone());
// Coherence check
if (bench.getSolution().getParentInstance() != instance) {
throw new IllegalStateException("Inconsistency in initial solutions");
}
bench.run();
Solution<ArrayListRoute> sol = bench.getSolution();
LinkedList<ArrayListRoute> emptyRoutes = new LinkedList<ArrayListRoute>();
for (ArrayListRoute r : sol) {
if (r.length() == 2) {
emptyRoutes.add(r);
}
}
for (ArrayListRoute r : emptyRoutes) {
sol.removeRoute(r);
}
String check = SolutionChecker.checkSolution(sol, false, true, true);
boolean acceptable = INSTANCE_READER instanceof NovoaPersistenceHelper
|| sol.getRouteCount() <= instance.getFleet().size()
&& check == null;
if (check != null) {
System.out.println(check);
}
double time = bench.getVNSTimer().readTimeMS();
double cost = SolutionChecker.calculateCost(sol);
if (acceptable) {
costAverage += cost;
timeAverage += bench.getGlobalTimer().readTimeMS();
vnsTimeAverage += time;
} else {
rejectedCount++;
}
double bks = -1;
double gap = -1;
if (acceptable && bestKnownSolutions != null) {
if (bestKnownSolutions.getBKS(instance.getName()) != null) {
bks = bestKnownSolutions.getBKS(instance.getName());
gap = (cost - bks) / bks;
if (gap < -1e-5) {
if (bestKnownSolutions.isOptimal(instance.getName())) {
System.err
.printf("Found a solution better than optimal: %s - %s (bks:%s gap:%s) %s\n",
instance.getName(), cost, bks, gap, sol);
System.exit(1);
}
System.out
.printf("New best solution found: %s - %s (bks:%s gap:%s) %s\n",
instance.getName(), cost, bks, gap, sol);
bestKnownSolutions.setBestKnownSolution(instance.getName(),
cost, false, null, null, null);
}
gapAverage += gap;
gapMax = gap > gapMax ? gap : gapMax;
gapMin = gap < gapMin ? gap : gapMin;
} else {
System.out.printf(
"New best solution found: %s - %s (no previous)\n",
instance.getName(), cost);
bestKnownSolutions.setBestKnownSolution(instance.getName(), cost,
false, null, null, null);
}
}
statCollector.collect(
selectedVNSDesc,
p.toString(),
String.format("%s-%s", instance.getName(), instance.getFleet()
.getVehicle().getCapacity()), run,
instance.getRequestCount(), cost, bks, gap, time,
sol.getRouteCount(), instance.getFleet().size(), !acceptable);
numRuns++;
}
// Save the BKS
if (UPDATE_BKS && bestKnownSolutions != null) {
try {
bestKnownSolutions.save("Solutions found sith VNS");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
costAverage /= numRuns - rejectedCount;
timeAverage /= numRuns - rejectedCount;
vnsTimeAverage /= numRuns - rejectedCount;
gapAverage /= numRuns - rejectedCount;
// Let the log messages be printed
Object o = new Object();
synchronized (o) {
try {
o.wait(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(" --------");
System.out.printf("Total runs : %s\n", numRuns);
System.out.printf("Rejected solutions: %s\n", rejectedCount);
System.out.printf("Average cost : %.1f\n", costAverage);
System.out.printf("Average gap : %.1f%s\n", gapAverage * 100, "%");
System.out.printf("Min gap : %.1f%s\n", gapMin * 100, "%");
System.out.printf("Max gap : %.1f%s\n", gapMax * 100, "%");
System.out.printf("Average vns time : %.1f ms\n", vnsTimeAverage);
System.out.printf("Neighborhoods : %s\n", selectedVNS.getNeighHandler());
System.out.println(" --------");
System.out.println();
if (p.strat == Strategy.SEQUENTIAL && p.reverse) {
Collections.reverse(selectedVNS.getNeighHandler().getComponents());
}
}
}
if (UPDATE_BKS && bestKnownSolutions != null) {
try {
bestKnownSolutions.save("Solutions found sith VNS");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("FINISHED");
}
/**
* Read the instances
*
* @return a list of instances
*/
private static List<IVRPInstance> readInstances() {
List<IVRPInstance> instances = new LinkedList<IVRPInstance>();
if (INSTANCE_READER instanceof NovoaPersistenceHelper) {
try {
instances = new LinkedList<IVRPInstance>(
((NovoaPersistenceHelper) INSTANCE_READER).readInstances(NOVOA_SIZES,
NOVOA_CAPACITIES, NOVOA_SETS, null));
} catch (IOException e) {
e.printStackTrace();
}
} else {
for (File f : INSTANCES_FILES) {
if (!f.getName().equals("1_info.txt") && !f.getName().equals(".svn")) {
try {
IVRPInstance[] ins;
if (INSTANCE_READER instanceof TSPLibPersistenceHelper) {
ins = new IVRPInstance[] { INSTANCE_READER.readInstance(f, 100) };
int k = Integer.valueOf(ins[0].getName().substring(
ins[0].getName().lastIndexOf('k') + 1));
ins[0].setFleet(Fleet.newHomogenousFleet(k, ins[0].getFleet()
.getVehicle()));
} else if (INSTANCE_READER instanceof NovoaPersistenceHelper) {
int s = Integer.valueOf(f.getName().substring(
f.getName().indexOf('_') + 1, f.getName().indexOf('r')));
if (s <= 60) {
ins = new IVRPInstance[] {
INSTANCE_READER.readInstance(f, Double
.valueOf(NovoaPersistenceHelper
.getCapacity(s, 1, 0))),
INSTANCE_READER.readInstance(f, Double
.valueOf(NovoaPersistenceHelper
.getCapacity(s, 1, 1))) };
} else {
ins = new IVRPInstance[0];
}
} else {
ins = new IVRPInstance[] { INSTANCE_READER.readInstance(f) };
if (ins[0].getDepot(0).getTimeWindow() != null) {
// Ignore distance constrained instances
ins = new IVRPInstance[0];
}
}
for (IVRPInstance i : ins) {
System.out.printf(" %s (%s clients) Fleet:%s\n", i.getName(),
i.getRequestCount(), i.getFleet());
instances.add(i);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
return instances;
}
private static class RunParams {
private final Strategy strat;
private final boolean reverse;
private final boolean reset;
/**
* Creates a new <code>runParams</code>
*
* @param strat
* @param reverse
* @param reset
*/
public RunParams(Strategy strat, boolean reverse, boolean reset) {
this.strat = strat;
this.reverse = reverse;
this.reset = reset;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("%s-%s", strat.toString().substring(0, 3), reverse ? "Rev"
: reset ? "R" : "NR");
}
}
}