/**
*
*/
package vroom.common.heuristics.vrp;
import java.util.LinkedList;
import vroom.common.heuristics.ConstraintHandler;
import vroom.common.heuristics.GenericNeighborhood;
import vroom.common.heuristics.vrp.constraints.FixedNodesConstraint;
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.IConstraint;
import vroom.common.utilities.optimization.IMove;
import vroom.common.utilities.optimization.IParameters;
/**
* <code>TwoOptNeighborhood</code> is a generic implementation of the 2-Opt neighborhood.
* <p>
* Creation date: Jun 18, 2010 - 4:19:34 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 TwoOptNeighborhood<S extends IVRPSolution<?>> extends
GenericNeighborhood<S, TwoOptMove> {
/**
* Creates a new <code>TwoOptNeighborhood</code>
*
* @param constraintHandler
*/
public TwoOptNeighborhood(ConstraintHandler<S> constraintHandler) {
super(constraintHandler);
boolean add = true;
for (IConstraint<?> ctr : getConstraintHandler()) {
if (ctr instanceof FixedNodesConstraint<?>) {
add = false;
break;
}
}
if (add) {
getConstraintHandler().addConstraint(new FixedNodesConstraint<S>());
}
}
/**
* Creates a new <code>TwoOptNeighborhood</code>
*/
public TwoOptNeighborhood() {
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
protected boolean executeMoveImplem(S solution, IMove move) {
if (!(move instanceof TwoOptMove)) {
throw new IllegalArgumentException("The move must be of type TwoOptMove");
}
boolean r;
TwoOptMove twoOptMove = (TwoOptMove) move;
IRoute rI = solution.getRoute(twoOptMove.getRouteI());
IRoute rJ = null;
int i = twoOptMove.getI();
int j = twoOptMove.getJ();
// Intra route move
if (twoOptMove.getRouteI() == twoOptMove.getRouteJ()) {
// Remove (nodeI,nodeI+1) and (nodeJ,nodeJ+1)
// Reconnect nodeI with nodeJ and nodeI+1 with nodeJ+1
rI.reverseSubRoute(i + 1, j);
// rI.updateCost(-twoOptMove.getImprovement());
r = true;
// Inter route move
} else {
rJ = solution.getRoute(twoOptMove.getRouteJ());
// Auxiliary method to solve type safety problems
r = executeMove(rI, rJ, i, j, twoOptMove.isStar());
}
return r;
}
/**
* Auxiliary method used for type safety
*
* @param <V>
* @param rI
* @param rJ
* @param nodeI
* @param nodeJ
* @param star
* @return
*/
protected <V extends INodeVisit> boolean executeMove(IRoute<V> rI, IRoute<V> rJ, int i, int j,
boolean star) {
if (!star) {
// standard 2-opt
// nodeI linked with nodeJ
// nodeI+1 linked with nodeJ+1
IRoute<V> startJ = rJ.extractSubroute(0, j);
IRoute<V> endI = rI.extractSubroute(i + 1, rI.length() - 1);
startJ.reverseRoute();
endI.reverseRoute();
rI.appendRoute(startJ);
rJ.insertSubroute(0, endI);
} else {
// special 2-opt*
// nodeI linked with nodeJ+1
// nodeI+1 linked with nodeJ
IRoute<V> endJ = rJ.extractSubroute(j + 1, rJ.length() - 1);
IRoute<V> endI = rI.extractSubroute(i + 1, rI.length() - 1);
rI.appendRoute(endJ);
rJ.appendRoute(endI);
}
return true;
}
/**
* Compute the (best) 2-opt move defined by the arcs <code>(nodeI,nodeI+1)</code> in route <code>rJ</code> and
* <code>(nodeJ,nodeJ+1)</code> in route <code>rJ</code>
*
* @param mSolution
* @param rI
* @param rJ
* @param nodeI
* @param nodeJ
* @return the (best) 2-opt move for the given routes and nodes
*/
protected TwoOptMove newMove(IVRPSolution<?> solution, int rI, int rJ, int i, int j) {
CostCalculationDelegate costHelper = solution.getParentInstance().getCostDelegate();
Vehicle v1 = solution.getRoute(rI).getVehicle();
Vehicle v2 = solution.getRoute(rJ).getVehicle();
INodeVisit a = solution.getRoute(rI).getNodeAt(i);
INodeVisit b = solution.getRoute(rI).getNodeAt(i + 1);
INodeVisit c = solution.getRoute(rJ).getNodeAt(j);
INodeVisit d = solution.getRoute(rJ).getNodeAt(j + 1);
double improv = 0;
boolean star = false;
// 2-opt intra/inter-route
// nodeI (a) linked with nodeJ (c)
// nodeI+1 (b) linked with nodeJ+1 (d)
improv = -costHelper.getCost(a, c, v1) - costHelper.getCost(b, d, v2)
+ costHelper.getCost(a, b, v1) + costHelper.getCost(c, d, v2);
if (rI != rJ) {
// special 2-opt*
// nodeI (a) linked with nodeJ+1 (d)
// nodeI+1 (b) linked with nodeJ (c)
double improvStar = -costHelper.getCost(a, d, v1) - costHelper.getCost(b, c, v2)
+ costHelper.getCost(a, b, v1) + costHelper.getCost(c, d, v2);
if (improvStar > improv) {
improv = improvStar;
star = true;
}
}
return new TwoOptMove(improv, solution, rI, rJ, i, j, star);
}
/*
* (non-Javadoc)
*
* @see vroom.common.heuristics.GenericNeighborhood#
* deterministicFirstImprovementExploration
* (vroom.common.utilities.optimization.ISolution,
* vroom.common.utilities.optimization.IParameters)
*/
@Override
public TwoOptMove deterministicFirstImprovement(S solution, IParameters params) {
// Iterate over all routes
for (int r1 = 0; r1 < solution.getRouteCount(); r1++) {
for (int r2 = r1; r2 < solution.getRouteCount(); r2++) {
// Iterate over the nodes of the first route
for (int i = 0; i < solution.getRoute(r1).length() - 1; i++) {
// Iterate over the nodes of the second route
for (int j = r1 != r2 ? 0 : i + 2; j < solution.getRoute(r2).length() - 1; j++) {
TwoOptMove move = new TwoOptMove(Double.NEGATIVE_INFINITY, solution, r1,
r2, i, j, false);
move.setImprovement(evaluateCandidateMove(move));
if ((getAcceptanceCriterion(params).accept(solution, this, move) || params
.acceptNonImproving())
&& getConstraintHandler().isFeasible(solution, move)) {
return move;
}
}
}
}
}
return null;
}
/*
* (non-Javadoc)
*
* @see
* vroom.common.heuristics.GenericNeighborhood#evaluateCandidateMove(vroom.
* common.heuristics.Move)
*/
@Override
protected double evaluateCandidateMove(TwoOptMove cand) {
CostCalculationDelegate costHelper = cand.mSolution.getParentInstance().getCostDelegate();
Vehicle v1 = cand.mSolution.getRoute(cand.getRouteI()).getVehicle();
Vehicle v2 = cand.mSolution.getRoute(cand.getRouteJ()).getVehicle();
INodeVisit a = cand.mSolution.getRoute(cand.getRouteI()).getNodeAt(cand.getI());
INodeVisit b = cand.mSolution.getRoute(cand.getRouteI()).getNodeAt(cand.getI() + 1);
INodeVisit c = cand.mSolution.getRoute(cand.getRouteJ()).getNodeAt(cand.getJ());
INodeVisit d = cand.mSolution.getRoute(cand.getRouteJ()).getNodeAt(cand.getJ() + 1);
double improv = 0;
boolean star = false;
// 2-opt intra/inter-route
// nodeI (a) linked with nodeJ (c)
// nodeI+1 (b) linked with nodeJ+1 (d)
improv = -costHelper.getCost(a, c, v1) - costHelper.getCost(b, d, v2)
+ costHelper.getCost(a, b, v1) + costHelper.getCost(c, d, v2);
if (cand.getRouteI() != cand.getRouteJ()) {
// special 2-opt*
// nodeI (a) linked with nodeJ+1 (d)
// nodeI+1 (b) linked with nodeJ (c)
double improvStar = -costHelper.getCost(a, d, v1) - costHelper.getCost(b, c, v2)
+ costHelper.getCost(a, b, v1) + costHelper.getCost(c, d, v2);
if (improvStar > improv) {
improv = improvStar;
star = true;
}
}
cand.setStar(star);
return improv;
}
/*
* (non-Javadoc)
*
* @see
* vroom.common.heuristics.GenericNeighborhood#generateCandidateList(vroom.
* common.utilities.optimization.ISolution,
* vroom.common.utilities.optimization.IParameters)
*/
@Override
protected LinkedList<TwoOptMove> generateCandidateList(S solution, IParameters params) {
LinkedList<TwoOptMove> candidates = new LinkedList<TwoOptMove>();
// Iterate over all routes
for (int r1 = 0; r1 < solution.getRouteCount(); r1++) {
for (int r2 = r1; r2 < solution.getRouteCount(); r2++) {
// Iterate over the nodes of the first route
for (int i = 0; i < solution.getRoute(r1).length() - 1; i++) {
// Iterate over the nodes of the second route
for (int j = r1 != r2 ? 0 : i + 2; j < solution.getRoute(r2).length() - 1; j++) {
candidates.add(new TwoOptMove(Double.NEGATIVE_INFINITY, solution, r1, r2,
i, j, false));
}
}
}
}
return candidates;
}
@Override
public String getShortName() {
return "twoOpt";
}
}