/**
*
*/
package vroom.trsp.optimization.constraints;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import vroom.common.heuristics.ConstraintHandler;
import vroom.common.utilities.IntegerSet;
import vroom.common.utilities.Utilities;
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.TRSPSolution;
import vroom.trsp.datamodel.TRSPTour;
/**
* <code>TourConstraintHandler</code> is an extension of {@link ConstraintHandler} that takes into account the
* specificities of {@link ITourConstraint}.
* <p>
* Creation date: Apr 7, 2011 - 5:07:44 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 TourConstraintHandler extends ConstraintHandler<ITRSPTour> implements ITourConstraint {
/**
* Creates a new <code>TourConstraintHandler</code> with the given constraints.
* <p>
* Please note that constraints will be checked in the same order they were added
* </p>
*
* @param ctr
* a set of constraints to add to the new handler
*/
public TourConstraintHandler(ITourConstraint... ctr) {
super(ctr);
}
/**
* Creates a new constraint handler for the specified instance.
*
* @param instance
* @return a constraint handler containing the constraints that apply to {@code instance}
*/
public static TourConstraintHandler newConstraintHandler(TRSPInstance instance) {
ArrayList<ITourConstraint> ctr = new ArrayList<>();
if (instance.isDynamic())
ctr.add(new ServicedRequestsConstraint());
if (!instance.isCVRPTW()) {
ctr.add(new HomeConstraint());
if (instance.getSkillCount() > 0)
ctr.add(new SkillsConstraint());
ctr.add(new TWConstraint());
if (instance.getToolCount() > 0)
ctr.add(new ToolsConstraint());
} else {
ctr.add(new TWConstraint());
}
if (instance.getMaxTourDuration() < Double.POSITIVE_INFINITY)
ctr.add(new MaxDurationConstraint(instance.getMaxTourDuration()));
if (instance.getSpareCount() > 0)
ctr.add(new SparePartsConstraint());
return new TourConstraintHandler(ctr.toArray(new ITourConstraint[ctr.size()]));
}
/**
* Check the feasibility of a solution
*
* @param solution
* the solution to be checked
* @param allowUnserved
* {@code true} if unserved requests are allowed, {@code false} if a solution with unserved requests is
* considered as infeasible
* @return {@code true} if the {@code solution} is feasible, {@code false} otherwise
*/
public boolean isFeasible(TRSPSolution solution, boolean allowUnserved) {
if (!allowUnserved && solution.getUnservedCount() != 0)
return false;
for (TRSPTour trspTour : solution) {
if (!isFeasible(trspTour))
return false;
}
return true;
}
@Override
public int firstInfeasibleNode(ITRSPTour tour) {
IntegerSet infeasibleNodes = new IntegerSet(tour.getSolution().getInstance().getMaxId());
for (IConstraint<ITRSPTour> c : this) {
if (c instanceof ITourConstraint) {
int inf = ((ITourConstraint) c).firstInfeasibleNode(tour);
if (inf != ITRSPTour.UNDEFINED)
infeasibleNodes.add(inf);
}
}
if (infeasibleNodes.size() == 0)
return ITRSPTour.UNDEFINED;
else if (infeasibleNodes.size() == 1)
return infeasibleNodes.iterator().next();
else {
for (Integer i : tour)
if (infeasibleNodes.contains(i))
return i;
return ITRSPTour.UNDEFINED;
}
}
@Override
public int checkFeasibility(ITRSPTour tour, IMove move) {
int state = 3;
for (IConstraint<ITRSPTour> c : this) {
if (c instanceof ITourConstraint) {
int feasibility = ((ITourConstraint) c).checkFeasibility(tour, move);
state &= feasibility;
if (state == 0)
return state;
}
}
return state;
}
/**
* Check the feasibility of a move for a particular tour.
* <p>
* If <code>previousState</code> is <code>null</code>, this method will stop constraint checking as soon as the
* infeasibility of the move is proven, if not it will stop when the move is proven to be both infeasible and
* forward infeasible.
*
* @param tour
* the tour at hand
* @param move
* the move to be checked
* @param previousState
* the state returned by a previous call to this method, or <code>null</code>
* @return the feasibility state of the move.
* @see ITourConstraint#checkFeasibility(ITRSPTour, IMove)
*/
public FeasibilityState checkFeasibility(ITRSPTour tour, IMove move,
FeasibilityState previousState) {
FeasibilityState feasState = null;
if (previousState == null) {
// First pass: check all constraints until the infeasibility of the move is detected
feasState = new FeasibilityState(size());
for (IConstraint<ITRSPTour> c : this) {
if (feasState.isFeasible()) {
if (c instanceof ITourConstraint) {
feasState.updateState(((ITourConstraint) c).checkFeasibility(tour, move));
} else {
feasState.updateFeasible(c.isFeasible(tour, move));
}
} else {
feasState.addUncheckedConstraint(c);
}
}
} else {
// Second pass: check all remaining constraints
feasState = previousState;
Iterator<IConstraint<ITRSPTour>> it = feasState.getUncheckedConstraints().iterator();
while (feasState.continueCtrCheck() && it.hasNext()) {
IConstraint<ITRSPTour> c = it.next();
if (c instanceof ITourConstraint) {
feasState.updateState(((ITourConstraint) c).checkFeasibility(tour, move));
} else {
feasState.updateFeasible(c.isFeasible(tour, move));
}
it.remove();
}
}
return feasState;
}
public static final class FeasibilityState {
private boolean mFeasible;
private boolean mForwardFeasible;
private final List<IConstraint<ITRSPTour>> mUncheckedConstraints;
/**
* Returns <code>true</code> if the move is feasible, <code>false</code> otherwise
*
* @return <code>true</code> if the move is feasible, <code>false</code> otherwise
*/
public boolean isFeasible() {
return mFeasible;
}
/**
* Update this state
*
* @param stateUpdate
*/
private void updateState(int stateUpdate) {
updateFeasible(stateUpdate % 2 == 1);
updateForwardFeasible(stateUpdate >= 2);
}
/**
* Returns <code>true</code> if the move is forward feasible, <code>false</code> otherwise
*
* @return <code>true</code> if the move is forward feasible, <code>false</code> otherwise
* @see ITourConstraint#checkFeasibility(ITRSPTour, IMove)
*/
public boolean isForwardFeasible() {
return mForwardFeasible;
}
/**
* Update the feasible flag
*
* @param feasible
*/
private void updateFeasible(boolean feasible) {
mFeasible &= feasible;
}
/**
* Update the forward feasible flag
*
* @param feasible
*/
private void updateForwardFeasible(boolean forwardFeasible) {
mForwardFeasible &= forwardFeasible;
}
/**
* Return <code>true</code> if the constraint checking process should continue (no definitive result),
* <code>false</code> otherwise
*
* @return <code>true</code> if the constraint checking process should continue (no definitive result),
* <code>false</code> otherwise
*/
private boolean continueCtrCheck() {
return mFeasible || mForwardFeasible;
}
/**
* Add a constraint to the set of unchecked constraints
*
* @param ctr
*/
private void addUncheckedConstraint(IConstraint<ITRSPTour> ctr) {
mUncheckedConstraints.add(ctr);
}
/**
* Returns the list of unchecked constraints
*
* @return the list of unchecked constraints
*/
private List<IConstraint<ITRSPTour>> getUncheckedConstraints() {
return mUncheckedConstraints;
}
/**
* Creates a new <code>FeasibilityState</code>
*/
public FeasibilityState(int ctrCount) {
mFeasible = true;
mForwardFeasible = true;
mUncheckedConstraints = new ArrayList<IConstraint<ITRSPTour>>(ctrCount);
}
@Override
public String toString() {
return String.format("feas:%s,fwdFeas:%s,unchecked:%s", isFeasible(),
isForwardFeasible(), Utilities.toShortString(getUncheckedConstraints()));
}
}
}