/** * */ package vroom.common.heuristics; import java.util.LinkedList; import umontreal.iro.lecuyer.rng.RandomPermutation; import vroom.common.heuristics.utils.HeuristicsLogging; import vroom.common.modeling.dataModel.IVRPSolution; import vroom.common.modeling.util.SolutionChecker; import vroom.common.utilities.optimization.IAcceptanceCriterion; import vroom.common.utilities.optimization.IInstance; import vroom.common.utilities.optimization.IMove; import vroom.common.utilities.optimization.IParameters; import vroom.common.utilities.optimization.ISolution; /** * <code>GenericNeighborhood</code> is a generic implementation that provide a frame for neighborhood exploration. * <p> * It is useful for rapid prototyping. * </p> * <p> * Creation date: Jun 22, 2010 - 9:21:21 AM * * @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 abstract class GenericNeighborhood<S extends ISolution, M extends Move> extends NeighborhoodBase<S, M> { /** * Creates a new <code>GenericNeighborhood</code> * * @param constraintHandler * the constraint handler to be used in this neighborhood */ public GenericNeighborhood(ConstraintHandler<S> constraintHandler) { super(constraintHandler); } /** * Creates a new <code>GenericNeighborhood</code> with a default constraint handler */ public GenericNeighborhood() { super(); } /** * A generic exploration procedure that enumerates all possible moves and then select one depending on the given * parameters * * @param solution * @param params * @return a move */ private final M genericExploration(S solution, IParameters params) { M move = null; // Create a list of candidate moves LinkedList<M> candidates = generateCandidateList(solution, params); if (params.randomize() && (params.acceptFirstImprovement() || params.acceptNonImproving())) { // Shuffle candidate list RandomPermutation.shuffle(candidates, params.getRandomStream()); } IAcceptanceCriterion acceptance = getAcceptanceCriterion(params); while (!candidates.isEmpty()) { // Pop the next candidate M cand = candidates.pop(); // Evaluate the move cand.setImprovement(evaluateCandidateMove(cand)); // Improving move or first improving move if ((params.acceptNonImproving() || acceptance.accept(solution, this, cand) && (getConstraintHandler().isFeasible(solution, cand))) && (move == null || (move != null && cand.getImprovement() > move .getImprovement()))) { move = cand; if (params.acceptFirstImprovement() || params.acceptNonImproving()) { break; } } } return move; } @Override protected M randomFirstImprovement(S solution, IParameters params) { return genericExploration(solution, params); }; @Override protected M deterministicFirstImprovement(S solution, IParameters params) { return genericExploration(solution, params); }; @Override public M deterministicBestImprovement(S solution, IParameters params) { return genericExploration(solution, params); }; /** * A specialized implementation of {@link #exploreNeighborhood(ISolution, IParameters)} for random non-improving * exploration. * * @param mSolution * mSolution the mSolution which neighborhood has to be explored * @param params * optional parameters for the neighborhood exploration * @return a random move in this neighborhood, not necesarily improving * @see #exploreNeighborhood(ISolution, IParameters) */ @Override public M randomNonImproving(S solution, IParameters params) { // Create a list of candidate moves LinkedList<M> candidates = generateCandidateList(solution, params); if (params.randomize() && (params.acceptFirstImprovement() || params.acceptNonImproving())) { // Shuffle candidate list RandomPermutation.shuffle(candidates, params.getRandomStream()); } while (!candidates.isEmpty()) { // Pop the next candidate M cand = candidates.pop(); // Improving move or first improving move if (getConstraintHandler().isFeasible(solution, cand)) { return cand; } // Evaluate the move cand.setImprovement(evaluateCandidateMove(cand)); } return null; } /** * Generate a list of candidate moves. * <p> * Implementations should not evaluate moves nor check their feasibility as it is the responsibility of the * {@link #exploreNeighborhood(ISolution, IParameters)} method * </p> * * @return a list containing candidate moves */ protected abstract LinkedList<M> generateCandidateList(S solution, IParameters params); /** * Evaluate a candidate move by calculating the associated improvement * * @param cand * a candidate move * @return the improvement associated with the given candidate move */ protected abstract double evaluateCandidateMove(M cand); @Override public final boolean executeMove(S solution, IMove move) { String prevSol = isCheckSolutionAfterMove() ? solution.toString() : null; boolean prev = checkSolution(solution, move, true, true, prevSol); boolean b = executeMoveImplem(solution, move); return b && checkSolution(solution, move, false, prev, prevSol); }; /** * Implementation of {@link #executeMove(ISolution, Move)} * * @param solution * @param move * @return */ protected abstract boolean executeMoveImplem(S solution, IMove move); @Override public void pertub(IInstance instance, S solution, IParameters parameters) { M move = randomNonImproving(solution, parameters); if (move != null) { executeMove(solution, move); } }; /** * Check a solution for feasibility * * @param solution * @param move * @param before * <code>true</code> if this is a pre-check * @param prevState * @param prevSol * @return */ @Override protected boolean checkSolution(S solution, IMove move, boolean before, boolean prevState, String prevSol) { if (isCheckSolutionAfterMove() && prevState) { String errChck = SolutionChecker.checkSolution((IVRPSolution<?>) solution, true, true, true); String errCtr = getConstraintHandler().getInfeasibilityExplanation(solution); String err = errChck != null ? errCtr != null ? errChck + " ; " + errCtr : errChck : errCtr; if (err != null && prevState) { if (!before) { HeuristicsLogging .getNeighborhoodsLogger() .debug("%s: solution is infeasible/inconsistent %s executing move %s (err: %s, sol:%s)", this.getClass().getSimpleName(), before ? "before" : "after", move, err, before ? solution : prevSol); } return false; } else { return true; } } return prevState; } @Override public void dispose() { // Do nothing } }