/**
*
*/
package vroom.trsp.optimization.localSearch;
import java.util.LinkedList;
import java.util.List;
import vroom.common.heuristics.NeighborhoodBase;
import vroom.common.utilities.optimization.IInstance;
import vroom.common.utilities.optimization.IMove;
import vroom.common.utilities.optimization.IParameters;
import vroom.trsp.datamodel.ITRSPTour;
import vroom.trsp.datamodel.ITourIterator;
import vroom.trsp.datamodel.TRSPDetailedSolutionChecker;
import vroom.trsp.datamodel.TRSPTour;
import vroom.trsp.optimization.TRSPMove;
import vroom.trsp.optimization.constraints.TourConstraintHandler;
import vroom.trsp.optimization.localSearch.TRSPShift.TRSPShiftMove;
import vroom.trsp.util.TRSPLogging;
/**
* <code>TRSPShift</code> is an implementation of the <code>shift</code> neighborhood for the TRSP problem and data
* model.
* <p>
* Creation date: Mar 17, 2011 - 1:30:00 PM
* </p>
*
* @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 TRSPShift extends NeighborhoodBase<ITRSPTour, TRSPShiftMove> {
/**
* <code>TRSPShiftMove</code> is a representation a <code>shift</code> move for the TRSP.
* <p>
* Creation date: Mar 17, 2011 - 1:45:02 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 static class TRSPShiftMove extends TRSPMove {
/** The id of the shifted node */
private final int mNode;
/** The id of the node new successor */
private final int mNewSucc;
/** <code>true</code> if the shift is forward, <code>false</code> if it is backward */
private final boolean mForward;
private List<Integer> mChangedSequence;
/**
* Creates a new <code>TRSPShiftMove</code>.
*
* @param tour
* the tour on which the new move will be defined
* @param improvement
* the improvement resulting from this move
* @param node
* the first node
* @param newSucc
* the second node
*/
protected TRSPShiftMove(TRSPTour tour, double improvement, int node, int newSucc,
boolean forward) {
super(improvement, tour);
mNode = node;
mNewSucc = newSucc;
mForward = forward;
mChangedSequence = null;
}
/**
* Getter for <code>node</code>
*
* @return the node
*/
public int getNode() {
return mNode;
}
/**
* Getter for <code>newSucc</code>
*
* @return the newSucc
*/
public int getNewSucc() {
return mNewSucc;
}
/**
* Getter for <code>forward</code>
*
* @return the forward
*/
public boolean isForward() {
return mForward;
}
@Override
public String getMoveName() {
return "shift";
}
/**
* Build the new sequence of node that will result from this <code>1-shift</code> move. Nodes which visit order
* in unchanged are ignored, except the immediate predecessor of the first changed node, which will appear
* first.
* <p>
* For performance reasons this sequence is stored after the first call. The tour should therefore not be
* modified between two successive calls
* </p>
*
* @return a list containing the changed sequence resulting from the execution of this <code>1-shift</code> move
* on the given <code>tour</code>.
*/
public synchronized List<Integer> getChangedSequence() {
TRSPTour tour = (TRSPTour) getTour();
if (mChangedSequence != null)
return mChangedSequence;
mChangedSequence = new LinkedList<Integer>();
if (isForward()) {
mChangedSequence.add(tour.getPred(getNode()));
ITourIterator it = tour.iterator(tour.getSucc(getNode()));
while (it.hasNext()) {
int next = it.next();
if (next == getNewSucc()) // Insert the node
mChangedSequence.add(getNode());
mChangedSequence.add(next);
}
// if newSucc==UNDEFINED it means the node is moved to the end of the mTour
if (getNewSucc() == ITRSPTour.UNDEFINED)
mChangedSequence.add(getNode());
} else {
// changedSequence.add(mTour.getPred(node));
mChangedSequence.add(tour.getPred(getNewSucc()));
mChangedSequence.add(getNode());
ITourIterator it = tour.iterator(getNewSucc());
while (it.hasNext()) {
int next = it.next();
if (next != getNode()) // Skip the node
mChangedSequence.add(next);
}
}
return mChangedSequence;
}
@Override
public String toString() {
return String.format("%s(%s:%s-%s,%.3f)", getMoveName(), mForward ? "F" : "B", mNode,
mNewSucc, getImprovement());
}
}
/**
* Creates a new <code>TRSPShift</code>
*/
public TRSPShift() {
super();
}
/**
* Creates a new <code>TRSPShift</code>
*
* @param constraintHandler
*/
public TRSPShift(TourConstraintHandler constraintHandler) {
super(constraintHandler);
}
@Override
public boolean executeMove(ITRSPTour itour, IMove move) {
TRSPShiftMove m = (TRSPShiftMove) move;
TRSPTour tour = (TRSPTour) itour;
TRSPLogging.getNeighborhoodLogger().lowDebug("TRSPShift.executeMove: executing move %s",
move);
if (tour != m.getTour())
throw new IllegalStateException("Cannot execute this move on a different tour");
int node = m.mNode, newSucc = m.mNewSucc;
// Check if the shift is valid
if (!tour.isVisited(node))
throw new IllegalArgumentException("Node " + node + " is not present in this tour");
if (newSucc != ITRSPTour.UNDEFINED && !tour.isVisited(newSucc))
throw new IllegalArgumentException("New successor " + node
+ " is not present in this tour");
if (tour.getSucc(node) == newSucc)
return false;
double oldCost = tour.getTotalCost();
// Node predecessor and successor
int pred = tour.getPred(node);
int succ = tour.getSucc(node);
// New predecessor
int newPred;
if (newSucc != ITRSPTour.UNDEFINED)
newPred = tour.getPred(newSucc);
else
// If newSucc is UNDEFINED then the node is appended to the end of the tour
newPred = tour.getLastNode();
// Extract node
if (succ != ITRSPTour.UNDEFINED)
tour.setPred(succ, pred);
else
tour.setLast(pred);
if (pred != ITRSPTour.UNDEFINED)
tour.setSucc(pred, succ);
else
tour.setFirst(succ);
// Insert node
if (newPred != ITRSPTour.UNDEFINED)
tour.setSucc(newPred, node);
else
tour.setFirst(node);
if (newSucc != ITRSPTour.UNDEFINED)
tour.setPred(newSucc, node);
else
tour.setLast(node);
tour.setPred(node, newPred);
tour.setSucc(node, newSucc);
// Propagate tools and spare parts
tour.propagateUpdate(m.mForward ? succ : node, m.mForward ? node : pred);
tour.getCostDelegate().nodeShifted(tour, node, pred, m.mForward);
// tour.setTotalCost(tour.getTotalCost() + m.getImprovement());
if (isCheckSolutionAfterMove()) {
String check = TRSPDetailedSolutionChecker.INSTANCE.checkTour(tour);
if (!check.isEmpty())
TRSPLogging.getNeighborhoodLogger()
.warn("TRSPShift.executeMove: Incoherencies in tour after move %s (%s)", m,
check);
if (Math.abs(oldCost - m.getImprovement() - tour.getTotalCost()) > 1e-3) {
TRSPLogging
.getNeighborhoodLogger()
.warn("TRSPShift.executeMove: The tour cost has not changed as expected after move %s (expected %.3f, was %.3f)",
m, -m.getImprovement(), tour.getTotalCost() - oldCost);
}
}
return true;
}
@Override
public void pertub(IInstance instance, ITRSPTour tour, IParameters params) {
throw new UnsupportedOperationException("pertub is not implemented yet " + params);
}
@Override
protected TRSPShiftMove randomNonImproving(ITRSPTour tour, IParameters params) {
throw new UnsupportedOperationException("randomNonImproving is not implemented yet "
+ params);
}
@Override
protected TRSPShiftMove randomFirstImprovement(ITRSPTour tour, IParameters params) {
throw new UnsupportedOperationException("randomFirstImprovement is not implemented yet "
+ params);
}
@Override
protected TRSPShiftMove deterministicBestImprovement(ITRSPTour tour, IParameters params) {
return deterministicExploration(tour, params, false);
}
@Override
protected TRSPShiftMove deterministicFirstImprovement(ITRSPTour tour, IParameters params) {
return deterministicExploration(tour, params, true);
}
protected TRSPShiftMove deterministicExploration(ITRSPTour tour, IParameters params,
boolean first) {
// backward movements
// ---------------------------------------------------
TRSPShiftMove move = exploreBackward(tour, params);
// forward movements
// ---------------------------------------------------
if (move == null || !move.isImproving()) {
move = exploreForward(tour, params);
}
return move;
}
/**
* Explore the sub-neighborhood composed of backward shifts
*
* @param tour
* the optimized tour
* @param params
* local search parameters
* @return the best/first improving backward shift (depending on the {@link IParameters#acceptFirstImprovement()
* params})
*/
protected TRSPShiftMove exploreBackward(ITRSPTour itour, IParameters params) {
if (itour.length() < 1)
return null;
TRSPTour tour = (TRSPTour) itour;
// We start from the beginning of the tour
ITourIterator mainIt = tour.iterator();
int node = ITRSPTour.UNDEFINED;
int candSucc = ITRSPTour.UNDEFINED;
TRSPShiftMove bestMove = null;
boolean skip = false;
if (mainIt.hasNext()) {
node = mainIt.next();
// Skip depot(s) and the first request which cannot be shifted backward
while (mainIt.hasNext() && tour.getInstance().isDepot(node))
node = mainIt.next();
} else
// No backward shift can be found
return null;
while (mainIt.hasNext()) {
// Main iterator
node = mainIt.next();
// Ignore depots
skip = tour.getInstance().isDepot(node);
ITourIterator insIt = null;
if (!skip) {
// Insertion iterator
insIt = tour.iterator();
// No candidate insertion point
if (!insIt.hasNext())
skip = true;
else {
candSucc = insIt.next();
// Skip depot(s)
while (insIt.hasNext() && tour.getInstance().isDepot(candSucc))
candSucc = insIt.next();
}
}
// Consider all nodes that are before the node
while (!skip && insIt.hasNext() && candSucc != node) {
// Evaluate the maximum lateness if node is shifted before candSucc
TRSPShiftMove move = new TRSPShiftMove(tour, Double.NaN, node, candSucc, false);
tour.getCostDelegate().evaluateMove(move);// evaluateMaxLateness(tour, node, candSucc, false);
// We found an improving move
if (params.getAcceptanceCriterion().accept(tour, move)
&& getConstraintHandler().isFeasible(tour, move)) {
if (params.acceptFirstImprovement())
return move;
else if (bestMove == null || move.getImprovement() > bestMove.getImprovement())
bestMove = move;
}
// Move to next node
candSucc = insIt.next();
}
}
return bestMove;
}
/**
* Explore the sub-neighborhood composed of forward shifts of non-violated requests.
*
* @param tour
* the optimized tour
* @param params
* local search parameters
* @return the best/first improving forward shifts of non-violated request (depending on the
* {@link IParameters#acceptFirstImprovement() params})
*/
protected TRSPShiftMove exploreForward(ITRSPTour itour, IParameters params) {
if (itour.length() < 1)
return null;
TRSPTour tour = (TRSPTour) itour;
// We start from the beginning of the tour
ITourIterator mainIt = tour.iterator();
int node = ITRSPTour.UNDEFINED;
int candSucc = ITRSPTour.UNDEFINED;
TRSPShiftMove bestMove = null;
boolean skip = false;
if (mainIt.hasNext()) {
node = mainIt.next();
// Skip depot(s) and the first request which cannot be shifted backward
while (mainIt.hasNext() && tour.getInstance().isDepot(node))
node = mainIt.next();
} else
// No shift can be found
return null;
while (mainIt.hasNext()) {
// Main iterator
node = mainIt.next();
// Ignore violated requests, depots, and the last node
skip = tour.getInstance().isDepot(node) || tour.getSucc(node) == ITRSPTour.UNDEFINED
|| tour.getSucc(tour.getSucc(node)) == ITRSPTour.UNDEFINED;
ITourIterator insIt = null;
if (!skip) {
// Insertion iterator
insIt = tour.iterator(tour.getSucc(tour.getSucc(node)));
// No candidate insertion point
if (!insIt.hasNext())
skip = true;
else
candSucc = insIt.next();
}
// Consider all nodes that are after the node
while (!skip) {
// End the loop after this iteration
if (candSucc == ITRSPTour.UNDEFINED)
skip = true;
TRSPShiftMove move = new TRSPShiftMove(tour, Double.NaN, node, candSucc, true);
tour.getCostDelegate().evaluateMove(move);
// We found an improving move
if (params.getAcceptanceCriterion().accept(tour, move)
&& getConstraintHandler().isFeasible(tour, move)) {
if (params.acceptFirstImprovement())
return move;
else if (bestMove == null || move.getImprovement() > bestMove.getImprovement())
bestMove = move;
}
// Move to next node
if (insIt.hasNext())
candSucc = insIt.next();
else
// Append to the end of the tour
candSucc = ITRSPTour.UNDEFINED;
}
}
return bestMove;
}
@Override
public String getShortName() {
return "shift";
}
/**
* <code>Lateness</code> is a simple container used when evaluating the max lateness in a tour.
* <p>
* Creation date: Mar 22, 2011 - 11:15: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 static class Lateness {
private final double maxLateness;
private final int maxLatenessReq;
/**
* Creates a new <code>Lateness</code>
*
* @param maxLateness
* @param maxLatenessReq
*/
public Lateness(double maxLateness, int maxLatenessReq) {
super();
this.maxLateness = maxLateness;
this.maxLatenessReq = maxLatenessReq;
}
/**
* Getter for <code>maxLateness</code>
*
* @return the maxLateness
*/
public double getMaxLateness() {
return maxLateness;
}
/**
* Getter for <code>maxLatenessReq</code>
*
* @return the maxLatenessReq
*/
public int getMaxLatenessReq() {
return maxLatenessReq;
}
}
}