package vroom.optimization.online.jmsa.vrp; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import vroom.common.modeling.dataModel.INodeVisit; import vroom.common.modeling.dataModel.IRoute; import vroom.common.modeling.dataModel.NodeInsertion; import vroom.common.modeling.dataModel.RouteBase; import vroom.common.modeling.dataModel.Solution; import vroom.common.utilities.Constants; import vroom.common.utilities.ILockable; import vroom.common.utilities.Utilities; import vroom.optimization.online.jmsa.IActualRequest; import vroom.optimization.online.jmsa.ISampledRequest; import vroom.optimization.online.jmsa.IScenario; /** * Creation date: Feb 25, 2010 - 3:16:51 PM<br/> * <code>VRPScenario</code> is an implementation of {@link IScenario} that represents a scenario in the context of * vehicle routing problems. It implements the {@link ILockable} interface to ensure that no concurrent modifications * are done on the same instance. Therefore before manipulating an instance, the current thread should acquire the lock * by calling {@link #acquireLock()} and release it afterward with {@link #releaseLock()}. * * @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 * @see VRPActualRequest * @see VRPSampledRequest * @see RouteBase */ public class VRPScenario extends Solution<VRPScenarioRoute> implements IScenario { private final List<VRPSampledRequest> mSampledRequests; // private final Map<Integer, VRPActualRequest> mPendingActualRequests; private int mNonImproving = 0; /** * Creates a new <code>RoutingScenario</code> based on the given <code>vrpInstance</code> * * @param vrpInstance * the instance of VRP from which the actual requests will be taken. * @param actualRequests * a list of pending actual requests (can be <code>null</code>) * @param sampledRequests * a list of sampled requests (can be <code>null</code>) */ public VRPScenario(MSAVRPInstance vrpInstance, List<VRPActualRequest> actualRequests, List<VRPSampledRequest> sampledRequests) { super(vrpInstance); // mInstance = vrpInstance; acquireLock(); if (vrpInstance == null) { throw new IllegalArgumentException("Argument vrpInstance cannot be null"); } mSampledRequests = new LinkedList<VRPSampledRequest>(); if (sampledRequests != null) { mSampledRequests.addAll(mSampledRequests); } // mPendingActualRequests = new HashMap<Integer, VRPActualRequest>(); // if (actualRequests != null) { // for (VRPActualRequest r : actualRequests) { // mPendingActualRequests.put(r.getID(), r); // } // } releaseLock(); } @Override public List<VRPActualRequest> getActualRequests() { // checkLock(); // ArrayList<VRPActualRequest> list = new ArrayList<VRPActualRequest>(mPendingActualRequests.values()); // internalReleaseLock(); // return list; return getParentInstance().getPendingRequests(); } @Override public List<VRPSampledRequest> getSampledRequests() { // checkLock(); ArrayList<VRPSampledRequest> list = new ArrayList<VRPSampledRequest>(mSampledRequests); // internalReleaseLock(); return list; } public VRPActualRequest getLastFixedRequest(int resource) { // checkLock(); VRPActualRequest visit = getRoute(resource).getLastFixedRequest(); // internalReleaseLock(); return visit != null ? (VRPActualRequest) visit : null; } /** * Retrieves an {@linkplain VRPActualRequest actual request} contained in this scenario by its id. * * @param id * the request id * @return the {@link VRPActualRequest} contained in this scenario with the given id, or <code>null</code> if there * is no such request. */ public VRPActualRequest getActualRequest(int id) { // return mPendingActualRequests.get(id); return getParentInstance().getNodeVisit(id); } @Override public IActualRequest getFirstActualRequest(int resource) { // checkLock(); IActualRequest req = getRoute(resource).getFirstActualRequest(); // internalReleaseLock(); return req; } @Override public List<IActualRequest> getOrderedActualRequests(int resource) { // checkLock(); List<IActualRequest> list = Utilities.convertToList(getRoute(resource) .getOrderedActualRequests()); // internalReleaseLock(); return list; } @Override public List<ISampledRequest> getOrderedSampledRequests(int resource) { // checkLock(); List<ISampledRequest> list = Utilities.convertToList(getRoute(resource) .getOrderedSampledRequests()); // internalReleaseLock(); return list; } @Override public int getResourceCount() { return getRouteCount(); } @Override public IActualRequest fixFirstActualRequest(int resource) { checkLock(); IActualRequest req = getRoute(resource).fixFirstActualRequest(); // mPendingActualRequests.remove(req.getID()); internalReleaseLock(); return req; } @Override public boolean markLastVisitAsServed(int resource) { checkLock(); boolean r = getRoute(resource).markLastRequestAsServed(); internalReleaseLock(); return r; } @Override public VRPScenarioRoute getRoute(int index) { // checkLock(); VRPScenarioRoute r = super.getRoute(index); // internalReleaseLock(); return r; } @Override public ListIterator<VRPScenarioRoute> iterator() { // checkLock(); ListIterator<VRPScenarioRoute> it = super.iterator(); // internalReleaseLock(); return it; } @Override public void addRoute(VRPScenarioRoute route) { checkLock(); if (route.getParentSolution() != this) { throw new IllegalStateException( "The added route parent solution is different from this instance"); } super.addRoute(route); internalReleaseLock(); } @Override public MSAVRPInstance getParentInstance() { return (MSAVRPInstance) super.getParentInstance(); } /* * (non-Javadoc) * @see vroom.common.modeling.dataModel.Solution#removeRoute(vroom.common.modeling.dataModel.IRoute) */ @Override public void removeRoute(IRoute<?> route) { checkLock(); super.removeRoute(route); internalReleaseLock(); } /* * (non-Javadoc) * @see vroom.common.modeling.dataModel.Solution#clear() */ @Override public void clear() { checkLock(); super.clear(); internalReleaseLock(); } /* * (non-Javadoc) * @see java.lang.Object#clone() */ @Override public VRPScenario clone() { checkLock(); VRPScenario clone = new VRPScenario(getParentInstance(), new ArrayList<VRPActualRequest>( getActualRequests()), new ArrayList<VRPSampledRequest>(getSampledRequests())); clone.acquireLock(); clone.clear(); for (VRPScenarioRoute route : this) { clone.addRoute(route.cloneInternal(clone)); } clone.releaseLock(); internalReleaseLock(); return clone; } /* * (non-Javadoc) * @see vroom.common.modeling.dataModel.Solution#addRoute(vroom.common.modeling.dataModel.IRoute, int) */ @Override public void addRoute(VRPScenarioRoute route, int index) { checkLock(); super.addRoute(route, index); internalReleaseLock(); } /** * Update the scenario when a request has been added to any route. * <p> * This method should only be called by the {@link VRPScenarioRoute} * </p> * * @param route * the route in which the request was added * @param request * the added request */ public void actualRequestAdded(VRPScenarioRoute route, VRPActualRequest request) { // mPendingActualRequests.put(request.getID(), request); } /** * Update the scenario when a request has been added to any route. * <p> * This method should only be called by the {@link VRPScenarioRoute} * </p> * * @param route * the route in which the request was added * @param request * the added request */ protected void nodeAdded(VRPScenarioRoute route, VRPRequest request) { // if(request instanceof VRPActualRequest){ // actualRequestAdded(route,request); // } } /** * Update the scenario when a request has been removed from any route. * <p> * This method should only be called by the {@link VRPScenarioRoute} * </p> * * @param route * the route in which the request was added * @param request * the added request */ protected void nodeRemoved(VRPScenarioRoute route, VRPRequest request) { // if(request instanceof VRPActualRequest){ // VRPActualRequest r = (VRPActualRequest) request; // if(mPendingActualRequests.containsKey(r.getID())){ // mPendingActualRequests.remove(r.getID()); // } // } } /* * (non-Javadoc) * @see vroom.common.modeling.dataModel.Solution#getCost() */ @Override public double getCost() { // acquireLock(); // checkLock(); return super.getCost(); } /** * Calculation of the best node insertion over all the routes contained in this scenario. * <p> * Note that this implementation will assume for each route that the request cannot be inserted before or after the * first and last node if they are depots. It will also assume that the request can only be inserted after the * shrunk node is any, assuming that it is in first position. * * @param request * the request to be inserted * @param exlusionFilter * an array containing the index of the routes to be excluded in the search * @return a {@link NodeInsertion} representing the best insertion, or <code>null</code> if no feasible insertion * has been found. * @see IRoute#getBestNodeInsertion(INodeVisit, int, int) * @see IRoute#canAccommodateRequest(vroom.common.modeling.dataModel.IVRPRequest) * @see VRPScenarioRoute#ensureRouteSequence() */ public NodeInsertion getBestInsertion(VRPRequest request, int... exlusionFilter) { Arrays.sort(exlusionFilter); NodeInsertion ins = null, bestIns = null; for (int rte = 0; rte < getRouteCount(); rte++) { VRPScenarioRoute route = getRoute(rte); // The route can accommodate the request if (Arrays.binarySearch(exlusionFilter, rte) < 0 && route.canAccommodateRequest(request)) { int max = (route.getLastNode() != null && route.getLastNode().isDepot()) ? route .length() - 1 : route.length(); int min = (route.getFirstNode() != null && route.getFirstNode().isDepot()) ? 1 : 0; min += route.containsShrunkNode() ? 1 : 0; // Find the best insertion in this route ins = route.getBestNodeInsertion(request, min, max); if (bestIns == null || ins.getCost() < bestIns.getCost()) { // This insertion is the best found so far bestIns = ins; } } } return bestIns; } @Override public String toString() { boolean b = tryLock(Constants.TOSTRING_LOCK_TIMOUT); if (b) { StringBuilder string = new StringBuilder(getRouteCount() * 50); string.append(String.format("Total Cost:%.2f", getCost())); string.append(" Routes: {"); Iterator<VRPScenarioRoute> it = iterator(); while (it.hasNext()) { string.append(it.next().toString()); if (it.hasNext()) { string.append(','); } } string.append('}'); releaseLock(); return string.toString(); } else { return Constants.TOSTRING_LOCKED; } } /** * Import the data of the given scenario into this scenario. * <p/> * To present duplicate references, all the copied information will be removed from the given scenario. * * @param scenario * the scenario which routes have to be imported */ public void importScenario(VRPScenario scenario) { // Ignore if the given scenario is this scenario if (scenario != this) { checkLock(); scenario.acquireLock(); // mPendingActualRequests.clear(); // mPendingActualRequests.putAll(scenario.mPendingActualRequests); mSampledRequests.clear(); mSampledRequests.addAll(scenario.mSampledRequests); super.clear(); // Copy routes in a temporary list to prevent concurrent modification exc. LinkedList<VRPScenarioRoute> routes = new LinkedList<VRPScenarioRoute>(); for (VRPScenarioRoute r : scenario) { routes.add(r); } // Change the routes parent solution and add them to this scenario for (VRPScenarioRoute r : routes) { r.changeParentSolution(this); addRoute(r); } // Clear the imported scenario scenario.clear(); scenario.mSampledRequests.clear(); // scenario.mPendingActualRequests.clear(); scenario.releaseLock(); internalReleaseLock(); } } /* * (non-Javadoc) * @see vroom.common.modeling.dataModel.Solution#acquireLock() */ @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 int getNonImprovingCount() { return mNonImproving; } @Override public void incrementNonImprovingCount() { mNonImproving++; } @Override public void resetNonImprovingCount() { mNonImproving = 0; } @Override public void dereference() { for (VRPScenarioRoute r : this) { r.dereference(); } clear(); } }