/**
*
*/
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.util.CostCalculationDelegate;
import vroom.common.utilities.optimization.IConstraint;
import vroom.common.utilities.optimization.IMove;
import vroom.common.utilities.optimization.IParameters;
/**
* <code>OrOptNeighborhood</code> is an implementation of the Or-opt neighborhood.
* <p>
* Creation date: Jul 2, 2010 - 1:59:41 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 OrOptNeighborhood<S extends IVRPSolution<?>> extends
GenericNeighborhood<S, OrOptMove<S>> {
/** Default for the maximum length of the relocated segment */
public static int DEFAULT_MAX_LENGTH = 3;
/** the maximum length of the relocated segment **/
private int mMaxLength;
/**
* Getter for maxLength
*
* @return the maximum length of the relocated segment
*/
public int getMaxLength() {
return this.mMaxLength;
}
/**
* Setter for maxLength : the maximum length of the relocated segment
*
* @param maxLength
* the value to be set for maxLength
*/
public void setMaxLength(int maxLength) {
this.mMaxLength = maxLength;
}
/**
* Creates a new <code>OrOptNeighborhood</code>
*
* @param constraintHandler
*/
public OrOptNeighborhood(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>());
}
setMaxLength(DEFAULT_MAX_LENGTH);
}
/*
* (non-Javadoc)
*
* @see
* vroom.common.heuristics.GenericNeighborhood#generateCandidateList(vroom.
* common.utilities.optimization.ISolution,
* vroom.common.utilities.optimization.IParameters)
*/
@Override
protected LinkedList<OrOptMove<S>> generateCandidateList(S solution, IParameters params) {
LinkedList<OrOptMove<S>> candidates = new LinkedList<OrOptMove<S>>();
// Iterate over all routes
for (int rI = 0; rI < solution.getRouteCount(); rI++) {
IRoute<?> route = solution.getRoute(rI);
// Iterate over all nodes of the route
for (int i = 0; i < route.length(); i++) {
if (!route.getNodeAt(i).isFixed()) {
// Iterate over the possible segments
for (int j = i; j < Math.min(i + getMaxLength() + 1, solution.getRoute(rI)
.length()); j++) {
if (route.getNodeAt(j).isFixed()) {
// containing a
// fixed node
break;
} else {
candidates.add(new OrOptMove<S>(solution, rI, i, j));
}
}
}
}
}
return candidates;
}
/*
* (non-Javadoc)
*
* @see vroom.common.heuristics.GenericNeighborhood#
* deterministicFirstImprovementExploration
* (vroom.common.utilities.optimization.ISolution,
* vroom.common.utilities.optimization.IParameters)
*/
@Override
public OrOptMove<S> deterministicFirstImprovement(S solution, IParameters params) {
// Iterate over all routes
for (int rI = 0; rI < solution.getRouteCount(); rI++) {
IRoute<?> route = solution.getRoute(rI);
// Iterate over all nodes of the route
for (int i = 0; i < route.length(); i++) {
if (!route.getNodeAt(i).isFixed()) {
// Iterate over the possible segments
for (int j = i; j < Math.min(i + getMaxLength() + 1, solution.getRoute(rI)
.length()); j++) {
if (route.getNodeAt(j).isFixed()) {
// containing a
// fixed node
break;
} else {
OrOptMove<S> move = new OrOptMove<S>(solution, rI, i, j);
evaluateCandidateMove(move);
if (getConstraintHandler().isFeasible(solution, move)
&& (getAcceptanceCriterion(params).accept(solution, this, move) || params
.acceptNonImproving())) {
return move;
}
}
}
}
}
}
return null;
}
/*
* (non-Javadoc)
*
* @see
* vroom.common.heuristics.GenericNeighborhood#evaluateCandidateMove(vroom.
* common.heuristics.Move)
*/
@Override
protected double evaluateCandidateMove(OrOptMove<S> cand) {
double bestImprov = Double.NEGATIVE_INFINITY;
int bestRoute = -1, bestIns = -1;
boolean bestInsRev = false;
CostCalculationDelegate cd = cand.getSolution().getParentInstance().getCostDelegate();
IRoute<?> route = cand.getSolution().getRoute(cand.getRouteI());
INodeVisit nI = route.getNodeAt(cand.getI());
INodeVisit nJ = route.getNodeAt(cand.getJ());
boolean singleton = cand.getI() == cand.getJ();
// Iterate over all routes
for (int r = 0; r < cand.getSolution().getRouteCount(); r++) {
IRoute<?> rIns = cand.getSolution().getRoute(r);
// Iterate over possible insertion points
for (int ins = 0; ins <= rIns.length(); ins++) {
// Exclude fixed nodes and positions in the sequence
if ((ins < rIns.length() && !rIns.getNodeAt(ins).isFixed() || ins == rIns.length()
&& !rIns.getLastNode().isFixed())
&& (r != cand.getRouteI() || ins > cand.getJ() + 1 || ins < cand.getI())) {
cand.setInsertion(r, ins, false);
// Check feasibility to prune infeasible insertions
// if (getConstraintHandler().checkMove(cand.getSolution(),
// cand)) {
double improv = 0;
INodeVisit nIns = ins < rIns.length() ? rIns.getNodeAt(ins) : null;
INodeVisit nPIns = ins > 0 ? rIns.getNodeAt(ins - 1) : null;
// Insertion cost if no reverse
// ADD (ins-1,i)
double nrCHead = nPIns != null ? cd.getCost(nPIns, nI, rIns.getVehicle()) : 0;
// ADD (j,ins)
double nrCTail = nIns != null ? cd.getCost(nJ, nIns, rIns.getVehicle()) : 0;
double rCHead = 0, rCTail = 0;
if (!singleton) {
// Insertion cost with reverse
// ADD (ins-1,j)
rCHead = ins > 0 ? cd.getCost(nPIns, nJ, rIns.getVehicle()) : 0;
// ADD (i,ins)
rCTail = ins < rIns.length() ? cd.getCost(nI, nIns, rIns.getVehicle()) : 0;
}
boolean reverse;
if (singleton || nrCHead + nrCTail < rCHead + rCTail) {
// Its cheaper not to reverse
reverse = false;
// Head insertion
improv -= nrCHead;
// Tail insertion
improv -= nrCTail;
} else {
// Its cheaper to reverse
reverse = true;
// Head insertion
// ADD (ins-1,i)
improv -= rCHead;
// Tail insertion
// ADD (j,ins)
improv -= rCTail;
}
if (nIns != null && nPIns != null) {
// REM (ins-1,ins)
improv += cd.getCost(nPIns, nIns, rIns.getVehicle());
}
if (improv > bestImprov
&& getConstraintHandler().isFeasible(cand.getSolution(), cand)) {
bestRoute = r;
bestIns = ins;
bestImprov = improv;
bestInsRev = reverse;
}
}
// }
}
}
// Extraction improvement
// REM (i-1,i)
if (cand.getI() > 0) {
bestImprov += cd.getCost(route.getNodeAt(cand.getI() - 1), nI, route.getVehicle());
}
// REM (j,j+1)
if (cand.getJ() < route.length() - 1) {
bestImprov += cd.getCost(nJ, route.getNodeAt(cand.getJ() + 1), route.getVehicle());
}
// ADD (i-1,j+1)
if (cand.getI() > 0 && cand.getJ() < route.length() - 1) {
bestImprov -= cd.getCost(route.getNodeAt(cand.getI() - 1),
route.getNodeAt(cand.getJ() + 1), route.getVehicle());
}
// Set the best insertion
cand.setInsertion(bestRoute, bestIns, bestInsRev);
cand.setImprovement(bestImprov);
return bestImprov;
}
/*
* (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) {
OrOptMove<?> mve = (OrOptMove<?>) move;
if (mve.getInsertionRoute() < 0) {
return false;
}
IRoute rSrc = solution.getRoute(mve.getRouteI());
IRoute rDest = solution.getRoute(mve.getInsertionRoute());
int i = mve.getI();
int j = mve.getJ();
int ins = mve.getInsertionIndex();
return executeMove(rSrc, rDest, i, j, ins, mve.isStringReversed());
}
/**
* Auxiliary method for type safety
*
* @param rSrc
* @param rDest
* @param i
* @param j
* @param ins
* @return
*/
private <V extends INodeVisit> boolean executeMove(IRoute<V> rSrc, IRoute<V> rDest, int i,
int j, int ins, boolean rev) {
if (rSrc == rDest && ins > j) {
// Shift the ins index
ins -= j - i + 1;
}
IRoute<V> subroute = rSrc.extractSubroute(i, j);
if (rev) {
subroute.reverseRoute();
}
return rDest.insertSubroute(ins, subroute);
}
/*
* (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 "OrOpt";
}
}