package vrp2013.algorithms; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; import java.util.concurrent.Callable; import vroom.common.heuristics.ConstraintHandler; import vroom.common.heuristics.GenericNeighborhood; import vroom.common.heuristics.vrp.StringExchangeNeighborhood; import vroom.common.modeling.dataModel.INodeVisit; import vroom.common.modeling.dataModel.IVRPInstance; import vroom.common.modeling.dataModel.RouteBase; import vroom.common.modeling.util.IRoutePool; 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 vrp2013.util.VRPLogging; import vrp2013.util.VRPSolution; /** * The class <code>VNS</code> is a sequential implementation of the Variable Neighborhood Search. * <p> * Creation date: 13/05/2013 - 9:36:40 AM * * @author Victor Pillac, <a href="http://www.nicta.com.au">National ICT Australia</a>, <a * href="http://www.victorpillac.com">www.victorpillac.com</a> * @version 1.0 */ public class VNS extends VND { private final List<NeighborhoodExplorer> mExplorers; /** * Creates a new <code>VNS</code> * * @param instance * @param constraintHandler * @param routePool * @author vpillac */ public VNS(IVRPInstance instance, ConstraintHandler<VRPSolution> constraintHandler, IRoutePool<INodeVisit> routePool) { super(instance, constraintHandler, routePool); // Redefine the neighborhoods used in this VNS getNeighborhoods().clear(); getNeighborhoods().add(new StringExchangeNeighborhood<>(constraintHandler, 2)); getNeighborhoods().add(new StringExchangeNeighborhood<>(constraintHandler, 3)); getNeighborhoods().add(new StringExchangeNeighborhood<>(constraintHandler, 4)); getNeighborhoods().add(new StringExchangeNeighborhood<>(constraintHandler, 5)); mExplorers = new ArrayList<>(getNeighborhoods().size()); for (INeighborhood<VRPSolution, ?> neighborhood : getNeighborhoods()) { mExplorers.add(new NeighborhoodExplorer(neighborhood, mExplorers.size())); } } /** * Returns the list of neighborhoods explorers * * @return the list of neighborhoods explorers * @author vpillac */ protected List<NeighborhoodExplorer> getExplorers() { return mExplorers; } @Override public boolean localSearch(final VRPSolution solution) { VRPLogging.logOptResults("ls-init", false, getNeighStopwatch(), solution); // The current solution is the best neighbor from the previous iteration VRPSolution currentSolution = solution; // A flag set to true if the solution was changed in the last iteration boolean changedLastIt = true; // A flag set to true if the solution was changed at least once boolean changedOverall = false; // While a the solution was changed in the last iteration while (changedLastIt) { changedLastIt = false; getNeighStopwatch().restart(); // Foreach neighborhood for (NeighborhoodExplorer explorer : mExplorers) { // Set the starting solution for this explorer explorer.setStartSolution(currentSolution); // Execute the exploration VRPSolution neighbor = explorer.call(); if (OptimizationSense.MINIMIZATION.isBetter(currentSolution, neighbor)) { // We found a neighbor that is better that the current best neighbor changedLastIt = true; changedOverall = true; currentSolution = neighbor; // Restart from the first neighborhood break; } } getNeighStopwatch().stop(); } // Copy the best solution to the solution passed as argument if (changedOverall) { solution.clear(); for (RouteBase r : currentSolution) if (r.length() > 2) solution.addRoute(r); } return changedOverall; } /** * The class <code>NeighborhoodExplorer</code> is an implementation of {@link Callable} that performs the * exploration of a neighborhood * <p> * Creation date: 13/05/2013 - 9:43:02 AM * * @author vpillac, <a href="http://www.nicta.com.au">National ICT Australia</a> * @version 1.0 */ public class NeighborhoodExplorer implements Callable<VRPSolution> { private final SimpleParameters mShakeParameters; private final VND mLS; private final INeighborhood<VRPSolution, ?> mShakeNeighborhood; private VRPSolution mStartSolution, mLastSolution; private final Stopwatch mStopwatch; /** * Creates a new <code>NeighborhoodExplorer</code> * * @param neighborhood * the neighborhood that will be used to <em>shake</em> the solution * @param seed * the seed for the random stream * @author vpillac */ @SuppressWarnings({ "unchecked", "rawtypes" }) public NeighborhoodExplorer(INeighborhood<VRPSolution, ?> neighborhood, long seed) { super(); mShakeNeighborhood = neighborhood; mLS = new VND(getInstance(), ((GenericNeighborhood) getShakeNeighborhood()).getConstraintHandler(), getRoutePool()); mShakeParameters = new SimpleParameters(LSStrategy.RND_NON_IMPROVING, Integer.MAX_VALUE, Integer.MAX_VALUE, seed); // Make sure the local search does not use the neighborhoods defined in the shake ListIterator<INeighborhood<VRPSolution, ?>> it = mLS.getNeighborhoods().listIterator(); while (it.hasNext()) { Class<? extends INeighborhood> nClass = it.next().getClass(); for (INeighborhood<VRPSolution, ?> shake : getNeighborhoods()) { if (nClass == shake.getClass()) it.remove(); break; } } mStopwatch = new Stopwatch(); } /** * Sets the start solution for the next call to {@link #call()} <br/> * Note that this will {@link VRPSolution#clone() clone} the {@code startSolution} * * @param startSolution * @author vpillac */ public void setStartSolution(VRPSolution startSolution) { mStartSolution = startSolution.clone(); } /** * Return the last solution found while exploring the attached neighborhood * * @return the last solution found while exploring the attached neighborhood * @author vpillac */ public VRPSolution getLastSolution() { return mLastSolution; } @Override public VRPSolution call() { mStopwatch.restart(); // Clone the start solution mLastSolution = mStartSolution; // Shake getShakeNeighborhood().pertub(getInstance(), mLastSolution, mShakeParameters); VRPLogging.logOptResults("#" + getShakeNeighborhood().getShortName(), true, mStopwatch, mLastSolution); // Execute the best improvement local search (VND) mLS.localSearch(mLastSolution); mStopwatch.stop(); // Return the solution return mLastSolution; } public INeighborhood<VRPSolution, ?> getShakeNeighborhood() { return mShakeNeighborhood; } } }