/** * */ package vroom.common.heuristics.vrp; import java.util.LinkedList; import vroom.common.heuristics.ConstraintHandler; import vroom.common.heuristics.GenericNeighborhood; import vroom.common.modeling.dataModel.INodeVisit; import vroom.common.modeling.dataModel.IRoute; import vroom.common.modeling.dataModel.IVRPSolution; import vroom.common.modeling.dataModel.Vehicle; import vroom.common.modeling.util.CostCalculationDelegate; import vroom.common.utilities.optimization.IMove; import vroom.common.utilities.optimization.IParameters; /** * <code>StringExchangeNeighborhood</code> is a generic implementation of the string-exchange neighborhood. * <p> * Creation date: Jul 8, 2010 - 3:30:54 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 */ public class StringExchangeNeighborhood<S extends IVRPSolution<?>> extends GenericNeighborhood<S, StringExchangeMove<S>> { /** A default value for the maximum string length */ public static int MAXIMUM_STRING_LENGTH = 4; /** the maximum string length to be evaluated **/ private int mMaxLength = MAXIMUM_STRING_LENGTH; /** * Getter for the maximum string length * * @return the maximum string length to be evaluated */ public int getMaxLength() { return this.mMaxLength; } /** * Setter for the maximum string length to be evaluated * * @param maxLength * the value to be set for the maximum string length */ public void setMaxLength(int maxLength) { this.mMaxLength = maxLength; } /** the minimum string length to be evaluated **/ private int mMinLength = 1; /** * Getter for the minimum string length * * @return the minimum string length to be evaluated */ public int getMinLength() { return this.mMinLength; } /** * Setter for the minimum string length to be evaluated * * @param minLength * the value to be set for the minimum string length */ public void setMinLength(int minLength) { this.mMinLength = minLength; } /** * Creates a new <code>StringExchangeNeighborhood</code> based on the given constraint handler * * @param ctrHandler * a constraint handler */ public StringExchangeNeighborhood(ConstraintHandler<S> constraintHandler) { this(constraintHandler, MAXIMUM_STRING_LENGTH); } /** * Creates a new <code>StringExchangeNeighborhood</code> based on the given constraint handler * * @param ctrHandler * a constraint handler * @param maxLength * the maximum length of the string to be considered */ public StringExchangeNeighborhood(ConstraintHandler<S> constraintHandler, int maxLength) { super(constraintHandler); setMaxLength(maxLength); } /* * (non-Javadoc) * * @see * vroom.common.heuristics.INeighborhood#executeMove(vroom.common.utilities * .optimization.ISolution, vroom.common.heuristics.Move) */ @SuppressWarnings({ "unchecked", "rawtypes" }) @Override protected boolean executeMoveImplem(S solution, IMove move) { StringExchangeMove<S> ex = (StringExchangeMove<S>) move; if (ex.getFirstRoute() == ex.getSecondRoute()) { } IRoute r1 = solution.getRoute(ex.getFirstRoute()); IRoute r2 = solution.getRoute(ex.getSecondRoute()); return executeMove(r1, ex.getNodeI(), ex.getNodeJ(), r2, ex.getNodeK(), ex.getNodeL(), ex.isReverseFirst(), ex.isReverseSecond()); } /** * Auxiliary method for type safety * * @param <V> * @param r1 * @param nodeI * @param nodeJ * @param r2 * @param nodeK * @param nodeL * @param rev1 * @param rev2 * @return */ private <V extends INodeVisit> boolean executeMove(IRoute<V> r1, int nodeI, int nodeJ, IRoute<V> r2, int nodeK, int nodeL, boolean rev1, boolean rev2) { IRoute<V> string2 = r2.extractSubroute(nodeK, nodeL); IRoute<V> string1 = r1.extractSubroute(nodeI, nodeJ); if (rev1) { string1.reverseRoute(); } if (rev2) { string2.reverseRoute(); } r1.insertSubroute(nodeI, string2); if (r1 == r2) { nodeK = nodeK - (nodeJ - nodeI) + nodeL - nodeK; } r2.insertSubroute(nodeK, string1); return true; } /* * (non-Javadoc) * * @see vroom.common.heuristics.GenericNeighborhood# * deterministicFirstImprovementExploration * (vroom.common.utilities.optimization.ISolution, * vroom.common.utilities.optimization.IParameters) */ @Override public StringExchangeMove<S> deterministicFirstImprovement(S solution, IParameters params) { // Iterate over all routes for (int r1 = 0; r1 < solution.getRouteCount(); r1++) { IRoute<?> route1 = solution.getRoute(r1); // Iterate over all nodes of the first route for (int i = 0; i < route1.length(); i++) { if (!route1.getNodeAt(i).isFixed()) { // Iterate over the possible segments of the first route for (int j = i + getMinLength() - 1; j < Math.min(i + getMaxLength() + 1, route1.length()); j++) { if (route1.getNodeAt(j).isFixed()) { break; } else { // Iterate over all routes for (int r2 = r1; r2 < solution.getRouteCount(); r2++) { IRoute<?> route2 = solution.getRoute(r2); for (int k = r1 == r2 ? j + 1 : 0; k < route2.length(); k++) { if (!route2.getNodeAt(k).isFixed()) { // Iterate over the possible segments of // the second route for (int l = k + Math.max(getMinLength() - 1, i == j ? 1 : 0); l < Math .min(k + getMaxLength() + 1, route2.length()); l++) { if (route2.getNodeAt(l).isFixed()) { break; } else { StringExchangeMove<S> move = new StringExchangeMove<S>( solution, r1, r2, i, j, k, l); boolean feasible = false; if (getConstraintHandler().isFeasible(solution, move)) { evaluateCandidateMove(move); feasible = true; } if (feasible && (getAcceptanceCriterion(params).accept( solution, this, move) || params .acceptNonImproving())) { return move; } else { move = null; } } } } } } } } } } } return null; } // FIXME implement random exploration // @Override // public StringExchangeMove<S> randomExploration(S solution, // IParameters params) { // // StringExchangeMove<S> move = null; // while (move==null) { // // // // } // return move; // }; /* * (non-Javadoc) * * @see * vroom.common.heuristics.GenericNeighborhood#generateCandidateList(vroom. * common.utilities.optimization.ISolution, * vroom.common.utilities.optimization.IParameters) */ @Override protected LinkedList<StringExchangeMove<S>> generateCandidateList(S solution, IParameters params) { LinkedList<StringExchangeMove<S>> candidates = new LinkedList<StringExchangeMove<S>>(); // Iterate over all routes for (int r1 = 0; r1 < solution.getRouteCount(); r1++) { IRoute<?> route1 = solution.getRoute(r1); // Iterate over all nodes of the first route for (int i = 0; i < route1.length(); i++) { if (!route1.getNodeAt(i).isFixed()) { // Iterate over the possible segments of the first route for (int j = i; j < Math.min(i + getMaxLength() + 1, route1.length()); j++) { if (route1.getNodeAt(j).isFixed()) { break; } else { // Iterate over all routes for (int r2 = r1; r2 < solution.getRouteCount(); r2++) { IRoute<?> route2 = solution.getRoute(r2); for (int k = r1 == r2 ? j + 1 : 0; k < route2.length(); k++) { if (!route2.getNodeAt(k).isFixed()) { // Iterate over the possible segments of // the second route for (int l = k + (i == j ? 1 : 0); l < Math.min(k + getMaxLength() + 1, route2.length()); l++) { if (route2.getNodeAt(l).isFixed()) { break; } else { candidates.add(new StringExchangeMove<S>(solution, r1, r2, i, j, k, l)); } } } } } } } } } } return candidates; } /* * (non-Javadoc) * * @see * vroom.common.heuristics.GenericNeighborhood#evaluateCandidateMove(vroom. * common.heuristics.Move) */ @Override protected double evaluateCandidateMove(StringExchangeMove<S> cand) { double improv = 0; // Assumes cost symmetry if (!cand.getSolution().getParentInstance().isSymmetric()) { throw new UnsupportedOperationException( "This implementation only support symetric instances"); } IRoute<?> r1 = cand.getSolution().getRoute(cand.getFirstRoute()); Vehicle v1 = r1.getVehicle(); IRoute<?> r2 = cand.getSolution().getRoute(cand.getSecondRoute()); Vehicle v2 = r2.getVehicle(); if (v1.getVariableCost() != v2.getVariableCost()) { throw new UnsupportedOperationException( "This implementation only support identical vehicles"); } boolean consec = r1 == r2 && cand.getNodeJ() + 1 == cand.getNodeK(); INodeVisit i = r1.getNodeAt(cand.getNodeI()); INodeVisit pi = cand.getNodeI() > 0 ? r1.getNodeAt(cand.getNodeI() - 1) : null; INodeVisit j = r1.getNodeAt(cand.getNodeJ()); INodeVisit sj = cand.getNodeJ() < r1.length() - 1 ? r1.getNodeAt(cand.getNodeJ() + 1) : null; INodeVisit k = consec ? sj : r2.getNodeAt(cand.getNodeK()); INodeVisit pk = consec ? j : cand.getNodeK() > 0 ? r2.getNodeAt(cand.getNodeK() - 1) : null; INodeVisit l = r2.getNodeAt(cand.getNodeL()); INodeVisit sl = cand.getNodeL() < r2.length() - 1 ? r2.getNodeAt(cand.getNodeL() + 1) : null; CostCalculationDelegate cd = cand.getSolution().getParentInstance().getCostDelegate(); if (pi != null) { improv += cd.getCost(pi, i, v1); } if (sj != null) { improv += cd.getCost(j, sj, v1); } if (pk != null && !consec) { improv += cd.getCost(pk, k, v2); } if (sl != null) { improv += cd.getCost(l, sl, v2); } boolean reverseFirst = false, reverseSecond = false; double cjsl = sl != null ? cd.getCost(j, sl, v2) : 0; // (j,l+1) double cpik = pi != null ? cd.getCost(pi, k, v1) : 0; // (i-1,k) double cpil = pi != null ? cd.getCost(pi, l, v1) : 0; // (i-1,l) double cisl = sl != null ? cd.getCost(i, sl, v2) : 0; // (i,l+1) if (!consec) { // No reverse double clsj = sj != null ? cd.getCost(l, sj, v1) : 0; // (l,j+1) //* // ..,i-1,k,..,l,j+1,.. double nrev2 = cpik + clsj; double cpki = pk != null ? cd.getCost(pk, i, v2) : 0; // (k-1,i) //* // ..,k-1,i,..,j,l,.. double nrev1 = cpki + cjsl; // Reverse double cksj = sj != null ? cd.getCost(k, sj, v1) : 0; // (k,j+1) //* // ..,i-1,l,..,k,j+1,.. double rev2 = cpil + cksj; double cpkj = pk != null ? cd.getCost(pk, j, v2) : 0; // (k-1,j) //* // ..,k-1,j,..,i,l+1,.. double rev1 = cpkj + cisl; if (rev2 < nrev2) { reverseSecond = true; improv -= rev2; } else { improv -= nrev2; } if (rev1 < nrev1) { reverseFirst = true; improv -= rev1; } else { improv -= nrev1; } } else { double cli = cd.getCost(l, i, v1); // (l,i) double clj = cd.getCost(l, j, v1); // (l,j) double cki = cd.getCost(k, i, v1); // (k,i) double ckj = cd.getCost(k, j, v1); // (k,j) double[] costs = new double[4]; // Consecutive subroutes // No rev 1 - No rev 2 // ..,i-1,k,..,l,i,..,j,l+1,.. costs[0] = cpik + cli + cjsl; // Rev 1 - No rev 2 // ..,i-1,k,..,l,j,..,i,l+1,.. costs[1] = cpik + clj + cisl; // No rev 1 - Rev 2 // ..,i-1,l,..,k,i,..,j,l+1,.. costs[2] = cpil + cki + cjsl; // Rev 1 - Rev 2 // ..,i-1,l,..,k,j,..,i,l+1,.. costs[3] = cpil + ckj + cisl; double min = Double.MAX_VALUE; int best = -1; for (int m = 0; m < costs.length; m++) { if (costs[m] < min) { min = costs[m]; best = m; } } reverseFirst = best == 1 || best == 3; reverseSecond = best == 2 || best == 3; improv -= min; } cand.setReverseFirst(reverseFirst); cand.setReverseSecond(reverseSecond); cand.setImprovement(improv); return improv; } /* * (non-Javadoc) * * @see vroom.common.heuristics.GenericNeighborhood#toString() */ @Override public String toString() { return String.format("%s (maxLenght:%s)", super.toString(), getMaxLength()); } @Override public String getShortName() { return "strExg-" + getMaxLength(); } }