/**
*
*/
package vroom.trsp.datamodel;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
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 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) {
for (TRSPTour t : this)
if (t.isVisited(reqId))
return t;
return 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 TRSPTour setTour(TRSPTour tour) {
TRSPTour prev = getTour(tour.getTechnician().getID());
mTours[tour.getTechnician().getID()] = tour;
return prev;
}
/** 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
*/
public TRSPSolution(TRSPInstance instance, TRSPCostDelegate costDelegate) {
mLock = new ExtendedReentrantLock();
mInstance = instance;
mCostDelegate = costDelegate;
mTours = new TRSPTour[instance.getFleet().size()];
mUnservedRequests = new IntegerSet(instance.getMaxId());
for (TRSPRequest r : instance.getReleasedRequests())
mUnservedRequests.add(r.getID());
for (int i = 0; i < mTours.length; i++) {
mTours[i] = new TRSPTour(this, getInstance().getFleet().getVehicle(i));
}
}
/**
* 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 getUnservedCount();
}
/**
* 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() {
// TODO TRSPTour redesign: optimize the cloning
TRSPSolution clone = new TRSPSolution(getInstance(), getCostDelegate());
for (int t = 0; t < this.mTours.length; t++) {
clone.mTours[t] = this.mTours[t].clone();
}
clone.mUnservedRequests.clear();
clone.mUnservedRequests.addAll(mUnservedRequests);
return clone;
}
@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);
}
/**
* 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();
}
}