/**
*
*/
package vroom.common.heuristics.alns;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import umontreal.iro.lecuyer.rng.RandomStream;
import vroom.common.heuristics.alns.IDestroy.IDestroyResult;
import vroom.common.utilities.BatchThreadPoolExecutor;
import vroom.common.utilities.BatchThreadPoolExecutor.NameThreadFactory;
import vroom.common.utilities.Stopwatch;
import vroom.common.utilities.logging.Logging;
import vroom.common.utilities.optimization.IComponentHandler;
import vroom.common.utilities.optimization.IComponentHandler.Outcome;
import vroom.common.utilities.optimization.IInstance;
import vroom.common.utilities.optimization.IParameters;
import vroom.common.utilities.optimization.ISolution;
import vroom.common.utilities.optimization.OptimizationSense;
/**
* <code>ParallelALNSOld</code> is a deprecated implementation of {@link ParallelALNS}
* <p>
* In particular it contains a <em>pull</em> scheme that was abandoned as it introduces more waiting times between
* threads
* </p>
* <p>
* Creation date: Nov 17, 2011 - 3:37:35 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
* @deprecated Use {@link ParallelALNS} instead
* @see ParallelALNS
*/
@Deprecated
public class ParallelALNSOld<S extends ISolution> extends AdaptiveLargeNeighborhoodSearch<S> {
/**
* {@code true} if the subprocesses push their results to a share queue (push scheme), {@code false} if the main
* thread collects the results when all subprocesses have finished (pull scheme)
* <p>
* 21/11/2011 The push scheme was introduced to improve memory management and prevent the storage of a great number
* of temporary solutions
* </p>
*/
protected static boolean sPushScheme = true;
/** The solution pool */
private IPALNSSolutionPool<S> mSolPool;
/** The executor used to execute subprocesses */
private BatchThreadPoolExecutor mExecutor;
/** The number of iterations to be executed in parallel (cached for performance */
private int mItP;
private BlockingQueue<PALNSItResult> mResultsQueue;
/**
* Sets the solution pool
*
* @param solPool
* the solution pool to be set
*/
public void setSolPool(IPALNSSolutionPool<S> solPool) {
if (solPool == null)
throw new NullPointerException();
mSolPool = solPool;
}
/**
* Returns the solution pool
*
* @return the solution pool
*/
public IPALNSSolutionPool<S> getSolPool() {
return mSolPool;
}
/**
* Creates a new <code>ParallelALNS</code>
*
* @param optimizationSense
* @param rndStream
* @param params
* @param destroyComponents
* @param repairComponents
*/
public ParallelALNSOld(OptimizationSense optimizationSense, RandomStream rndStream, ALNSGlobalParameters params,
IComponentHandler<IDestroy<S>> destroyComponents, IComponentHandler<IRepair<S>> repairComponents) {
super(optimizationSense, rndStream, params, destroyComponents, repairComponents);
mSolPool = getGlobalParameters().newInstance(ALNSGlobalParameters.PALNS_POOL, getOptimizationSense(), params);
}
@Override
void initialize(IInstance instance, S solution, IParameters params) {
if (getCurrentInstance() != null && getCurrentInstance() != instance)
mSolPool.clear();
super.initialize(instance, solution, params);
if (mExecutor != null)
mExecutor.shutdownNow();
mExecutor = new BatchThreadPoolExecutor(getGlobalParameters().get(ALNSGlobalParameters.PALNS_THREAD_COUNT),
new PALNSThreadFactory("pALNS"));
mItP = getGlobalParameters().get(ALNSGlobalParameters.PALNS_IT_P);
// mResultsQueue = new SynchronousQueue<ParallelALNS<S>.PALNSItResult>();
mResultsQueue = new ArrayBlockingQueue<ParallelALNSOld<S>.PALNSItResult>(getGlobalParameters().get(
ALNSGlobalParameters.PALNS_THREAD_COUNT)
* getGlobalParameters().get(ALNSGlobalParameters.PALNS_IT_P));
};
@Override
public S localSearch(IInstance instance, S solution, IParameters params) {
if (params.getAcceptanceCriterion() != null)
setAcceptanceCriterion(params.getAcceptanceCriterion());
initialize(instance, solution, params);
mSolPool.add(solution, true);
getProgress().start();
while (!getStoppingCriterion().isStopCriterionMet()) {
// Select a subset of solutions
Collection<S> solSubset = mSolPool.subset(mExecutor.getMaximumPoolSize(), getRandomStream());
// Collection<S> solSubset = mSolPool.subset(Math.max(1, mExecutor.getMaximumPoolSize() - 1),
// getRandomStream());
getLogger().debug("ALNS %s: New iteration batch, stopping criterion: %s, solution pool size:%s",
getProgress(), getStoppingCriterion(), mSolPool.size());
// Parallel forall
// - Batch of subprocesses
ArrayList<PALNSSubprocess> batch = new ArrayList<PALNSSubprocess>(solSubset.size());
// Add the current best solution
// if (mExecutor.getMaximumPoolSize() > 1)
// batch.add(new PALNSSubprocess(mSolPool.getBest(), params));
while (batch.size() < mExecutor.getMaximumPoolSize())
for (S sol : solSubset) {
batch.add(new PALNSSubprocess(sol, params));
if (batch.size() == mExecutor.getMaximumPoolSize())
break;
}
// - Execute the subprocesses
Map<PALNSSubprocess, Future<List<PALNSItResult>>> results = null;
try {
results = mExecutor.submitBatch(batch, false);
} catch (InterruptedException e) {
getLogger().fatalException("ParallelALNS.perfomLocalSearch", e);
Logging.awaitLogging(5000);
System.exit(1);
}
// - Synchronize with the results
if (sPushScheme)
synchronizePush();
else
synchronizePull(results);
for (Entry<PALNSSubprocess, Future<List<PALNSItResult>>> r : results.entrySet()) {
try {
r.getValue().get();
} catch (Exception e) {
throw new IllegalStateException("pALNS subprocess terminated abnormally", e);
}
getSolPool().add(r.getKey().mSolution, true);
}
// Update the number of iterations
getProgress().iterationsFinished(mItP * solSubset.size());
// Update the global stopping criterion
getStoppingCriterion().update(mItP * solSubset.size(), new Object[0]);
}
getProgress().stop();
setStopped();
getCallbacks().callbacks(
new ALNSCallbackEvent<S>(ALNSEventType.FINISHED, this, getTimer().readTimeMS(), getProgress()
.getIteration(), instance, mSolPool.getBest()));
mExecutor.shutdownNow();
return mSolPool.getBest();
}
/**
* Synchronization between {@link PALNSSubprocess subprocesses} using the push scheme
*
* @see #sPushScheme
*/
protected void synchronizePush() {
while (!mExecutor.isBatchComplete() || !mResultsQueue.isEmpty()) {
try {
PALNSItResult result = mResultsQueue.poll(10, TimeUnit.MILLISECONDS);
if (result != null)
synchronize(result);
} catch (InterruptedException e) {
getLogger().exception("ParallelALNS.synchronize", e);
}
}
}
/**
* This method collect the results of various {@link PALNSSubprocess subprocesses} and updates all shared data with
* the pull scheme
*
* @param results
* the results of the subprocesses
* @see #sPushScheme
*/
protected void synchronizePull(Map<PALNSSubprocess, Future<List<PALNSItResult>>> results) {
ArrayList<Future<List<PALNSItResult>>> futures = new ArrayList<Future<List<PALNSItResult>>>(results.values());
boolean completed = false;
while (!completed) {
completed = true;
for (int i = 0; i < futures.size(); i++) {
Future<List<PALNSItResult>> future = futures.get(i);
if (future == null)
continue;
else if (!future.isDone()) {
completed = false;
continue;
}
futures.set(i, null);
List<PALNSItResult> itResultList = null;
try {
itResultList = future.get();
} catch (InterruptedException e) {
getLogger().exception("ParallelALNS.synchronize", e);
continue;
} catch (ExecutionException e) {
getLogger().exception("ParallelALNS.synchronize", e);
continue;
}
for (PALNSItResult result : itResultList) {
synchronize(result);
}
}
}
}
/**
* Synchronize shared data for a single result
*
* @param result
*/
protected void synchronize(PALNSItResult result) {
Outcome state = result.mAccepted ? Outcome.ACCEPTED : Outcome.REJECTED;
// Add the solution to the pool
boolean added = mSolPool.add(result.mTempSol, false);
if (added)
state = Outcome.NEW_BEST;
// Update destroy stats
getDestroyComponents().updateStats(result.mDestroy,
getAcceptanceCriterion().getImprovement(result.mCurrentSol, result.mTempSol), result.mTime,
result.mIteration, state);
// Update repair stats
getRepairComponents().updateStats(result.mRepair,
getAcceptanceCriterion().getImprovement(result.mCurrentSol, result.mTempSol), result.mTime,
result.mIteration, state);
// Execute callbacks
getCallbacks().callbacks(
new ALNSCallbackEvent<S>(ALNSEventType.REPAIRED, this, getTimer().readTimeMS(), getProgress()
.getIteration(), mSolPool.getBest(), result.mCurrentSol, result.mTempSol, result.mRepair,
result.mRepaired));
S newCurrent = result.mCurrentSol;
switch (state) {
case NEW_BEST:
getCallbacks().callbacks(
new ALNSCallbackEvent<S>(ALNSEventType.SOL_NEW_BEST, this, getTimer().readTimeMS(), getProgress()
.getIteration(), result.mTempSol));
newCurrent = result.mTempSol;
break;
case ACCEPTED:
getCallbacks().callbacks(
new ALNSCallbackEvent<S>(ALNSEventType.SOL_NEW_CURRENT, this, getTimer().readTimeMS(),
getProgress().getIteration(), mSolPool.getBest(), result.mTempSol));
newCurrent = result.mTempSol;
break;
case REJECTED:
getCallbacks().callbacks(
new ALNSCallbackEvent<S>(ALNSEventType.SOL_REJECTED, this, getTimer().readTimeMS(), getProgress()
.getIteration(), result.mTempSol));
break;
default:
break;
}
getCallbacks().callbacks(
new ALNSCallbackEvent<S>(ALNSEventType.IT_FINISHED, this, getTimer().readTimeMS(), getProgress()
.getIteration(), mSolPool.getBest(), newCurrent, result.mTempSol, getMainTimer()
.getReadOnlyStopwatch()));
}
/**
* <code>PALNSSubprocess</code> is the class responsible to execute a subprocess of the parallel ALNS.
* <p>
* Creation date: Nov 17, 2011 - 5:16:43 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
*/
protected class PALNSSubprocess implements Callable<List<PALNSItResult>> {
/** The current solution */
private S mSolution;
/** Parameters */
private final IParameters mParams;
/**
* The local copy of the acceptance criterion
*/
// private final IAcceptanceCriterion mAccept;
/**
* Creates a new <code>PALNSSubprocess</code>
*
* @param solution
* the starting solution
* @param params
* parameters for the ALNS
*/
public PALNSSubprocess(S solution, IParameters params) {
mSolution = solution;
mParams = params;
}
@Override
public List<PALNSItResult> call() {
@SuppressWarnings("unchecked")
PALNSThread thread = (PALNSThread) Thread.currentThread();
Stopwatch itTimer = new Stopwatch();
ArrayList<PALNSItResult> subProcessResults = sPushScheme ? null : new ArrayList<PALNSItResult>(mItP);
for (int it = 1; it <= mItP; it++) {
itTimer.reset();
itTimer.start();
@SuppressWarnings("unchecked")
S tmp = (S) mSolution.clone();
// Select destroy operator
IDestroy<S> destroy = getDestroyComponents().nextComponent();
IDestroy<S> destroyClone = thread.getClone(destroy);
double sizeMin = getGlobalParameters().get(ALNSGlobalParameters.DESTROY_SIZE_RANGE)[0];
double sizeMax = getGlobalParameters().get(ALNSGlobalParameters.DESTROY_SIZE_RANGE)[1];
double size = sizeMin + mParams.getRandomStream().nextDouble() * (sizeMax - sizeMin);
// getLogger().lowDebug("ALNS %s: Selecting destroy %s (size=%.2f)", getProgress(), destroy, size);
// Destroy solution
IDestroyResult<S> result = destroyClone.destroy(tmp, mParams, size);
getLogger().lowDebug("ALNS %s: Destroy result: %s ", getProgress(), result);
// Select repair operator
IRepair<S> repair = getRepairComponents().nextComponent();
IRepair<S> repairClone = thread.getClone(repair);
// getLogger().lowDebug("ALNS %s: Selecting repair %s", getProgress(), repair);
// Repair solution
boolean repaired = repairClone.repair(tmp, result, mParams);
if (isCheckSolutionAfterMove()) {
String err = checkSolution(tmp);
if (!err.isEmpty()) {
getLogger().warn("ALNS %s: Infeasible temporary solution: %s", getProgress(), err);
}
}
itTimer.stop();
// FIXME Apply an optional post-repair local search
// Test the solution and update the acceptance criterion
// boolean accepted = mAccept.accept(mSolution, tmp);
boolean accepted = ParallelALNSOld.this.getAcceptanceCriterion().accept(mSolution, tmp);
// Add the results of this iteration
PALNSItResult alnsResult = new PALNSItResult(mSolution, tmp, destroy, repair, repaired, accepted,
itTimer.readTimeMS(), getProgress().getIteration() + it);
if (sPushScheme)
try {
boolean pushed = ParallelALNSOld.this.mResultsQueue.offer(alnsResult, 60, TimeUnit.SECONDS);
if (!pushed)
getLogger().error("PALNSSubprocess.call: unable to push result %s", alnsResult);
} catch (InterruptedException e) {
getLogger().exception("PALNSSubprocess.call", e);
}
else
subProcessResults.add(alnsResult);
if (accepted) {
// The new solution is accepted as current solution
mSolution = tmp;
getLogger().debug("ALNS %s: New current [%.2f] (d:%s,r:%s)", getProgress(),
mSolution.getObjectiveValue(), destroy, repair);
}
}
return subProcessResults;
}
}
/**
* <code>PALNSItResult</code> contains the information required to update the operators scores when subprocesses
* finish
* <p>
* Creation date: Nov 17, 2011 - 5:08:25 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
*/
protected class PALNSItResult {
protected final S mCurrentSol;
protected final S mTempSol;
protected final IDestroy<S> mDestroy;
protected final IRepair<S> mRepair;
protected final boolean mRepaired;
protected final boolean mAccepted;
protected final double mTime;
protected final int mIteration;
/**
* Creates a new <code>PALNSItResult</code>
*
* @param currentSol
* the current solution
* @param tempSol
* the temporary solution (at this iteration)
* @param destroy
* the destroy operator used
* @param repair
* the repair operator used
* @param repaired
* {@code true} if the solution was successfully repaired
* @param accepted
* {@code true} if the solution was accepted as current solution
* @param time
* the current time
* @param iteration
* the current iteration
*/
public PALNSItResult(S currentSol, S tempSol, IDestroy<S> destroy, IRepair<S> repair, boolean repaired,
boolean accepted, double time, int iteration) {
mCurrentSol = currentSol;
mTempSol = tempSol;
mDestroy = destroy;
mRepair = repair;
mRepaired = repaired;
mAccepted = accepted;
mTime = time;
mIteration = iteration;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("d:%s,r:%s,a:%s,t:%s,it:%s]", mDestroy, mRepair, mAccepted, mTime, mIteration);
}
}
/**
* <code>PALNSThread</code> is a specialization of {@link Thread} that contains a clone of all the ALNS components
* <p>
* Creation date: Nov 18, 2011 - 1:47:27 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
*/
protected class PALNSThread extends Thread {
private final Map<IDestroy<S>, IDestroy<S>> mDestroyClones;
private final Map<IRepair<S>, IRepair<S>> mRepairCopies;
public PALNSThread(ThreadGroup group, Runnable runnable, String name, int stackSize) {
super(group, runnable, name, stackSize);
mDestroyClones = new HashMap<IDestroy<S>, IDestroy<S>>();
mRepairCopies = new HashMap<IRepair<S>, IRepair<S>>();
for (IDestroy<S> d : getDestroyComponents().getComponents())
mDestroyClones.put(d, d.clone());
for (IRepair<S> r : getRepairComponents().getComponents())
mRepairCopies.put(r, r.clone());
}
/**
* Returns this thread's clone of the given component
*
* @param original
* @return this thread's clone of the given component
*/
public IDestroy<S> getClone(IDestroy<S> original) {
return mDestroyClones.get(original);
}
/**
* Returns this thread's clone of the given component
*
* @param original
* @return this thread's clone of the given component
*/
public IRepair<S> getClone(IRepair<S> original) {
return mRepairCopies.get(original);
}
}
/**
* <code>PALNSThreadFactory</code> is a specialization of {@link NameThreadFactory} that returns instances of
* {@link PALNSThread}
* <p>
* Creation date: Nov 18, 2011 - 1:47:53 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
*/
protected class PALNSThreadFactory extends NameThreadFactory {
public PALNSThreadFactory(String name) {
super(name);
}
@Override
public Thread newThread(Runnable r) {
Thread t = new PALNSThread(getGroup(), r, getNamePrefix() + getThreadNumber().getAndIncrement(), 0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
@Override
public void dispose() {
super.dispose();
mSolPool.clear();
mExecutor.shutdown();
}
}