package vroom.optimization.online.jmsa.vrp.vrpsd;
import static vroom.common.heuristics.vls.VLSGlobalParameters.OPTIMIZATION_DIRECTION;
import static vroom.common.heuristics.vls.VLSGlobalParameters.PARAM_INIT;
import static vroom.common.heuristics.vls.VLSGlobalParameters.PARAM_LOCALSEARCH;
import static vroom.common.heuristics.vls.VLSGlobalParameters.PARAM_PERTUBATION;
import java.util.ArrayList;
import vroom.common.heuristics.ConstraintHandler;
import vroom.common.heuristics.GenericNeighborhoodHandler;
import vroom.common.heuristics.Identity;
import vroom.common.heuristics.GenericNeighborhoodHandler.Strategy;
import vroom.common.heuristics.vls.SimpleAcceptanceCriterion;
import vroom.common.heuristics.vls.VLSGlobalParameters;
import vroom.common.heuristics.vls.VLSParameters;
import vroom.common.heuristics.vls.VLSStateBase;
import vroom.common.heuristics.vls.VersatileLocalSearch;
import vroom.common.heuristics.vns.VariableNeighborhoodSearch;
import vroom.common.heuristics.vns.VariableNeighborhoodSearch.VNSVariant;
import vroom.common.heuristics.vrp.OrOptNeighborhood;
import vroom.common.heuristics.vrp.RelocateNeighborhood;
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.util.SolutionChecker;
import vroom.common.utilities.Stopwatch;
import vroom.common.utilities.optimization.INeighborhood;
import vroom.common.utilities.optimization.IParameters.LSStrategy;
import vroom.common.utilities.optimization.OptimizationSense;
import vroom.common.utilities.optimization.SimpleParameters;
import vroom.optimization.online.jmsa.IInstance;
import vroom.optimization.online.jmsa.components.ComponentManager;
import vroom.optimization.online.jmsa.components.ScenarioOptimizerBase;
import vroom.optimization.online.jmsa.components.ScenarioOptimizerParam;
import vroom.optimization.online.jmsa.utils.MSALogging;
import vroom.optimization.online.jmsa.vrp.MSAVRPSolutionFactory;
import vroom.optimization.online.jmsa.vrp.VRPActualRequest;
import vroom.optimization.online.jmsa.vrp.VRPScenario;
import vroom.optimization.online.jmsa.vrp.VRPScenarioGeneratorBase;
import vroom.optimization.online.jmsa.vrp.VRPScenarioRoute;
import vroom.optimization.online.jmsa.vrp.optimization.MSACWInitialization;
import vroom.optimization.online.jmsa.vrp.optimization.MSAFixedNodeConstraint;
import vroom.optimization.online.jmsa.vrp.optimization.VRPScenarioInstanceSmartAdapter;
import vroom.optimization.online.jmsa.vrp.optimization.VRPSmartInitialization;
/**
* <code>VRPSDSVScenarioOptimizer</code> is a optimizer for the VRPSD with a single vehicle based on a VLS procedure.
* <p>
* It uses a Clark and Wright heuristic for initialization and a VNS for the local search.
* <p/>
* <p>
* Creation date: 5-May-2010 9:48:13 a.m.
* <p/>
*
* @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
* @param <S>
* @param <I>
*/
public class VRPSDSVScenarioOptimizer<S extends VRPScenario, I extends IInstance> extends ScenarioOptimizerBase<S> {
private final VersatileLocalSearch<S> mInitVLS;
private final VLSGlobalParameters mInitGlobParams;
private final VLSParameters mInitVLSParams;
private final VariableNeighborhoodSearch<S> mInitVNS;
private final VRPParameters mInitVNSParams;
private final VersatileLocalSearch<S> mOptVLS;
private final VLSGlobalParameters mOptGlobParams;
private final VLSParameters mOptVLSParams;
private final VariableNeighborhoodSearch<S> mOptVNS;
private final VRPParameters mOptVNSParams;
private final ConstraintHandler<S> mConstraintHandler;
private final VRPSDAcceptanceCriterion mAcceptanceCriterion;
public VersatileLocalSearch<S> getOptHeuristic() {
return mOptVLS;
}
public VersatileLocalSearch<S> getInitHeuristic() {
return mInitVLS;
}
/**
* @param componentManager
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public VRPSDSVScenarioOptimizer(ComponentManager<S, I> componentManager) {
super(componentManager);
mAcceptanceCriterion = new VRPSDAcceptanceCriterion(OptimizationSense.MINIMIZATION);
mAcceptanceCriterion.setCostTolerance(0.10);
mAcceptanceCriterion.setLoadThreshold(0.10);
// Constraints
this.mConstraintHandler = new ConstraintHandler<S>();
this.mConstraintHandler.addConstraint(new CapacityConstraint<S>());
this.mConstraintHandler.addConstraint(new MSAFixedNodeConstraint<S>());
// -----------------------------------
// OPTIMIZATION
// -----------------------------------
// Shake
ArrayList<INeighborhood<S, ?>> shakeNeighborhoods = new ArrayList<INeighborhood<S, ?>>();
// Local search
ArrayList<INeighborhood<S, ?>> lsNeighborhoods = new ArrayList<INeighborhood<S, ?>>();
// Swap
lsNeighborhoods.add(new SwapNeighborhood<S>(this.mConstraintHandler));
// Relocate
RelocateNeighborhood<S> reloc = new RelocateNeighborhood<S>(mConstraintHandler, getSolutionFactory());
reloc.setCardinality(1);
// lsNeighborhoods.add(reloc);
// shakeNeighborhoods.add(reloc);
// 2-opt
lsNeighborhoods.add(new TwoOptNeighborhood<S>(this.mConstraintHandler));
// Or-opt
OrOptNeighborhood<S> orOpt = new OrOptNeighborhood<S>(this.mConstraintHandler);
orOpt.setMaxLength(3);
lsNeighborhoods.add(orOpt);
// reloc = new RelocateNeighborhood<S>(mConstraintHandler,
// getSolutionFactory());
// reloc.setCardinality(2);
// shakeNeighborhoods.add(reloc);
// reloc = new RelocateNeighborhood<S>(mConstraintHandler,
// getSolutionFactory());
// reloc.setCardinality(3);
// shakeNeighborhoods.add(reloc);
// reloc = new RelocateNeighborhood<S>(mConstraintHandler,
// getSolutionFactory());
// reloc.setCardinality(4);
// shakeNeighborhoods.add(reloc);
// String exchange
StringExchangeNeighborhood<S> strExc = new StringExchangeNeighborhood<S>(this.mConstraintHandler);
strExc.setMinLength(2);
strExc.setMaxLength(3);
shakeNeighborhoods.add(strExc);
strExc = new StringExchangeNeighborhood<S>(this.mConstraintHandler);
strExc.setMinLength(3);
strExc.setMaxLength(4);
shakeNeighborhoods.add(strExc);
VariableNeighborhoodSearch<S> ls = VariableNeighborhoodSearch.newVNS(VNSVariant.VND,
OptimizationSense.MINIMIZATION, null, getMSAProxy().getOptimizationRandomStream(), lsNeighborhoods);
((GenericNeighborhoodHandler) ls.getNeighHandler()).setStrategy(Strategy.EFFICIENCY_BASED);
// Initialization
// ---------------------------------
this.mInitGlobParams = new VLSGlobalParameters();
this.mInitVLSParams = new VLSParameters(this.mInitGlobParams, 1, 0, 0, 60000);
this.mInitVLSParams.setStoppingCriterion(getComponentManager().getParentMSAProxy().newStoppingCriterion(
mInitVLSParams.getStoppingCriterion(), true));
this.mInitGlobParams.set(VLSGlobalParameters.ENABLE_CALLBACKS, false);
// Initialization parameters
this.mInitVNSParams = new VRPParameters(Integer.MAX_VALUE, Integer.MAX_VALUE, false, false, getMSAProxy()
.getOptimizationRandomStream());
// this.mInitVNSParams.setAcceptanceCriterion(mAcceptanceCriterion);
this.mInitGlobParams.set(OPTIMIZATION_DIRECTION, -1);
this.mInitGlobParams.set(PARAM_INIT, new SimpleParameters(LSStrategy.RND_FIRST_IMPROVEMENT, 10000,
Integer.MAX_VALUE, getMSAProxy().getOptimizationRandomStream()));
this.mInitGlobParams.set(PARAM_LOCALSEARCH, mInitVNSParams);
this.mInitGlobParams.set(PARAM_PERTUBATION, new SimpleParameters(LSStrategy.RND_FIRST_IMPROVEMENT, 100, 6,
getMSAProxy().getOptimizationRandomStream()));
mInitVNS = VariableNeighborhoodSearch.newVNS(VariableNeighborhoodSearch.VNSVariant.GVNS,
OptimizationSense.MINIMIZATION, ls, getMSAProxy().getOptimizationRandomStream(), shakeNeighborhoods);
((GenericNeighborhoodHandler) mInitVNS.getNeighHandler()).setStrategy(Strategy.SEQUENTIAL);
this.mInitVNS.setAcceptanceCriterion(mAcceptanceCriterion);
this.mInitVLS = new VersatileLocalSearch(this.mInitGlobParams, this.mInitVLSParams, VLSStateBase.class,
new SimpleAcceptanceCriterion(this.mInitGlobParams), new MSACWInitialization(getMSAProxy()
.getOptimizationRandomStream()), mInitVNS, new Identity<VRPScenario>(), mConstraintHandler);
this.mInitVLS.setAcceptanceCriterion(mAcceptanceCriterion);
// Optimization
// ---------------------------------
this.mOptGlobParams = new VLSGlobalParameters();
this.mOptVLSParams = new VLSParameters(mOptGlobParams, 3, 0, 0, 60000);
this.mOptVLSParams.setStoppingCriterion(getComponentManager().getParentMSAProxy().newStoppingCriterion(
mOptVLSParams.getStoppingCriterion(), true));
this.mOptGlobParams.set(VLSGlobalParameters.ENABLE_CALLBACKS, false);
mOptVNSParams = new VRPParameters(10000, 10000, false, false, getMSAProxy().getOptimizationRandomStream());
// mOptVNSParams.setAcceptanceCriterion(mAcceptanceCriterion);
this.mOptGlobParams.set(OPTIMIZATION_DIRECTION, -1);
this.mOptGlobParams.set(PARAM_INIT, new SimpleParameters(LSStrategy.RND_FIRST_IMPROVEMENT, 10000,
Integer.MAX_VALUE, getMSAProxy().getOptimizationRandomStream()));
this.mOptGlobParams.set(PARAM_LOCALSEARCH, mOptVNSParams);
this.mOptGlobParams.set(PARAM_PERTUBATION, new SimpleParameters(LSStrategy.RND_FIRST_IMPROVEMENT, 100, 6,
getMSAProxy().getOptimizationRandomStream()));
mOptVNS = VariableNeighborhoodSearch.newVNS(VariableNeighborhoodSearch.VNSVariant.GVNS,
OptimizationSense.MINIMIZATION, ls, getMSAProxy().getOptimizationRandomStream(), shakeNeighborhoods);
mOptVNS.setAcceptanceCriterion(mAcceptanceCriterion);
((GenericNeighborhoodHandler<S>) mOptVNS.getNeighHandler()).setStrategy(Strategy.SEQUENTIAL);
((GenericNeighborhoodHandler<S>) mOptVNS.getNeighHandler()).setResetStrategy(10000, 0.1);
mOptVLS = new VersatileLocalSearch(mOptGlobParams, mOptVLSParams, VLSStateBase.class,
new SimpleAcceptanceCriterion(this.mInitGlobParams), new VRPSmartInitialization(getMSAProxy()
.getOptimizationRandomStream()), mOptVNS, new Identity<S>(), mConstraintHandler);
mOptVLS.setAcceptanceCriterion(mAcceptanceCriterion);
}
@Override
public boolean initialize(S scenario, ScenarioOptimizerParam params) {
scenario.acquireLock();
boolean b = true;
Stopwatch timer = new Stopwatch();
timer.start();
// Ignore empty scenarios
if (scenario.getActualRequests().isEmpty() && scenario.getSampledRequests().isEmpty()) {
VRPScenarioRoute route = new VRPScenarioRoute(scenario, scenario.getParentInstance().getFleet()
.getVehicle(0));
route.appendNode(new VRPActualRequest(scenario.getParentInstance().getDepotsVisits().iterator().next()));
route.appendNode(scenario.getParentInstance().getShrunkRequest(0));
route.appendNode(new VRPActualRequest(scenario.getParentInstance().getDepotsVisits().iterator().next()));
scenario.addRoute(route);
} else {
VersatileLocalSearch<S> solver = getInitHeuristic();
solver.reset();
// Update max time
solver.getParameters().setMaxTime(params.getMaxTimePerScen());
// Update the stopping criterion
solver.getParameters().setStoppingCriterion(
getMSAProxy().newStoppingCriterion(solver.getParameters().getStoppingCriterion(),
params.isInterruptible()));
for (VRPScenarioRoute r : scenario) {
r.setAddAsObserver(false);
}
VRPScenarioInstanceSmartAdapter adapter = new VRPScenarioInstanceSmartAdapter(scenario);
solver.setInstance(adapter);
solver.run();
VRPScenario bestSol = solver.getBestSolution();
b = VRPSDScenarioUpdater.repairSolution(bestSol);
if (b) {
scenario.importScenario(bestSol);
if (scenario.getRouteCount() == 0) {
MSALogging.getComponentsLogger().warn(
"VRPSDSVScenarioOptimizer.initialize: resulting scenario is empty: %s", scenario);
scenario.releaseLock();
return false;
}
} else {
MSALogging.getComponentsLogger().warn(
"VRPSDSVScenarioOptimizer.initialize: resulting scenario is infeasible: %s", scenario);
scenario.releaseLock();
return false;
}
}
// Check coherence
String err = SolutionChecker.checkSolution(scenario, true, true, true);
if (err != null) {
MSALogging.getComponentsLogger().debug(
"VRPSDSVScenarioOptimizer.initialize: resulting scenario (%s) had inconsistencies: %s",
scenario.hashCode(), err);
}
// Check feasibility
String infeas = this.mConstraintHandler.getInfeasibilityExplanation(scenario);
if (infeas != null) {
MSALogging.getComponentsLogger().debug(
"VRPSDSVScenarioOptimizer.initialize: resulting scenario (%s) is infeasible: %s",
scenario.hashCode(), infeas);
b = false;
}
// Automatically monitor request updates
if (b) {
for (VRPScenarioRoute r : scenario) {
r.setAddAsObserver(true);
}
}
timer.stop();
MSALogging.getComponentsLogger().lowDebug(
"VRPSDSVScenarioOptimizer.initialize: New scenario initialized in %sms: %s", timer.readTimeMS(), scenario);
scenario.releaseLock();
return b;
}
@Override
public boolean optimize(S scenario, ScenarioOptimizerParam params) {
// Ignore empty scenarios
if (scenario.getRouteCount() == 1 && scenario.getRoute(0).length() <= 4) {
return true;
}
scenario.acquireLock();
String err1 = SolutionChecker.checkSolution(scenario, true, true, true);
if (err1 != null) {
MSALogging.getComponentsLogger().debug(
"VRPSDSVScenarioOptimizer.optimize: (pre-opt) the scenario (%s) had inconsistencies: %s",
scenario.hashCode(), err1);
}
Stopwatch timer = new Stopwatch();
timer.start();
// mOptVNSParams.setMaxTime(params.getMaxTimePerScen() * 1000l);
// VRPScenario bestSol = mOptVNS.perfomLocalSearch((MSAVRPInstance)
// getComponentManager()
// .getParentMSA().getInstance(), scenario, mOptVNSParams);
VersatileLocalSearch<S> solver = getOptHeuristic();
solver.reset();
// Update max time
solver.getParameters().setMaxTime(params.getMaxTimePerScen());
// Update the stopping criterion
solver.getParameters().setStoppingCriterion(
getMSAProxy().newStoppingCriterion(solver.getParameters().getStoppingCriterion(),
params.isInterruptible()));
VRPScenarioInstanceSmartAdapter adapter = new VRPScenarioInstanceSmartAdapter(scenario.clone());
for (VRPScenarioRoute r : scenario) {
r.setAddAsObserver(false);
}
solver.setInstance(adapter);
solver.run();
S bestSol = solver.getBestSolution();
// Check coherence
boolean feasible = true;
// boolean improvement = bestSol.getCost() < scenario.getCost();
boolean improvement = mAcceptanceCriterion.accept(scenario, bestSol);
if (improvement) {
feasible = VRPSDScenarioUpdater.repairSolution(bestSol);
if (!feasible) {
MSALogging.getComponentsLogger().warn(
"VRPSDSVScenarioOptimizer.optimize: resulting scenario is infeasible: %s", bestSol);
bestSol.releaseLock();
return VRPSDScenarioUpdater.repairSolution(scenario);
}
// Check coherence
String err = SolutionChecker.checkSolution(bestSol, true, true, true);
if (err != null) {
MSALogging.getComponentsLogger().debug(
"VRPSDSVScenarioOptimizer.optimize: resulting scenario (%s) had inconsistencies: %s",
bestSol.hashCode(), err);
if (SolutionChecker.checkUnservedCustomers(bestSol) != null) {
feasible = false;
return VRPSDScenarioUpdater.repairSolution(scenario);
}
}
// Check feasibility
String infeas = this.mConstraintHandler.getInfeasibilityExplanation(bestSol);
if (infeas != null) {
MSALogging.getComponentsLogger().debug(
"VRPSDSVScenarioOptimizer.optimize: resulting scenario (%s) is infeasible: %s (%s)",
bestSol.hashCode(), infeas, bestSol);
feasible = false;
return VRPSDScenarioUpdater.repairSolution(scenario);
}
// Automatically monitor request updates
if (feasible && improvement) {
scenario.importScenario(bestSol);
scenario.resetNonImprovingCount();
for (VRPScenarioRoute r : scenario) {
r.setAddAsObserver(true);
}
} else {
scenario.incrementNonImprovingCount();
}
} else {
scenario.incrementNonImprovingCount();
}
timer.stop();
MSALogging.getComponentsLogger().lowDebug("VRPSDSVScenarioOptimizer.optimize: Scenario optimized in %sms %s",
timer.readTimeMS(), scenario);
scenario.releaseLock();
return true;
}
@Override
protected void finalize() throws Throwable {
this.mInitVLS.destroy();
super.finalize();
}
@Override
public String toString() {
return String.format("%s \nInit (%s):%s\nOpt (%s): %s", this.getClass().getSimpleName(), mInitVLSParams,
mInitVLS, mOptVNSParams, mOptVNS);
}
protected MSAVRPSolutionFactory getSolutionFactory() {
return ((VRPScenarioGeneratorBase<?>) getComponentManager().getScenarioGenerator()).getScenarioFactory();
}
}// end VRPSDSVScenarioOptimizer