package vroom.common.heuristics.vls;
import static vroom.common.heuristics.vls.VLSGlobalParameters.ACCEPTANCE_CRITERIA_CLASS;
import static vroom.common.heuristics.vls.VLSGlobalParameters.INITIALIZATION_CLASS;
import static vroom.common.heuristics.vls.VLSGlobalParameters.LOCAL_SEARCH_CLASS;
import static vroom.common.heuristics.vls.VLSGlobalParameters.PERTUBATION_CLASS;
import static vroom.common.heuristics.vls.VLSGlobalParameters.STATE_CLASS;
import static vroom.common.heuristics.vls.VLSPhase.ELS;
import static vroom.common.heuristics.vls.VLSPhase.GRASP;
import static vroom.common.heuristics.vls.VLSPhase.ILS;
import java.util.Collection;
import vroom.common.heuristics.ConstraintHandler;
import vroom.common.heuristics.IInitialization;
import vroom.common.utilities.Stopwatch;
import vroom.common.utilities.Utilities;
import vroom.common.utilities.callbacks.CallbackEventBase;
import vroom.common.utilities.callbacks.CallbackManagerDelegate;
import vroom.common.utilities.callbacks.ICallback;
import vroom.common.utilities.optimization.IInstance;
import vroom.common.utilities.optimization.ILocalSearch;
import vroom.common.utilities.optimization.IParameters;
import vroom.common.utilities.optimization.ISolution;
import vroom.common.utilities.params.ParameterKey;
import vroom.common.utilities.ssj.RandomSourceBase;
/**
* The Class <code>VersatileLocalSearch</code> is a generic implementation of a GRASPx[ILS,ELS] procedure.
* <p>
* Creation date: Apr 26, 2010 - 10:11:57 a.m.
*
* @param <S>
* the generic type
* @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 VersatileLocalSearch<S extends ISolution> extends RandomSourceBase implements
Runnable, IInitialization<S> {
/** The current state. */
private final IVLSState<S> mState;
/** The acceptance criteria. */
private IVLSAcceptanceCriterion mAcceptanceCriterion;
/**
* Getter for <code>AcceptanceCriterion</code>.
*
* @return the AcceptanceCriterion
*/
public IVLSAcceptanceCriterion getAcceptanceCriterion() {
return mAcceptanceCriterion;
}
/**
* Setter for <code>AcceptanceCriterion</code>.
*
* @param acceptanceCriterion
* the new acceptance criterion
*/
public void setAcceptanceCriterion(IVLSAcceptanceCriterion acceptanceCriterion) {
mAcceptanceCriterion = acceptanceCriterion;
}
/** The initialization. */
private final IInitialization<S> mInitialization;
/** The local search. */
private final ILocalSearch<S> mLocalSearch;
/** The perturbation. */
private final IVLSPertubation<S> mPerturbation;
/** The constraint handler used in this VLS procedure *. */
private final ConstraintHandler<S> mConstraintHandler;
/**
* Getter for the constraint handler.
*
* @return The constraint handler used in this VLS procedure
*/
public ConstraintHandler<S> getConstraintHandler() {
return this.mConstraintHandler;
}
/** The parameters for this VLS procedure. */
private final VLSGlobalParameters mGlobalParameters;
/**
* Getter for the global parameters used in this procedure.
*
* @return the global parameters used in this procedure
*/
public final VLSGlobalParameters getGlobalParameters() {
return mGlobalParameters;
}
/** The parameters used in the VLS *. */
private final VLSParameters mParameters;
/**
* Getter for the VLS parameters : The parameters used in the VLS.
*
* @return the value of the VLS parameters
*/
public VLSParameters getParameters() {
return this.mParameters;
}
/** A flag to determine the running state of the procedure. */
private boolean mRunning;
/**
* Getter for <code>running</code> flag.
*
* @return <code>true</code> if the heuristic is currently running
*/
public boolean isRunning() {
return mRunning;
}
/** The instance on which the heuristic will be run. */
private IInstance mInstance;
/** A callback manager. */
private final CallbackManagerDelegate<VersatileLocalSearch<S>, VLSCallbackEvents> mCallbackDelegate;
/** The Timer. */
private final Stopwatch mTimer;
/**
* Instantiates a new versatile local search.
*
* @param parameters
* the global parameters from which this instance will be initialized
*/
public VersatileLocalSearch(VLSGlobalParameters parameters) {
mGlobalParameters = parameters;
mConstraintHandler = new ConstraintHandler<S>();
Collection<ParameterKey<?>> missingParams = mGlobalParameters.checkRequiredParameters();
if (!missingParams.isEmpty()) {
throw new IllegalStateException("Some required parameters are missing: "
+ missingParams.toString());
}
mState = getGlobalParameters().newInstance(STATE_CLASS, this);
mState.reset();
setAcceptanceCriterion((IVLSAcceptanceCriterion) getGlobalParameters().newInstance(
ACCEPTANCE_CRITERIA_CLASS, getGlobalParameters()));
mInitialization = getGlobalParameters().newInstance(INITIALIZATION_CLASS,
getGlobalParameters());
mLocalSearch = getGlobalParameters().newInstance(LOCAL_SEARCH_CLASS, getGlobalParameters());
mPerturbation = getGlobalParameters().newInstance(PERTUBATION_CLASS, getGlobalParameters());
if (parameters.get(VLSGlobalParameters.ENABLE_CALLBACKS)) {
mCallbackDelegate = new CallbackManagerDelegate<VersatileLocalSearch<S>, VLSCallbackEvents>(
VLSCallbackEvents.class, "vls");
} else {
mCallbackDelegate = null;
}
mParameters = new VLSParameters(parameters);
mTimer = new Stopwatch();
}
/**
* Creates a new <code>VersatileLocalSearch</code> with the given components.
*
* @param parameters
* the global parameters for this instance
* @param params
* the params
* @param stateClass
* the class used to describe the state of the procedure
* @param AcceptanceCriterion
* the acceptance criteria that will be used in this procedure
* @param initialization
* the instance of {@link IInitialization} responsible for the initialization of new solutions
* @param localSearch
* the instance of {@link ILocalSearch} that will perform local searches on solutions
* @param perturbation
* the instance of {@link IVLSPertubation} that will be used to introduce variability in solutions
* @param ctrHandler
* the {@link ConstraintHandler} to be used in this procedure
*/
@SuppressWarnings("unchecked")
public VersatileLocalSearch(VLSGlobalParameters parameters, VLSParameters params,
@SuppressWarnings("rawtypes") Class<? extends IVLSState> stateClass,
IVLSAcceptanceCriterion AcceptanceCriterion, IInitialization<S> initialization,
ILocalSearch<S> localSearch, IVLSPertubation<S> perturbation,
ConstraintHandler<S> ctrHandler) {
mGlobalParameters = parameters;
if (ctrHandler == null) {
ctrHandler = new ConstraintHandler<S>();
}
mConstraintHandler = ctrHandler;
mParameters = params;
mGlobalParameters.set(VLSGlobalParameters.NS, params.getNS());
mGlobalParameters.set(VLSGlobalParameters.NC, params.getNC());
mGlobalParameters.set(VLSGlobalParameters.NI, params.getNI());
mGlobalParameters.set(VLSGlobalParameters.VLS_MAX_TIME, params.getMaxTime());
mState = Utilities.newInstance(stateClass, this);
mState.reset();
setAcceptanceCriterion(AcceptanceCriterion);
mInitialization = initialization;
mLocalSearch = localSearch;
mPerturbation = perturbation;
if (parameters.get(VLSGlobalParameters.ENABLE_CALLBACKS)) {
mCallbackDelegate = new CallbackManagerDelegate<VersatileLocalSearch<S>, VLSCallbackEvents>(
VLSCallbackEvents.class, "vls");
} else {
mCallbackDelegate = null;
}
mTimer = new Stopwatch();
}
/**
* Sets the instance on which the heuristic will be run.
*
* @param instance
* the new instance
*/
public void setInstance(IInstance instance) {
if (isRunning()) {
throw new IllegalStateException(
"Cannot set the instance while the heuristic is running");
}
mInstance = instance;
mState.reset();
}
/**
* Getter for the <code>instance</code>.
*
* @return the instance on which the heuristic will be run
*/
public IInstance getInstance() {
return mInstance;
}
/**
* Gets the best mSolution.
*
* @return the best mSolution
*/
public S getBestSolution() {
return getState().getOverallBestSolution();
}
/**
* Gets the current state.
*
* @return the current state
*/
public IVLSState<S> getState() {
return mState;
}
/*
* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
if (getInstance() == null) {
throw new IllegalStateException("The working instance has not been set");
}
mRunning = true;
newSolution(null, getInstance(), getParameters());
VLSLogging.getProcedureLogger().info(
"VLS procedure terminated after %sms, best mSolution: %s", mTimer.readTimeMS(),
getBestSolution());
stop();
}
/**
* Performs an iteration of the grasp loop.
*
* @param graspCount
* this iteration of the grasp loop
*/
protected void graspIteration(int graspCount) {
VLSLogging.getProcedureLogger().debug("GRASP loop iteration #%s", graspCount);
getState().setCurrentPhase(GRASP);
Stopwatch detail = new Stopwatch();
Stopwatch timeGRASP = new Stopwatch();
timeGRASP.start();
detail.start();
// TODO add timer & callback
S sol = mInitialization.newSolution(getState(), getInstance(), getParameters()
.getInitParams());
VLSLogging.getProcedureLogger().debug("GRASP #%s> initialization (%sms)\t - %s",
graspCount, detail.readTimeMS(), sol);
detail.restart();
// TODO add timer & callback
// TODO add pre-LS action? (eg Split)
// TODO add timer & callback
sol = mLocalSearch.localSearch(getInstance(), sol, getParameters().getLSParameters());
VLSLogging.getProcedureLogger().debug("GRASP #%s> local search (%sms)\t - %s",
graspCount, detail.readTimeMS(), sol);
// TODO add timer & callback
// TODO add post LS action? (eg Concat)
// First iteration, initializing the best GRASP mSolution
if (graspCount == 0) {
getState().solutionAccepted(sol, GRASP);
}
// ILS LOOP
int ilsCount = 0;
// Initializing the best ILS mSolution
getState().solutionAccepted(sol, ILS);
while (ilsCount < getParameters().getNI()) {
if (!isRunning()) {
break;
}
ilsIteration(graspCount, ilsCount, getState().getBestSolution(ILS));
ilsCount++;
}
getState().setCurrentPhase(GRASP);
if (getAcceptanceCriterion().acceptSolution(getState(), getInstance(),
getState().getBestSolution(ILS))) {
// ILS Solution accepted
getState().solutionAccepted(getState().getBestSolution(ILS), GRASP);
VLSLogging.getProcedureLogger().info("GRAPS #%s> New best mSolution: %s", graspCount,
getState().getBestSolution(ILS));
callbacks(VLSCallbackEvents.SOLUTION_ACCEPTED, getState().getBestSolution(ILS), GRASP,
getState());
} else {
// ILS Solution rejected
getState().solutionRejected(getState().getBestSolution(ILS), GRASP);
VLSLogging.getProcedureLogger().debug("GRAPS #%s> Solution rejected %s", graspCount,
getState().getBestSolution(ILS));
callbacks(VLSCallbackEvents.SOLUTION_REJECTED, getState().getBestSolution(ILS), GRASP,
getState());
}
// Reset the ILS best mSolution for next iteration
getState().resetBestSolution(ILS);
timeGRASP.stop();
VLSLogging.getProcedureLogger().debug("GRAPS loop iteration #%s finished after %sms",
graspCount, timeGRASP.readTimeMS());
}
/**
* Perform an iteration of the ils loop.
*
* @param graspCount
* the current grasp iteration
* @param ilsCount
* this ils iteration
* @param sol
* the initial mSolution of the grasp
*/
protected void ilsIteration(int graspCount, int ilsCount, S sol) {
VLSLogging.getProcedureLogger().debug("> ILS loop iteration #%s-%s", graspCount, ilsCount);
mState.setCurrentPhase(ILS);
Stopwatch timILS = new Stopwatch();
timILS.start();
sol.acquireLock();
// ELS LOOP
int elsCount = 0;
while (elsCount < getParameters().getNC()) {
if (!isRunning()) {
break;
}
elsIteration(graspCount, ilsCount, elsCount, sol);
elsCount++;
}
getState().setCurrentPhase(ILS);
if (getAcceptanceCriterion().acceptSolution(getState(), getInstance(),
getState().getBestSolution(ELS))) {
// ELS mSolution accepted
getState().solutionAccepted(getState().getBestSolution(ELS), ILS);
VLSLogging.getProcedureLogger().debug("> ILS #%s-%s> Solution accepted %s", graspCount,
ilsCount, getState().getBestSolution(ELS));
callbacks(VLSCallbackEvents.SOLUTION_ACCEPTED, getState().getBestSolution(ELS), ILS,
getState());
} else {
// ELS mSolution rejected
getState().solutionRejected(getState().getBestSolution(ELS), ILS);
VLSLogging.getProcedureLogger().lowDebug("> ILS #%s-%s> Solution rejected %s",
graspCount, ilsCount, getState().getBestSolution(ELS));
callbacks(VLSCallbackEvents.SOLUTION_REJECTED, getState().getBestSolution(ELS), ILS,
getState());
}
// Reset the ELS best mSolution for next iteration
getState().resetBestSolution(ELS);
sol.releaseLock();
timILS.stop();
VLSLogging.getProcedureLogger().debug("> ILS loop iteration #%s-%s finished after %sms",
graspCount, ilsCount, timILS.readTimeMS());
}
/**
* Performs an iteration of els loop.
*
* @param graspCount
* the current grasp iteration
* @param ilsCount
* the current ils iteration
* @param elsCount
* this els iteration
* @param sol
* the initial mSolution generated by the grasp initialization
*/
@SuppressWarnings("unchecked")
protected void elsIteration(int graspCount, int ilsCount, int elsCount, S sol) {
VLSLogging.getProcedureLogger().debug(">> ELS loop iteration #%s-%s-%s", graspCount,
ilsCount, elsCount);
getState().setCurrentPhase(ELS);
Stopwatch timELS = new Stopwatch();
timELS.start();
Stopwatch detail = new Stopwatch();
detail.start();
sol.acquireLock();
// Perturbation (mutation)
// TODO add timer & callback
S solTmp = (S) sol.clone();
solTmp.acquireLock();
mPerturbation.pertub(getState(), getInstance(), solTmp, getParameters()
.getPertubParameters());
VLSLogging.getProcedureLogger().lowDebug(">> ELS #%s-%s-%s> Perturbation (%sms)\t - %s",
graspCount, ilsCount, elsCount, detail.readTimeMS(), solTmp);
// TODO add timer & callback
// Local search
// TODO add timer & callback
detail.restart();
solTmp = mLocalSearch.localSearch(getInstance(), solTmp, getParameters().getLSParameters());
VLSLogging.getProcedureLogger().lowDebug(">> ELS #%s-%s-%s> Local search (%sms)\t - %s",
graspCount, ilsCount, elsCount, detail.readTimeMS(), solTmp);
// TODO add timer & callback
if (getAcceptanceCriterion().acceptSolution(getState(), getInstance(), solTmp)) {
// Solution accepted
getState().solutionAccepted(solTmp, ELS);
VLSLogging.getProcedureLogger().debug(">> ELS #%s-%s-%s> Solution accepted %s",
graspCount, ilsCount, elsCount, solTmp);
callbacks(VLSCallbackEvents.SOLUTION_ACCEPTED, solTmp, ELS, getState());
} else {
// Solution rejected
getState().solutionRejected(solTmp, ELS);
VLSLogging.getProcedureLogger().lowDebug(">> ELS #%s-%s-%s> Solution rejected %s",
graspCount, ilsCount, elsCount, solTmp);
callbacks(VLSCallbackEvents.SOLUTION_REJECTED, solTmp, ELS, getState());
}
solTmp.releaseLock();
sol.releaseLock();
timELS.stop();
VLSLogging.getProcedureLogger().debug(
">> ELS loop iteration #%s-%s-%s finished after %sms", graspCount, ilsCount,
elsCount, timELS.readTimeMS());
}
/**
* Reset the heuristic to prevent collisions between runs.
*/
public void reset() {
stop();
getParameters().getStoppingCriterion().reset();
mState.reset();
mTimer.reset();
setInstance(null);
}
/**
* Causes the heuristic to stop at the end of the current loop iteration.
*/
public void stop() {
mRunning = false;
if (mTimer.isStarted() && !mTimer.isStopped())
mTimer.stop();
getParameters().getStoppingCriterion().reset();
}
/*
* (non-Javadoc)
* @see java.lang.Object#finalize()
*/
@Override
protected void finalize() throws Throwable {
destroy();
super.finalize();
}
/**
* Association of a callback to a specific event.
*
* @param eventType
* the event that will cause the execution of <code>callback</code>
* @param callback
* the callback object that will be associated with <code>event</code>
*/
public void registerCallback(VLSCallbackEvents eventType,
ICallback<VersatileLocalSearch<S>, VLSCallbackEvents> callback) {
if (mCallbackDelegate != null) {
this.mCallbackDelegate.registerCallback(callback, eventType);
} else {
throw new IllegalStateException(
"Callbacks are disabled, set ENABLE_CALLBACKS to true in VLSGlobalParameters");
}
}
/**
* Execute the callbacks associated with <code>event</code>.
*
* @param eventType
* the event type that has occurred and for which the associated callbacks will be run
* @param params
* an optional parameter that will be transmitted to the callback
*/
protected void callbacks(VLSCallbackEvents eventType, Object... params) {
if (mCallbackDelegate != null) {
this.mCallbackDelegate
.callbacks(new CallbackEventBase<VersatileLocalSearch<S>, VLSCallbackEvents>(
eventType, this, params));
}
}
/**
* Will stop all child threads.
*/
public void destroy() {
if (mCallbackDelegate != null) {
mCallbackDelegate.stop();
}
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("%s (%s)\n Init:%s\n Pertub:%s\n LS:%s", this.getClass()
.getSimpleName(), mParameters, mInitialization, mPerturbation, mLocalSearch);
}
@Override
public S newSolution(IVLSState<S> state, IInstance instance, IParameters params) {
mTimer.setTimout(getParameters().getMaxTime());
getParameters().getStoppingCriterion().reset();
VLSLogging.getProcedureLogger().info("VLS procedure started (%s)", getParameters());
getParameters().getStoppingCriterion().init();
// GRASP LOOP
int graspCount = 0;
mTimer.start();
while (graspCount < getParameters().getNS() && !mTimer.hasTimedOut()) {
if (!isRunning()) {
break;
}
graspIteration(graspCount);
graspCount++;
getParameters().getStoppingCriterion().update(getState());
if (getParameters().getStoppingCriterion().isStopCriterionMet()) {
VLSLogging.getProcedureLogger().info("Stopping conditions are met: %s",
getParameters().getStoppingCriterion());
stop();
}
}
return getBestSolution();
}
}// end VersatilLocalSearch