/**
*
*/
package vroom.common.heuristics.vrp.constraints;
import java.util.Iterator;
import java.util.List;
import vroom.common.heuristics.cw.kernel.RouteMergingMove;
import vroom.common.heuristics.vrp.OrOptMove;
import vroom.common.heuristics.vrp.RelocateMove;
import vroom.common.heuristics.vrp.RelocateMove.RelocateAtomicMove;
import vroom.common.heuristics.vrp.StringExchangeMove;
import vroom.common.heuristics.vrp.SwapMove;
import vroom.common.heuristics.vrp.TwoOptMove;
import vroom.common.modeling.dataModel.INodeVisit;
import vroom.common.modeling.dataModel.IRoute;
import vroom.common.modeling.dataModel.IVRPSolution;
import vroom.common.utilities.Utilities;
import vroom.common.utilities.optimization.IConstraint;
import vroom.common.utilities.optimization.IMove;
/**
* <code>CapacityConstraint</code> is a constraint that ensures that each routes of a mSolution satisfy the capacity of its vehicle and that a given
* move will not violate capacities.
* <p>
* Supported moves:
* <ul>
* <li>{@link TwoOptMove}</li>
* <li>{@link SwapMove}</li>
* <li>{@link OrOptMove}</li>
* <li>{@link RouteMergingMove}</li>
* </ul>
* <p>
* Creation date: Jun 22, 2010 - 9:56:51 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 class CapacityConstraint<S extends IVRPSolution<?>> implements IConstraint<S> {
/*
* (non-Javadoc)
* @see vroom.common.heuristics.IConstraint#checkMove(java.lang.Object, vroom.common.heuristics.Move)
*/
@Override
public boolean isFeasible(S solution, IMove move) {
if (move instanceof TwoOptMove) {
return check2Opt(solution, (TwoOptMove) move);
} else if (move instanceof RouteMergingMove) {
return checkRouteMerge(solution, (RouteMergingMove) move);
} else if (move instanceof SwapMove) {
return checkSwap(solution, (SwapMove) move);
} else if (move instanceof OrOptMove<?>) {
return checkOrOpt(solution, (OrOptMove<?>) move);
} else if (move instanceof StringExchangeMove<?>) {
return checkStringExchange(solution, (StringExchangeMove<?>) move);
} else if (move instanceof RelocateMove) {
for (RelocateAtomicMove reloc : ((RelocateMove) move).getAtomicMoves()) {
if (!checkRelocAtomicMove(reloc)) {
return false;
}
}
return true;
} else if (move instanceof RelocateAtomicMove) {
return checkRelocAtomicMove((RelocateAtomicMove) move);
}
return true;
}
public boolean checkRelocAtomicMove(RelocateAtomicMove move) {
IRoute<?> rte = move.getInsertion().getRoute();
double[] loads = new double[rte.getVehicle().getCompartmentCount()];
for (int p = 0; p < loads.length; p++) {
loads[p] += move.getNode().getDemand(p);
if (loads[p] > rte.getVehicle().getCapacity(p)) {
return false;
}
}
return true;
}
/**
* Capacity check for a Or-opt move
*
* @param mSolution
* @param move
* @return
*/
public boolean checkOrOpt(S solution, OrOptMove<?> move) {
if (move.getRouteI() == move.getInsertionRoute()) {
// Assumes that the route is already feasible
return true;
}
List<?> segment = solution.getRoute(move.getRouteI()).subroute(move.getI(), move.getJ());
if (move.getInsertionRoute() < 0) {
return false;
}
IRoute<?> dest = solution.getRoute(move.getInsertionRoute());
double[] load = new double[dest.getVehicle().getCompartmentCount()];
for (int p = 0; p < load.length; p++) {
load[p] = dest.getLoad(p);
}
Iterator<INodeVisit> it = Utilities.castIterator(segment.iterator());
while (it.hasNext()) {
INodeVisit node = it.next();
for (int p = 0; p < load.length; p++) {
load[p] += node.getDemand(p);
if (load[p] > dest.getVehicle().getCapacity(p)) {
return false;
}
}
}
return true;
}
public boolean checkStringExchange(S solution, StringExchangeMove<?> move) {
if (move.getFirstRoute() == move.getSecondRoute()) {
// Assumes that the route is already feasible
return true;
}
IRoute<?> r1 = solution.getRoute(move.getFirstRoute());
IRoute<?> r2 = solution.getRoute(move.getSecondRoute());
double[] s1Load = new double[r1.getVehicle().getCompartmentCount()];
double[] s2Load = new double[r2.getVehicle().getCompartmentCount()];
for (int i = move.getNodeI(); i <= move.getNodeJ(); i++) {
for (int k = 0; k < s1Load.length; k++) {
s1Load[k] += r1.getNodeAt(i).getDemand(k);
}
}
for (int i = move.getNodeK(); i <= move.getNodeL(); i++) {
for (int k = 0; k < s2Load.length; k++) {
s2Load[k] += r2.getNodeAt(i).getDemand(k);
}
}
for (int k = 0; k < s1Load.length; k++) {
if (r1.getLoad(k) - s1Load[k] + s2Load[k] > r1.getVehicle().getCapacity(k)) {
return false;
}
if (r2.getLoad(k) - s2Load[k] + s1Load[k] > r2.getVehicle().getCapacity(k)) {
return false;
}
}
return true;
}
/**
* Capacity check for a node swap
*
* @param mSolution
* @param move
* @return <code>true</code> if the move is feasible
*/
public boolean checkSwap(IVRPSolution<?> solution, SwapMove move) {
if (move.getRouteI() == move.getRouteJ()) {
// Assumes that the route is already feasible
return true;
}
IRoute<?> rI = solution.getRoute(move.getRouteI());
IRoute<?> rJ = solution.getRoute(move.getRouteJ());
INodeVisit i = rI.getNodeAt(move.getI());
INodeVisit j = rJ.getNodeAt(move.getJ());
for (int p = 0; p < rI.getVehicle().getCompartmentCount(); p++) {
if (rI.getLoad(p) - i.getDemand(p) + j.getDemand(p) > rI.getVehicle().getCapacity(p)) {
return false;
}
if (rJ.getLoad(p) - j.getDemand(p) + i.getDemand(p) > rJ.getVehicle().getCapacity(p)) {
return false;
}
}
return true;
}
/**
* Capacity check for route merge move
*
* @param mSolution
* @param move
* @return <code>true</code> if the move is feasible
*/
public boolean checkRouteMerge(IVRPSolution<?> solution, RouteMergingMove move) {
for (int p = 0; p < move.tailRoute.getVehicle().getCompartmentCount(); p++) {
if (move.tailRoute.getLoad() + move.headRoute.getLoad() > move.tailRoute.getVehicle()
.getCapacity()) {
return false;
}
}
return true;
}
/**
* Capacity check for 2-opt move
*
* @param mSolution
* @param move
* @return <code>true</code> if the move is feasible
*/
public boolean check2Opt(IVRPSolution<?> solution, TwoOptMove move) {
if (move.getRouteI() == move.getRouteJ()) {
// Assumes that the route is already feasible
return true;
}
IRoute<?> rI = solution.getRoute((move).getRouteI());
IRoute<?> rJ = solution.getRoute((move).getRouteJ());
int i = (move).getI();
int j = (move).getJ();
double[] startI = new double[rI.getVehicle().getCompartmentCount()];
double[] endI = new double[startI.length];
double[] startJ = new double[startI.length];
double[] endJ = new double[startI.length];
for (int p = 0; p < startI.length; p++) {
int node = 0;
Iterator<? extends INodeVisit> it = rI.iterator();
// Accumulated load of the first route up to nodeI
while (node <= i && it.hasNext()) {
startI[p] += it.next().getDemand(p);
node++;
}
// Accumulated load of the first route from nodeI+1 to depot
while (it.hasNext()) {
endI[p] += it.next().getDemand(p);
node++;
}
node = 0;
it = rJ.iterator();
// Accumulated load of the second route up to nodeJ
while (node <= j && it.hasNext()) {
startJ[p] += it.next().getDemand(p);
node++;
}
// Accumulated load of the second route from to nodeJ+1 to depot
while (it.hasNext()) {
endJ[p] += it.next().getDemand(p);
node++;
}
}
// Capacity check
// standard 2-opt
// nodeI linked with nodeJ
// nodeI+1 linked with nodeJ+1
if (!(move).isStar()) {
// Route I : startI + startJ
// Route J : endI + endJ
for (int p = 0; p < startI.length; p++) {
if (startI[p] + startJ[p] > rI.getVehicle().getCapacity(p)
|| endI[p] + endJ[p] > rJ.getVehicle().getCapacity(p)) {
return false;
}
}
}
// special 2-opt*
// nodeI linked with nodeJ+1
// nodeI+1 linked with nodeJ
else {
// Route I : startI + endJ
// Route J : startJ + endI
for (int p = 0; p < startI.length; p++) {
if (startI[p] + endJ[p] > rI.getVehicle().getCapacity(p)
|| startJ[p] + endI[p] > rJ.getVehicle().getCapacity(p)) {
return false;
}
}
}
return true;
}
/*
* (non-Javadoc)
* @see vroom.common.heuristics.IConstraint#checkSolution(java.lang.Object)
*/
@Override
public boolean isFeasible(S solution) {
double[] load;
for (IRoute<?> route : solution) {
load = new double[route.getVehicle().getCompartmentCount()];
for (int p = 0; p < load.length; p++) {
for (INodeVisit n : route) {
load[p] += n.getDemand(p);
if (load[p] > route.getVehicle().getCapacity(p)) {
return false;
}
}
}
}
return true;
}
/*
* (non-Javadoc)
* @see vroom.common.heuristics.IConstraint#getInfeasibilityExplanation(java. lang.Object)
*/
@Override
public String getInfeasibilityExplanation(S solution) {
double[] load;
StringBuilder sb = new StringBuilder();
for (IRoute<?> route : solution) {
load = new double[route.getVehicle().getCompartmentCount()];
boolean stop = false;
for (INodeVisit n : route) {
for (int p = 0; p < load.length; p++) {
load[p] += n.getDemand(p);
if (load[p] > route.getVehicle().getCapacity(p)) {
if (sb.length() > 0) {
sb.append(", ");
}
sb.append(String.format(
"Capacity Ctr: route:%s product:%s load:%s (cap:%s, node:%s)", route.hashCode(),
p, load[p], route.getVehicle().getCapacity(p), n.getID()));
break;
}
}
if (stop) {
break;
}
}
}
return sb.length() > 0 ? sb.toString() : null;
}
/*
* (non-Javadoc)
* @see vroom.common.heuristics.IConstraint#getInfeasibilityExplanation(java. lang.Object, vroom.common.heuristics.Move)
*/
@Override
public String getInfeasibilityExplanation(S solution, IMove move) {
// StringBuilder sb = new StringBuilder();
//
// return sb.length()>0?sb.toString():null;
if (!isFeasible(solution, move)) {
return String.format("move:%s violates capacity constraint", move);
} else {
return null;
}
}
}