/**
*
*/
package vroom.trsp.datamodel.costDelegates;
import java.util.List;
import java.util.ListIterator;
import vroom.common.utilities.optimization.IMove;
import vroom.trsp.datamodel.ITRSPTour;
import vroom.trsp.datamodel.TRSPSolution;
import vroom.trsp.datamodel.TRSPTour;
import vroom.trsp.optimization.InsertionMove;
import vroom.trsp.optimization.RemoveMove;
import vroom.trsp.optimization.biobj.LevenshteinPR.AtomicPRMove;
import vroom.trsp.optimization.biobj.LevenshteinPR.PRMove;
import vroom.trsp.optimization.localSearch.TRSPShift.TRSPShiftMove;
import vroom.trsp.optimization.localSearch.TRSPTwoOpt.TRSPTwoOptMove;
import vroom.trsp.optimization.mpa.DTRSPSolution;
/**
* <code>TRSPCostDelegate</code> is an interface for classes that will be responsible for the calculation and upate of
* {@link TRSPTour} costs
* <p>
* Creation date: Feb 23, 2011 - 4:07:12 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 abstract class TRSPCostDelegate {
/**
* the penalize unserved request flag: <code>true</code> if a cost penalty should be applied when requests are
* unserved
**/
private boolean mUnservedPenalized;
/**
* Getter for the penalize unserved request flag
*
* @return <code>true</code> if a cost penalty should be applied when requests are unserved
*/
public boolean isUnservedPenalized() {
return this.mUnservedPenalized;
}
/**
* Setter for the penalize unserved request flag.
*
* @param penalize
* <code>true</code> if a cost penalty should be applied when requests are unserved
*/
public void setPenalize(boolean penalize) {
this.mUnservedPenalized = penalize;
}
/** the penalty added for each unserved request **/
private double mUnservedPenalty;
/**
* Getter for the penalty added for each unserved request
*
* @return the value of unservedPenalty
*/
public double getUnservedPenalty() {
return this.mUnservedPenalty;
}
/**
* Sets the penalty added for each unserved request to {@code 0}
*/
public synchronized void unsetUnservedPenalty() {
this.mUnservedPenalty = 0;
}
/**
* Sets the penalty added for each unserved request
*
* @param solution
* the reference solution which cost will be used to evaluate the penalty
* @param gamma
* the scaling factor for the unserved penalty
*/
public synchronized void setUnservedPenalty(TRSPSolution solution, double gamma) {
unsetUnservedPenalty();
this.mUnservedPenalty = evaluateSolution(solution, true, false) * gamma;
}
/**
* Creates a new <code>TRSPCostDelegate</code>
*/
public TRSPCostDelegate() {
mUnservedPenalized = false;
mUnservedPenalty = 0;
}
/**
* Evaluates the penalty associated with a solution
*
* @param solution
* the solution to be evaluated
* @return the penalty associated with <code>solution</code>, or 0 if the penalty is disabled
*/
protected double evaluatePenalty(TRSPSolution solution) {
return isUnservedPenalized() ? solution.getUnservedCount() * getUnservedPenalty() : 0;
}
/**
* Evaluate the cost of a solution
*
* @param solution
* the solution to be evaluated
* @param evaluateTours
* <code>true</code> if the tour cost should be {@linkplain #evaluateTour(TRSPTour, boolean)
* re-evaluated}, or <code>false</code> if the store {@linkplain TRSPTour#getTotalCost() cost} should be
* used
* @param updateTours
* see {@link #evaluateTour(TRSPTour, boolean)}, only applies if <code>evaluateTours=true</code>
* @return the cost of <code>solution</code>
*/
public double evaluateSolution(TRSPSolution solution, boolean evaluateTours, boolean updateTours) {
double cost = 0;
for (TRSPTour t : solution)
cost += evaluateTours ? evaluateTour(t, updateTours) : t.getTotalCost();
cost += evaluatePenalty(solution);
return cost;
}
/**
* Evaluate the cost of a tour, ignoring any previously stored values.
*
* @param tour
* the tour that will be reevaluated
* @param updateTour
* <code>true</code> if the tour stored costs have to be updated
* @return the total cost of the tour
*/
public double evaluateTour(ITRSPTour tour, boolean updateTour) {
double cost = 0;
// Ignore empty tours
if (tour.length() != 0) {
if (tour instanceof TRSPTour)
cost = evaluateTRSPTour((TRSPTour) tour, ITRSPTour.UNDEFINED, updateTour);
else
cost = evaluateGenericTour(tour);
}
if (updateTour)
tour.setTotalCost(cost);
return cost;
}
/**
* Evaluate a tour
*
* @param tour
* the tour to be evaluated
* @return the total cost of the tour
*/
protected abstract double evaluateGenericTour(ITRSPTour tour);
/**
* Evaluate a portion of the {@link TRSPTour} starting at the given <code>node</code>
* <p>
* Implementations must ensure that if <code>node</code> is equal to {@link ITRSPTour#UNDEFINED} then the whole tour
* will be reevaluated
* </p>
*
* @param tour
* the tour to be evaluated
* @param node
* the node at which reevaluation should start
* @param updateTour
* <code>true</code> if the tour stored costs have to be updated
* @return the total cost of the tour
*/
protected abstract double evaluateTRSPTour(TRSPTour tour, int node, boolean updateTour);
/**
* Updates the <code>tour</code> cost after a node was inserted.
*
* @param tour
* the modified tour
* @param predecessor
* the predecessor, <code>null</code> if the node was inserted at the beginning
* @param node
* the inserted node
* @param successor
* the successor, <code>null</code> if the node was appended
*/
public void nodeInserted(TRSPTour tour, int predecessor, int node, int successor) {
if (tour.isAutoUpdated())
evaluateTRSPTour(tour, predecessor, true);
}
/**
* Updates the {@code tour} after a node was {@link DTRSPSolution#freeze() frozen}
*
* @param tour
* the affected tour
* @param node
*/
public void nodeFrozen(TRSPTour tour, int node) {
}
/**
* Updates the <code>tour</code> cost after a tour was inserted.
*
* @param tour
* the modified tour
* @param predecessor
* the predecessor, <code>null</code> if the modified tour was inserted at the beginning
* @param insertedTour
* the inserted tour
* @param successor
* the successor, <code>null</code> if the modified tour was appended
*/
public void tourInserted(TRSPTour tour, int predecessor, List<Integer> insertedTour,
int successor) {
if (tour.isAutoUpdated())
evaluateTRSPTour(tour, predecessor, true);
}
/**
* Updates the <code>tour</code> cost after a node was removed.
*
* @param tour
* the modified tour
* @param predecessor
* the predecessor, <code>null</code> if the first node was removed
* @param node
* the removed node
* @param successor
* the successor, <code>null</code> if the last node was removed
*/
public void nodeRemoved(TRSPTour tour, int predecessor, int node, int successor) {
if (tour.isAutoUpdated())
evaluateTRSPTour(tour, predecessor, true);
}
/**
* Updates the <code>tour</code> cost after a node was replaced.
*
* @param tour
* the modified tour
* @param predecessor
* the predecessor, <code>null</code> if the first node was replaced
* @param previousNode
* the node that was replaced
* @param node
* the new node
* @param successor
* the successor, <code>null</code> if the last node was replaced
*/
public void nodeReplaced(TRSPTour tour, int predecessor, int previousNode, int node,
int successor) {
if (tour.isAutoUpdated())
evaluateTRSPTour(tour, predecessor, true);
}
/**
* Updates the <code>tour</code> cost after two nodes were swapped.
* <p>
* Note that <code>node1</code> have to appear before <code>node2</code> in the tour
* </p>
*
* @param tour
* the modified tour
* @param pred1
* the predecessor of the first node
* @param node1
* the first node
* @param succ1
* the successor of the first node
* @param pred2
* the predecessor of the second node
* @param node2
* the second node
* @param succ2
* the successor of the second node
*/
public void nodesSwapped(TRSPTour tour, int pred1, int node1, int succ1, int pred2, int node2,
int succ2) {
if (tour.isAutoUpdated())
evaluateTRSPTour(tour, pred1, true);
}
/**
* Updates the <code>tour</code> cost after a node shift
*
* @param tour
* the modified tour
* @param node
* the shifted node
* @param pred
* the former predecessor of the shifted <code>node</code>
* @param forward
* <code>true</code> if the shift was forward, <code>false</code> otherwise
*/
public void nodeShifted(TRSPTour tour, int node, int pred, boolean forward) {
if (tour.isAutoUpdated())
evaluateTRSPTour(tour, forward ? pred : tour.getPred(node), true);
}
/**
* Updates the <code>tour</code> cost after a subtour was removed.
*
* @param tour
* the modified tour
* @param predecessor
* the predecessor, <code>null</code> if the first node was removed
* @param removedTour
* the removed tour
* @param successor
* the successor, <code>null</code> if the last node was removed
*/
public void subtourRemoved(TRSPTour tour, int predecessor, List<Integer> removedTour,
int successor) {
if (tour.isAutoUpdated())
evaluateTRSPTour(tour, predecessor, true);
}
/**
* Updates the <code>tour</code> cost after a subtour was reversed.
*
* @param tour
* the modified tour
* @param predecessor
* the predecessor, <code>null</code> if the first node was included
* @param first
* the first node of the reversed subtour
* @param last
* the last node of the reversed subtour
* @param successor
* the successor, <code>null</code> if the last node was included
*/
public void subtourReversed(TRSPTour tour, int predecessor, int first, int last, int successor) {
if (tour.isAutoUpdated())
evaluateTRSPTour(tour, predecessor, true);
}
/**
* Gets the detour cost of a node.
*
* @param tour
* the considered tour
* @param i
* the predecessor, {@link ITRSPTour#UNDEFINED} if head insertion
* @param n
* the inserted node
* @param j
* the successor, {@link ITRSPTour#UNDEFINED} if tail insertion
* @param isRemoval
* <code>true</code> if the evaluation corresponds to the removal of <code>node</code>,
* <code>false</code> otherwise.
* @return the detour cost (which sign is expected to be positive and is independent of the <code>isRemoval</code>
* argument)
*/
public abstract double evaluateDetour(ITRSPTour tour, int i, int n, int j, boolean isRemoval);
/**
* Evaluates the {@link IMove#getImprovement()} of a move
*
* @param move
* the move to be evaluated
* @return the {@linkplain IMove#getImprovement() improvement} resulting from the execution of the specified
* <code>move</code> to the given <code>tour</code>
* @throws UnsupportedOperationException
* if the <code>move</code> is not supported
*/
public double evaluateMove(IMove move) throws UnsupportedOperationException {
double imp;
if (move instanceof InsertionMove)
imp = evaluateInsMove((InsertionMove) move);
else if (move instanceof PRMove)
imp = evaluatePRMove((PRMove) move);
else if (move instanceof TRSPShiftMove)
imp = evaluateShiftMove((TRSPShiftMove) move);
else if (move instanceof TRSPTwoOptMove)
imp = evaluateTwoOptMove((TRSPTwoOptMove) move);
else if (move instanceof RemoveMove)
imp = evaluateRemMove((RemoveMove) move);
else
throw new UnsupportedOperationException(String.format(
"Move %s is not supported by this cost delegate (%s)", move, this.getClass()
.getSimpleName()));
move.setImprovement(imp);
return imp;
}
/**
* Evaluate a {@link RemoveMove}.
* <p>
* Subclasses should override this method, which is called in {@link #evaluateMove(IMove)}.
* </p>
*
* @param move
* the move to be evaluated
* @return the evaluation of the move
*/
protected double evaluateRemMove(RemoveMove move) {
throw new UnsupportedOperationException(String.format(
"Move %s is not supported by this cost delegate (%s)", move, this.getClass()
.getSimpleName()));
}
/**
* Evaluate a {@link TRSPTwoOptMove}.
* <p>
* Subclasses should override this method, which is called in {@link #evaluateMove(IMove)}.
* </p>
*
* @param move
* the move to be evaluated
* @return the evaluation of the move
*/
protected double evaluateTwoOptMove(TRSPTwoOptMove move) {
throw new UnsupportedOperationException(String.format(
"Move %s is not supported by this cost delegate (%s)", move, this.getClass()
.getSimpleName()));
}
/**
* Evaluate a {@link TRSPShiftMove}.
* <p>
* Subclasses should override this method, which is called in {@link #evaluateMove(IMove)}.
* </p>
*
* @param move
* the move to be evaluated
* @return the evaluation of the move
*/
protected double evaluateShiftMove(TRSPShiftMove move) {
throw new UnsupportedOperationException(String.format(
"Move %s is not supported by this cost delegate (%s)", move, this.getClass()
.getSimpleName()));
}
/**
* Evaluate a {@link InsertionMove}.
* <p>
* Subclasses should override this method, which is called in {@link #evaluateMove(IMove)}.
* </p>
*
* @param move
* the move to be evaluated
* @return the evaluation of the move
*/
protected double evaluateInsMove(InsertionMove move) {
throw new UnsupportedOperationException(String.format(
"Move %s is not supported by this cost delegate (%s)", move, this.getClass()
.getSimpleName()));
}
/**
* Evaluate a {@link PRMove}.
*
* @param move
* the move to be evaluated
* @return the evaluation of the move
*/
protected double evaluatePRMove(PRMove move) {
double imp = 0;
for (AtomicPRMove mve : move.getAtomicMoves()) {
switch (mve.getType()) {
case INS:
if (mve.getInsertedNodes().size() == 1) {
InsertionMove ins = new InsertionMove(mve.getNewReq(), mve.getTour(),
Double.NaN, mve.getEditedReq(), mve.getTour().getSucc(
mve.getEditedReq()));
imp += evaluateInsMove(ins);
} else {
ListIterator<Integer> it = mve.getInsertedNodes().listIterator(
mve.getInsertedNodes().size());
// Head insertion
int node = it.previous();
int pred = mve.getEditedReq();
int succ = it.previous();
imp += evaluateInsMove(new InsertionMove(node, mve.getTour(), Double.NaN, pred,
succ));
while (it.hasPrevious()) {
pred = node;
node = succ;
succ = it.previous();
imp += evaluateInsMove(new InsertionMove(node, mve.getTour(), Double.NaN,
pred, succ));
}
pred = node;
node = succ;
succ = mve.getTour().getSucc(mve.getEditedReq());
imp += evaluateInsMove(new InsertionMove(node, mve.getTour(), Double.NaN, pred,
succ));
}
break;
case DEL:
imp += evaluateRemMove(new RemoveMove(mve.getEditedReq(), mve.getTour()));
break;
case SUB:
imp += evaluateRemMove(new RemoveMove(mve.getEditedReq(), mve.getTour()));
imp += evaluateInsMove(new InsertionMove(mve.getNewReq(), mve.getTour(),
Double.NaN, mve.getTour().getPred(mve.getEditedReq()), mve.getTour()
.getSucc(mve.getEditedReq())));
break;
default:
throw new UnsupportedOperationException("Unsupported edit type :" + mve.getType());
}
}
return imp;
}
/**
* Returns <code>true</code> if the cost of an insertion depends on the sequence preceding the insertion.
*
* @return <code>true</code> if the cost of an insertion depends on the sequence preceding the insertion.
*/
public abstract boolean isInsertionSeqDependent();
@Override
public String toString() {
return String.format("%s(p:%s|%.3f)", this.getClass().getSimpleName(),
isUnservedPenalized(), getUnservedPenalty());
}
}