/**
*
*/
package vroom.trsp.datamodel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import vroom.common.utilities.ArrayIterator;
import vroom.common.utilities.ExtendedReentrantLock;
import vroom.common.utilities.IntegerSet;
import vroom.common.utilities.Utilities;
import vroom.common.utilities.optimization.ISolution;
import vroom.trsp.datamodel.costDelegates.TRSPCostDelegate;
/**
* <code>TRSPSolution</code> is a class representing a solution for the TRSP. It contains a set of {@link TRSPTour},
* each associated with a {@link Technician}
* <p>
* Creation date: Mar 24, 2011 - 2:14:41 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 TRSPSolution implements ISolution, Iterable<TRSPTour> {
/** The main data structure for all tours */
private final GiantPermutation mGiantPermutation;
/**
* Returns the giant permutation in which all tour data is stored
*
* @return the giant permutation in which all tour data is stored
*/
protected GiantPermutation getGiantPermutation() {
return mGiantPermutation;
}
// Arrays shared across all tours
/** the set of unserved requests **/
private final Set<Integer> mUnservedRequests;
/**
* Returns the set of unserved requests <br/>
* This set is initialized with all requests but should be maintained externally
*
* @return the set of unserved requests
*/
public Set<Integer> getUnservedRequests() {
return this.mUnservedRequests;
}
/** The tours contained in this solution */
private final TRSPTour[] mTours;
/**
* Returns the tour associated with a given technician
*
* @param techId
* the id of the technician
* @return the tour associated with the technician with id <code>techID</code>
*/
public TRSPTour getTour(int techId) {
return mTours[techId];
}
/**
* Returns the tour that visits a given request
*
* @param reqId
* the considered request
* @return the tour that visits the request with id <code>reqId</code>
*/
public TRSPTour getVisitingTour(int reqId) {
int tech = getGiantPermutation().getVisitingTechnician(reqId);
return tech >= 0 ? getTour(getGiantPermutation().getVisitingTechnician(reqId)) : null;
}
/**
* Add the tour to this solution, replacing possible previous values
*
* @param tour
* the tour to be added
* @return the tour previously associated with the same technician
*/
public ITRSPTour importTour(TRSPTour tour) {
TRSPTour t = getTour(tour.getTechnicianId());
TRSPSimpleTour prev = new TRSPSimpleTour(t);
t.importTour(tour);
for (int i : prev)
markAsUnserved(i);
for (int i : tour)
markAsServed(i);
return prev;
}
/**
* Import a solution into this solution. Will replace all the tours from this solution with the one present in
* {@code solution}
*
* @param solution
* the solution to import
*/
public void importSolution(TRSPSolution solution) {
if (solution != this) {
getGiantPermutation().clear();
mUnservedRequests.clear();
mUnservedRequests.addAll(solution.getUnservedRequests());
for (TRSPTour tour : solution) {
TRSPTour thisTour = getTour(tour.getTechnicianId());
thisTour.importTour(tour);
}
getGiantPermutation().importPermutationInternal(solution.getGiantPermutation());
}
}
/** the parent instance of this solution **/
private final TRSPInstance mInstance;
/**
* Getter for the parent instance of this solution
*
* @return the value of the instance
*/
public TRSPInstance getInstance() {
return this.mInstance;
}
/** the cost delegate used in this solution tours **/
private TRSPCostDelegate mCostDelegate;
/**
* Getter for the cost delegate used in this solution tours
*
* @return the value of costDelegate
*/
public TRSPCostDelegate getCostDelegate() {
return this.mCostDelegate;
}
/**
* Setter for the cost delegate used in this solution tours
*
* @param costDelegate
* the value to be set for the cost delegate used in this solution tours
*/
public void setCostDelegate(TRSPCostDelegate costDelegate) {
this.mCostDelegate = costDelegate;
getCostDelegate().evaluateSolution(this, true, true);
}
/**
* Creates a new <code>TRSPSolution</code>
*
* @param instance
* the parent instance
* @param costDelegate
* the cost delegate used to evaluate tours
* @see #freeze()
* @see #unfreeze()
*/
public TRSPSolution(TRSPInstance instance, TRSPCostDelegate costDelegate) {
mLock = new ExtendedReentrantLock();
mInstance = instance;
mCostDelegate = costDelegate;
mTours = new TRSPTour[instance.getFleet().size()];
mGiantPermutation = newGiantPermutation();
mUnservedRequests = new IntegerSet(instance.getMaxId());
for (int r : instance.getUnservedReleasedRequests())
markAsUnserved(r);
for (int i = 0; i < mTours.length; i++) {
mTours[i] = newTour(getInstance().getFleet().getVehicle(i));
}
}
/**
* Instanciates a new giant permutation, used in constructor
*
* @return
*/
protected GiantPermutation newGiantPermutation() {
return new GiantPermutation(this);
}
/**
* Factory method used in constructor {@link #TRSPSolution(TRSPInstance, TRSPCostDelegate, boolean)} to instanciate
* the tours
*
* @param t
* the technician to associate with the returned tour
* @return a new tour associated with the given technician
*/
protected TRSPTour newTour(Technician t) {
return new TRSPTour(this, t);
}
/**
* Creates a new <code>TRSPSolution</code> by cloning
*
* @param parent
* the solution to be cloned
*/
protected TRSPSolution(TRSPSolution parent) {
mLock = new ExtendedReentrantLock();
mInstance = parent.getInstance();
mCostDelegate = parent.getCostDelegate();
mTours = new TRSPTour[getInstance().getFleet().size()];
mGiantPermutation = clonePermutation(parent.getGiantPermutation());
mUnservedRequests = new IntegerSet(getInstance().getMaxId());
mUnservedRequests.addAll(parent.getUnservedRequests());
for (int i = 0; i < mTours.length; i++) {
mTours[i] = cloneTour(parent.getTour(i));
}
}
/**
* clone a permutation, used internally in {@link #TRSPSolution(TRSPSolution)}
*
* @param perm
* @return
*/
protected GiantPermutation clonePermutation(GiantPermutation perm) {
return perm.clone(this);
}
/**
* Clone a tour, used internally in {@link #TRSPSolution(TRSPSolution)}
*
* @param tour
* the tour to be cloned
* @return a clone of {@code tour}
*/
protected TRSPTour cloneTour(TRSPTour tour) {
return tour.clone(this);
}
/**
* Returns the number of tours in this solution
*
* @return the number of tours in this solution
*/
public int getTourCount() {
return mTours.length;
}
/**
* Returns the number of non-empty tours in this solution
*
* @return the number of non-empty tours in this solution
*/
public int getActualTourCount() {
int count = 0;
for (TRSPTour t : this) {
for (int n : t) {
if (getInstance().isRequest(n)) {
// This tour visits a request, it is not empty
count++;
break;
}
}
}
return count;
}
@Override
public Comparable<?> getObjective() {
return getObjectiveValue();
}
@Override
public double getObjectiveValue() {
return getCostDelegate().evaluateSolution(this, false, false);
}
/**
* Returns the number of requests that are not served by this solution
*
* @return the number of requests that are not served by this solution
*/
public int getUnservedCount() {
return getUnservedRequests().size();
}
/**
* Mark a request as served, or does nothing if {@code id} is not a valid request id
*
* @param id
* an id
*/
public void markAsServed(int id) {
if (getInstance().isRequest(id))
getUnservedRequests().remove(id);
}
/**
* Mark all requests as served, in other words clear the {@link #getUnservedRequests() unserved request}
*/
public void markAllAsServed() {
mUnservedRequests.clear();
}
/**
* Mark a request as unserved, or does nothing if {@code id} is not a valid request id
*
* @param id
* an id
*/
public void markAsUnserved(int id) {
if (getInstance().isRequest(id))
getUnservedRequests().add(id);
}
/**
* Returns the number of request that are present in this solution
* <p>
* This method will iterate over all tours
* </p>
*
* @return the number of request that are present in this solution
*/
public int getServedCount() {
int count = 0;
for (TRSPTour t : this)
for (Integer n : t)
if (getInstance().isRequest(n))
count++;
return count;
}
@Override
public TRSPSolution clone() {
return new TRSPSolution(this);
}
@Override
public Iterator<TRSPTour> iterator() {
return new ArrayIterator<TRSPTour>(mTours);
}
// ------------------------------------
// ILockable interface implementation
// ------------------------------------
/** A lock to be used by this instance */
private final ExtendedReentrantLock mLock;
private boolean mSelfLock = false;
@Override
public boolean tryLock(long timeout) {
try {
return getLockInstance().tryLock(timeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
return false;
}
}
@Override
public void acquireLock() {
try {
if (!getLockInstance().tryLock(TRY_LOCK_TIMOUT, TRY_LOCK_TIMOUT_UNIT)) {
throw new IllegalStateException(
String.format(
"Unable to acquire lock on this instance of %s (%s) after %s %s, owner: %s",
this.getClass().getSimpleName(), hashCode(), TRY_LOCK_TIMOUT,
TRY_LOCK_TIMOUT_UNIT, getLockInstance().getOwnerName()));
}
} catch (InterruptedException e) {
throw new IllegalStateException(String.format(
"Unable to acquire lock on this instance of %s (%s)", this.getClass()
.getSimpleName(), hashCode()), e);
}
;
}
@Override
public void releaseLock() {
if (mLock.isLocked()) {
this.mLock.unlock();
}
}
public void internalReleaseLock() {
if (mSelfLock) {
releaseLock();
mSelfLock = false;
}
}
@Override
public boolean isLockOwnedByCurrentThread() {
return this.mLock.isHeldByCurrentThread();
}
@Override
public ExtendedReentrantLock getLockInstance() {
return this.mLock;
}
/**
* Check the lock state of this object
*
* @return <code>true</code> if there was no previous lock and lock was acquired, <code>false</code> if the lock was
* already owned by the current thread
* @throws ConcurrentModificationException
*/
public boolean checkLock() throws ConcurrentModificationException {
if (!isLockOwnedByCurrentThread() && mLock.isLocked()) {
throw new ConcurrentModificationException(
String.format(
"The current thread (%s) does not have the lock on this instance of %s, owner: %s",
Thread.currentThread(), this.getClass().getSimpleName(),
getLockInstance().getOwnerName()));
} else if (!mLock.isLocked()) {
acquireLock();
mSelfLock = true;
return true;
} else {
mSelfLock = false;
return false;
}
}
// ------------------------------------
@Override
public String toString() {
StringBuilder b = new StringBuilder();
b.append(String.format("Cost:%.2f, Unserved:%s Tours [", getObjectiveValue(),
Utilities.toShortString(getUnservedRequests())));
Iterator<TRSPTour> it = iterator();
while (it.hasNext()) {
b.append(it.next().toString());
if (it.hasNext())
b.append(',');
}
b.append(']');
return b.toString();
}
@Override
public int hashCode() {
return getInstance().getSolutionHasher().hash(this);
}
@Override
public int hashSolution() {
return getInstance().getSolutionHasher().hash(this);
}
/**
* Returns the hash code of this instance as defined in {@link Object#hashCode()}
*
* @return the hash code of this instance as defined in {@link Object#hashCode()}
*/
public int defaultHashCode() {
return super.hashCode();
}
/**
* Convert a solution to a giant tour
*
* @return a list containing all the tours concatenated into a giant tour
*/
public List<Integer> toGiantTour() {
ArrayList<Integer> tour = new ArrayList<Integer>(getInstance().getMaxId() + 1);
for (ITRSPTour t : this) {
for (Integer node : t) {
tour.add(node);
}
}
return tour;
}
/**
* Convert a solution to a short string of the form: <br/>
* <code>"<1,11,12,0,13,21|2,14,15,16,22>"</code>
*
* @return a short string describing this solution
*/
public String toShortString() {
StringBuilder sb = new StringBuilder();
sb.append("<");
for (ITRSPTour tour : this) {
if (sb.length() > 1)
sb.append("|");
int idx = 0;
for (int node : tour) {
if (idx > 0)
sb.append(",");
sb.append(node);
idx++;
}
}
sb.append(">");
return sb.toString();
}
/**
* <code>GiantPermutation</code>
* <p>
* Creation date: Apr 26, 2012 - 4:56:09 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 GiantPermutation {
/** The cumulative cost at each node. */
private final double[] mCumulativeCost;
/** The earliest time of visit at each node. */
private final double[] mEarliestArrivalTime;
/** The latest feasible arrival time at each node. */
private final double[] mLatestFeasibleTime;
/** The total waiting time at between any two nodes */
private final double[][] mWaitingTime;
/** The forward time slack between any two nodes */
private final double[][] mFwdTimeSlack;
/** The available tools at each node */
private final boolean[][] mTools;
/** The available spare parts at each node */
private final int[][] mSpareParts;
/** The number of spare parts required at each node and for the rest of the tour */
private final int[][] mRequiredSpareParts;
/** <code>true</code> if the depot is visited prior to the corresponding node */
private final boolean[] mDepotVisited;
/** The Predecessor of each node. */
private final int[] mPred;
/** The Successor of each node. */
private final int[] mSucc;
/** The technician visiting each node */
private final int[] mTechId;
private final TRSPSolution mSolution;
/**
* The Constant NA to represent unknown data, for instance for cumulative cost of a node that is not in the tour
*/
public final static double NA = Double.NaN;
private TRSPInstance getInstance() {
return mSolution.getInstance();
}
protected GiantPermutation(TRSPSolution solution) {
mSolution = solution;
mCumulativeCost = new double[getInstance().getMaxId()];
mEarliestArrivalTime = new double[getInstance().getMaxId()];
mLatestFeasibleTime = new double[getInstance().getMaxId()];
mWaitingTime = new double[getInstance().getMaxId()][];
boolean defFwdSlck = !getInstance().isCVRPTW() || getInstance().isForceFwdSlackTime();
mFwdTimeSlack = defFwdSlck ? new double[getInstance().getMaxId()][] : null;
for (int i = 0; i < mWaitingTime.length; i++) {
mWaitingTime[i] = new double[defFwdSlck ? mWaitingTime.length - i : 1];
if (defFwdSlck)
mFwdTimeSlack[i] = new double[mFwdTimeSlack.length - i];
}
mTools = getInstance().isCVRPTW() ? null
: new boolean[getInstance().getMaxId()][getInstance().getToolCount()];
mSpareParts = new int[getInstance().getMaxId()][getInstance().getSpareCount()];
mRequiredSpareParts = new int[getInstance().getMaxId()][getInstance().getSpareCount()];
mDepotVisited = new boolean[getInstance().getMaxId()];
mPred = new int[getInstance().getMaxId()];
mSucc = new int[getInstance().getMaxId()];
mTechId = new int[getInstance().getMaxId()];
for (int id = 0; id < mTechId.length; id++) {
mTechId[id] = TRSPTour.UNDEFINED;
mPred[id] = TRSPTour.UNDEFINED;
mSucc[id] = TRSPTour.UNDEFINED;
}
}
/**
* Import {@code giantPermutation} in this permutation
*
* @param giantPermutation
*/
protected void importPermutationInternal(GiantPermutation giantPermutation) {
// Nothing to do here - the work is delegated to TRSPTour.importTour
}
/**
* Used internally in {@link #clone()} to instantiate a new permutation, should be overriden by subclasses
*
* @param solution
* @return
*/
protected GiantPermutation cloneInternal(TRSPSolution solution) {
return new GiantPermutation(solution);
}
/**
* Clone this permutation and associate it to a possibly different solution
*
* @param solution
* @return a clone of this permutation
*/
private GiantPermutation clone(TRSPSolution solution) {
GiantPermutation clone = cloneInternal(solution);
clone.importPermutation(this);
return clone;
}
/**
* Clone this permutation and associate it to a possibly different solution
*
* @param solution
* @param clone
* an empty instance of {@link GiantPermutation}
* @return a clone of this permutation
*/
public void importPermutation(GiantPermutation clone) {
for (int i = 0; i < mCumulativeCost.length; i++) {
this.mCumulativeCost[i] = clone.mCumulativeCost[i];
this.mEarliestArrivalTime[i] = clone.mEarliestArrivalTime[i];
this.mLatestFeasibleTime[i] = clone.mLatestFeasibleTime[i];
for (int j = 0; j < this.mWaitingTime[i].length; j++)
this.mWaitingTime[i][j] = clone.mWaitingTime[i][j];
if (this.mFwdTimeSlack != null)
for (int j = 0; j < this.mFwdTimeSlack[i].length; j++)
this.mFwdTimeSlack[i][j] = clone.mFwdTimeSlack[i][j];
if (this.mTools != null)
for (int j = 0; j < this.mTools[i].length; j++)
this.mTools[i][j] = clone.mTools[i][j];
for (int j = 0; j < this.mSpareParts[i].length; j++)
this.mSpareParts[i][j] = clone.mSpareParts[i][j];
for (int j = 0; j < this.mRequiredSpareParts[i].length; j++)
this.mRequiredSpareParts[i][j] = clone.mRequiredSpareParts[i][j];
this.mDepotVisited[i] = clone.mDepotVisited[i];
this.mPred[i] = clone.mPred[i];
this.mSucc[i] = clone.mSucc[i];
this.mTechId[i] = clone.mTechId[i];
}
}
/**
* Returns {@code true} if the forward slack time is defined in this instance
*
* @return {@code true} if the forward slack time is defined in this instance
*/
public boolean isFwdSlackTimeDefined() {
return mFwdTimeSlack != null;
}
/**
* Returns the id of the technician visiting the given node
*
* @param node
* @return the id of the technician visiting the given node
*/
public int getVisitingTechnician(int node) {
return mTechId[node];
}
/**
* Sets the id of the technician visiting the given node
*
* @param node
* @param techId
*/
public void setVisitingTechnician(int node, int techId) {
if (mTechId[node] != TRSPTour.UNDEFINED && techId != TRSPTour.UNDEFINED
&& techId != mTechId[node])
throw new IllegalStateException(String.format(
"Cannot affect %s to technician %s (already visited by %s)", node, techId,
mTechId[node]));
mTechId[node] = techId;
}
/**
* Gets the cumulative cost at a given node
*
* @param nodeId
* the id of the considered node
* @return the cumulative cost at the specified node
*/
public double getCumulativeCost(int nodeId) {
return mCumulativeCost[nodeId];
}
/**
* Sets the cumulative cost at a given node
*
* @param nodeId
* the id of the considered node
* @param value
* the cumulative cost at the specified node
*/
public void setCumulativeCost(int nodeId, double value) {
mCumulativeCost[nodeId] = value;
}
/**
* Gets the earliest arrival time at a given node, independently of the node time window.
*
* @param nodeId
* the id of the considered node
* @return the earliest arrival time at node with id <code>nodeId</code>
*/
public double getEarliestArrivalTime(int nodeId) {
return mEarliestArrivalTime[nodeId];
}
/**
* Sets the earliest arrival time at a given node
*
* @param nodeId
* the id of the considered node
* @param time
* the earliest arrival time at node with id <code>nodeId</code>
*/
public void setEarliestArrivalTime(int nodeId, double time) {
mEarliestArrivalTime[nodeId] = time;
}
/**
* Gets the waiting time at a given node, i.e. the time that the vehicle will be waiting before the start of the
* time window.
*
* @param nodeId
* the id of the considered node
* @return the waiting time at node with id <code>nodeId</code>
*/
public double getWaitingTime(int nodeId) {
return mWaitingTime[nodeId][0];
}
/**
* Gets the cumulative waiting time between two nodes, i.e. the total time the vehicle will spend waiting
* between nodes <code>i</code> and <code>j</code>
*
* @param i
* the first node
* @param j
* the second node
* @return the waiting time at node with id <code>nodeId</code>
*/
public double getWaitingTime(int i, int j) {
if (j < i) {
int a = j;
j = i;
i = a;
}
return mWaitingTime[i][j - i];
}
/**
* Sets the waiting time at a given node, i.e. the time that the vehicle will be waiting before the start of the
* time window.
*
* @param nodeId
* the id of the considered node
* @param time
* the waiting time at node with id <code>nodeId</code>
*/
public void setWaitingTime(int nodeId, double time) {
mWaitingTime[nodeId][0] = time;
}
/**
* Sets the cumulative waiting time between two nodes, i.e. the total time the vehicle will spend waiting
* between nodes <code>i</code> and <code>j</code>
*
* @param i
* the first node
* @param j
* the second node
* @param time
* the cumulative waiting time between <code>i</code> and <code>j</code>
* @return the waiting time at node with id <code>nodeId</code>
*/
public void setWaitingTime(int i, int j, double time) {
if (j < i) {
int a = j;
j = i;
i = a;
}
try {
mWaitingTime[i][j - i] = time;
} catch (ArrayIndexOutOfBoundsException e) {
// FIXME remove
e.printStackTrace();
throw e;
}
}
/**
* Returns the forward slack time at node {@code i} relative to the path {@code (i,...,j)}
*
* @param i
* @param j
* @return the forward slack time at node {@code i} relative to the path {@code (i,...,j)}
*/
public double getFwdSlackTime(int i, int j) {
if (j < i) {
int a = j;
j = i;
i = a;
}
return mFwdTimeSlack[i][j - i];
}
/**
* Sets the forward slack time at node {@code i} relative to the path {@code (i,...,j)}
*
* @param i
* @param j
* @param slack
* the forward slack time at node {@code i} relative to the path {@code (i,...,j)}
*/
public void setFwdSlackTime(int i, int j, double slack) {
if (j < i) {
int a = j;
j = i;
i = a;
}
mFwdTimeSlack[i][j - i] = slack;
}
/**
* Gets the latest arrival time at a node so that the remaining of the route can be executed.
*
* @param nodeId
* the id of the considered node
* @return the latest feasible arrival time at node with id <code>nodeId</code>
*/
public double getLatestFeasibleArrivalTime(int nodeId) {
return mLatestFeasibleTime[nodeId];
}
/**
* Sets the latest arrival time at a node so that the remaining of the route can be executed.
*
* @param nodeId
* the id of the considered node
* @param time
* the latest feasible arrival time at node with id <code>nodeId</code>
*/
public void setLatestFeasibleTime(int nodeId, double time) {
mLatestFeasibleTime[nodeId] = time;
}
/**
* Returns <code>true</code> if the main depot is visited in this tour before visiting <code>node</code>.
*
* @param node
* the id of the considered node
* @return <code>true</code> if the main depot is visited in this tour before visiting <code>node</code>.
*/
public boolean isMainDepotVisited(int node) {
return getInstance().isMainDepot(node) || mDepotVisited[node];
}
/**
* Sets the main depot visited flag
*
* @param node
* @param visited
*/
public void setMainDepotVisited(int node, boolean visited) {
mDepotVisited[node] = visited;
}
/**
* Gets the predecessor of a node.
*
* @param node
* the node
* @return the predecessor of <code>node</code>
*/
public int getPred(int node) {
return this.mPred[node];
}
/**
* Sets the predecessor of a node.
*
* @param node
* the node
*/
public void setPred(int node, int pred) {
this.mPred[node] = pred;
}
/**
* Gets the successor of a node.
*
* @param node
* the node
* @return the successor of <code>node</code>
*/
public int getSucc(int node) {
return this.mSucc[node];
}
/**
* Sets the predecessor of a node.
*
* @param node
* the node
* @param succ
* the successor of <code>node</code>
*/
public void setSucc(int node, int succ) {
this.mSucc[node] = succ;
}
/**
* Checks if a tool is available at a given node
*
* @param node
* the considered node
* @param tool
* the considered tool
* @return <code>true</code> if the <code>tool</code> is available at the given <code>node</code>
*/
public boolean isToolAvailable(int node, int tool) {
return mTools[node][tool];
}
/**
* Returns the available tools at a given node
*
* @param node
* @return the available tools at a given node
*/
public int[] getAvailableTools(int node) {
int count = 0;
for (int i = 0; i < mTools[node].length; i++)
if (mTools[node][i])
count++;
int[] tools = new int[count];
int j = 0;
for (int i = 0; i < mTools[node].length; i++)
if (mTools[node][i])
tools[j++] = i;
return tools;
}
/**
* Set the tool availability
*
* @param node
* @param tool
* @param available
*/
public void setToolAvailability(int node, int tool, boolean available) {
mTools[node][tool] = available;
}
/**
* Gets the number of spare parts available after servicing a given node
*
* @param node
* the considered node
* @param type
* the considered spare part type
* @return the number of spare parts of <code>type</code> available after servicing the given <code>node</code>,
* or at the start of the tour if <code>node={@link ITRSPTour#UNDEFINED}</code>
*/
public int getAvailableSpareParts(int node, int type) {
return mSpareParts[node][type];
}
/**
* Gets the available spare parts after servicing a given node
*
* @param node
* the considered node
* @param type
* the considered spare part type
* @return the available spare parts after servicing a given node
*/
public int[] getAvailableSpareParts(int node) {
return Arrays.copyOf(mSpareParts[node], mSpareParts[node].length);
}
/**
* Sets the number of spare parts available after servicing a given node
*
* @param node
* the considered node
* @param type
* the considered spare part type
* @param num
* the number of spare parts of <code>type</code> available after servicing the given
* <code>node</code>, or at the start of the tour if <code>node={@link ITRSPTour#UNDEFINED}</code>
*/
public void setAvailableSpareParts(int node, int type, int num) {
mSpareParts[node][type] = num;
}
/**
* Gets the number of spare parts required to serve the tour starting at a given node.
*
* @param node
* the considered node
* @param type
* the considered spare part type
* @return the number of spare parts of <code>type</code> required to serve the given <code>node</code> and all
* subsequent nodes, or 0 if <code>node={@link ITRSPTour#UNDEFINED}</code>
*/
public int getRequiredSpareParts(int node, int type) {
if (node == ITRSPTour.UNDEFINED)
return 0;
return mRequiredSpareParts[node][type];
}
/**
* Sets the number of spare parts required to serve the tour starting at a given node.
* <p>
* The returned value ignores possible trips to the depot in the following nodes.
* </p>
*
* @param node
* the considered node
* @param type
* the considered spare part type
* @param num
* the number of spare parts of <code>type</code> required to serve the given <code>node</code> and
* all subsequent nodes, or 0 if <code>node={@link ITRSPTour#UNDEFINED}</code>
*/
public void setRequiredSpareParts(int node, int type, int num) {
mRequiredSpareParts[node][type] = num;
}
/**
* Gets the number of spare parts required to serve the tour starting at a given node.
* <p>
* The returned value ignores possible trips to the depot in the following nodes.
* </p>
*
* @param node
* the considered node
* @return the number of spare parts of <code>type</code> required to serve the given <code>node</code> and all
* subsequent nodes, or 0 if <code>node={@link ITRSPTour#UNDEFINED}</code>
*/
public int[] getRequiredSpareParts(int node) {
if (node == ITRSPTour.UNDEFINED)
return new int[getInstance().getSpareCount()];
return mRequiredSpareParts[node];
}
protected void resetNodeData(int node) {
setPred(node, ITRSPTour.UNDEFINED);
setSucc(node, ITRSPTour.UNDEFINED);
setVisitingTechnician(node, ITRSPTour.UNDEFINED);
// FIXME find why inconsistencies appear if this is not reset
setCumulativeCost(node, NA);
setEarliestArrivalTime(node, GiantPermutation.NA);
setLatestFeasibleTime(node, GiantPermutation.NA);
Arrays.fill(this.mSpareParts[node], 0);
Arrays.fill(this.mRequiredSpareParts[node], 0);
setWaitingTime(node, NA);
if (!getInstance().isCVRPTW()) {
Arrays.fill(this.mTools[node], false);
for (int i = 0; i < getInstance().getMaxId(); i++) {
setWaitingTime(i, node, NA);
}
}
setMainDepotVisited(node, getInstance().isMainDepot(node));
}
/**
* Reset all the information stored in this instance
*/
public void clear() {
for (int n = 0; n < mPred.length; n++)
resetNodeData(n);
}
/**
* Check this giant permutation for cycles
*
* @return the first cycle found in the permutation, or <code>null</code> if none was found
*/
public List<Integer> checkForCycles() {
boolean[] touched = new boolean[mPred.length];
int start = 0;
while (start < mPred.length) {
if (!touched[start]) {
LinkedList<Integer> cycle = new LinkedList<>();
// start was not touched before
int n = start;
cycle.add(n);
touched[n] = true;
while (mSucc[n] != ITRSPTour.UNDEFINED) {
cycle.add(n);
if (touched[n]) // We found a cycle
return cycle;
touched[n] = true;
n = mSucc[n];
}
} else {
start++; // start was already touched: move to the next node
}
}
return null;
}
public boolean isFrozen(int node) {
return false;
}
}
/** Reset this solution to its initial state */
public void clear() {
getGiantPermutation().clear();
for (TRSPTour t : this) {
t.clear();
}
for (Integer r : getInstance().getReleasedRequests())
getUnservedRequests().add(r);
}
}