/**
*
*/
package vroom.trsp.optimization.constraints;
import java.util.Arrays;
import vroom.common.utilities.optimization.IConstraint;
import vroom.common.utilities.optimization.IMove;
import vroom.trsp.datamodel.ITRSPTour;
import vroom.trsp.datamodel.TRSPInstance;
import vroom.trsp.datamodel.TRSPTour;
import vroom.trsp.datamodel.TRSPTour.TRSPTourIterator;
import vroom.trsp.optimization.InsertionMove;
import vroom.trsp.optimization.localSearch.TRSPShift.TRSPShiftMove;
import vroom.trsp.optimization.localSearch.TRSPTwoOpt.TRSPTwoOptMove;
import vroom.trsp.util.TRSPLogging;
/**
* <code>SparePartsConstraint</code> is an implementation of {@link IConstraint} that checks if moves are feasible in
* terms of spare parts constraints.
* <p>
* Creation date: Mar 28, 2011 - 10:32:52 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 SparePartsConstraint extends TourConstraintBase {
/**
* Check the feasibility of a move
*
* @param tour
* @param move
* @return 1 if move is feasible, -1 if the technician does not have the spare parts and no visit to depot is
* planned, -2 if the spare part constraint is violated at one of the successors
*/
private int checkInsFeasibility(ITRSPTour itour, InsertionMove move) {
if (!TRSPTour.class.isAssignableFrom(itour.getClass())) {
TRSPLogging
.getOptimizationLogger()
.warn(String
.format("SparePartsConstraint.checkInsFeasibility: unsupported tour class (%s)",
itour.getClass()));
return 1;
}
TRSPTour tour = (TRSPTour) itour;
if (move.isDepotTrip() || !tour.getInstance().isRequest(move.getNodeId()))
return 1;
if (!tour.isMainDepotVisited()
&& !move.isDepotTrip()
&& !tour.getInstance().hasRequiredSpareParts(tour.getTechnicianId(),
move.getNodeId()))
return -1;
int pred = move.getInsertionPred();
TRSPInstance instance = tour.getInstance();
for (int s = 0; s < instance.getSpareCount(); s++) {
// Available spare parts
int av = tour.getAvailableSpareParts(pred, s) // Will return the initial spare parts if pred ==
// UNDEFINED
- itour.getInstance().getRequest(move.getNodeId()).getSparePartRequirement(s);
if (tour.getRequiredSpareParts(move.getInsertionSucc(), s) > av)
return -2;
}
return 1;
}
@Override
protected boolean isInsFeasible(ITRSPTour tour, InsertionMove move) {
return checkInsFeasibility(tour, move) == 1;
}
@Override
protected boolean isTwoOptFeasible(ITRSPTour itour, TRSPTwoOptMove move) {
if (!TRSPTour.class.isAssignableFrom(itour.getClass())) {
TRSPLogging.getOptimizationLogger().warn(
String.format(
"SparePartsConstraint.isTwoOptFeasible: unsupported tour class (%s)",
itour.getClass()));
return true;
}
TRSPTour tour = (TRSPTour) itour;
if (!tour.isMainDepotVisited() // The main depot is not visited, the tour is already feasible
|| tour.isMainDepotVisited(tour.getSucc(move.getFirst())) // The visit to the main depot is done before
// the first
// affected node
|| !tour.isMainDepotVisited(move.getSecond())) // The visit to the main depot is done
// after the last affected node
return true;
// The main depot is visited between j and m
TRSPTourIterator it = tour.iterator(move.getSecond());
// Spare parts available at node i
int[] spares = new int[tour.getInstance().getSpareCount()];
for (int s = 0; s < spares.length; s++) {
spares[s] = tour.getAvailableSpareParts(move.getFirst(), s);
}
// Start with node m and iterate backward
int node = it.previous();
while (it.hasNext() && node != tour.getMainDepotId()) {
for (int s = 0; s < spares.length; s++) {
spares[s] -= tour.getRequiredSpareParts(node, s);
if (spares[s] < 0)
// The technician does not have the required spare parts for this request
return false;
}
}
// No incompatible request was found, the move is feasible
return true;
}
@Override
protected boolean isShiftFeasible(ITRSPTour itour, TRSPShiftMove move) {
if (!TRSPTour.class.isAssignableFrom(itour.getClass())) {
TRSPLogging.getOptimizationLogger().warn(
String.format(
"SparePartsConstraint.isShiftFeasible: unsupported tour class (%s)",
itour.getClass()));
return true;
}
TRSPTour tour = (TRSPTour) itour;
if (!tour.isMainDepotVisited()) {
// The main depot is not visited, we assume that the tour is and will remain feasible
return true;
} else {
// The main depot is visited
if (move.getNode() == tour.getMainDepotId()) {
// The shifted node is the main depot
if (!move.isForward()) {
// A backward move of the main depot is always feasible
return true;
} else {
// Check if the requests between the current depot position and its new one require a visit to
// the depot to be served
for (int p = 0; p < tour.getInstance().getSpareCount(); p++) {
// The total requirement for this part
int reqTotal = tour.getRequiredSpareParts(tour.getFirstNode(), p);
// The requirement for the subtour that will be after the main depot visit
int reqFinal = tour.getRequiredSpareParts(move.getNewSucc(), p);
// Check if the technician has the spare parts parts for the subtour that will be before the
// main depot visit
if (reqTotal - reqFinal > tour.getTechnician().getAvailableSpareParts(p))
return false;
}
return true;
}
} else {
int pred = move.getNewSucc() != ITRSPTour.UNDEFINED ? tour.getPred(move
.getNewSucc()) : tour.getLastNode();
if (tour.isMainDepotVisited(move.getNode()) == tour.isMainDepotVisited(pred)) {
// Both the shifted node and its new predecessor are on the same "side" of the tour (either before
// or after the depot visit), the move is therefore feasible
return true;
} else {
// The shifted node and its new predecessor are on different "side" of the tour (one is before
// while the other is after the depot visit)
if (move.isForward()) {
// The node will be after the depot visit, the move is therefore feasible
return true;
} else {
// The node is shifted before the depot visit, check if the technician will have the
// required spare parts for the nodes in between
for (int p = 0; p < tour.getInstance().getSpareCount(); p++) {
// The requirement for the whole tour
int reqTotal = tour.getRequiredSpareParts(tour.getFirstNode(), p);
// The requirement for the subtour that is after the main depot visit
int reqFinal = tour.getRequiredSpareParts(tour.getMainDepotId(), p);
// Check if the technician has the spare parts parts for the subtour that will be before the
// main depot visit
if (reqTotal
- reqFinal
+ tour.getInstance().getRequest(move.getNode())
.getSparePartRequirement(p) > tour.getTechnician()
.getAvailableSpareParts(p))
return false;
}
return true;
}
}
}
}
}
@Override
protected FeasibilityState checkFeasibility(ITRSPTour tour) {
TRSPInstance instance = tour.getInstance();
int[] spare = tour.getInstance().getFleet().getVehicle(tour.getTechnicianId())
.getSpareParts();
for (int node : tour) {
if (instance.isMainDepot(node)) {
Arrays.fill(spare, Integer.MAX_VALUE);
} else if (!instance.isDepot(node)) {
for (int i = 0; i < spare.length; i++) {
spare[i] -= instance.getRequest(node).getSparePartRequirement(i);
if (spare[i] < 0) {
return new FeasibilityState(node,
"Missing spare part %s to serve request %s", i, node);
}
}
}
}
return new FeasibilityState();
}
@Override
public int checkFeasibility(ITRSPTour tour, IMove move) {
// We assume that the tour data is updated and coherent
if (move instanceof InsertionMove) {
int feasibilty = checkInsFeasibility(tour, (InsertionMove) move);
return (feasibilty == 1 ? 1 : 0) +
// -1 if the technician does not have the spare parts and no visit to depot is planned
(feasibilty != -1 ? 2 : 0);
} else {
return isFeasible(tour, move) ? 3 : 1;
}
}
}