/** * */ 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 vroom.common.modeling.dataModel.INodeVisit; import vroom.common.modeling.dataModel.IVRPRequest; import vroom.common.modeling.dataModel.Node; import vroom.common.modeling.dataModel.Request; import vroom.common.modeling.dataModel.Vehicle; import vroom.common.modeling.util.CostCalculationDelegate; import vroom.common.utilities.IObservable; import vroom.common.utilities.IObserver; import vroom.common.utilities.Update; import vroom.common.utilities.ValueUpdate; import vroom.optimization.online.jmsa.vrp.vrpsd.VRPSDActualRequest; /** * Creation date: May 3, 2010 - 10:54:10 AM<br/> * <code>VRPShrunkRequest</code> is an extension of {@link VRPRequest} used to * represent a virtual request that aggregate the currently served requests. * * @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 VRPShrunkRequest extends VRPRequest implements IObserver, IObservable { private final LinkedList<VRPActualRequest> mShrunkNodes; private final double[] mDemands; private IVRPRequest mShrunkRequest; private boolean mServed; private double mCost; private final CostCalculationDelegate mCostDelegate; private final Vehicle mVehicle; /** * Creates a new <code>VRPShrunkRequest</code> * * @param nProd */ public VRPShrunkRequest(Vehicle vehicle, MSAVRPInstance instance) { super(null); mShrunkNodes = new LinkedList<VRPActualRequest>(); mDemands = new double[vehicle.getCompartmentCount()]; mServed = true; mCost = 0; mCostDelegate = instance.getCostDelegate(); mVehicle = vehicle; } /** * Shrunk an additional request in this shrunk node * * @param request * the request to be shrunk */ public synchronized void shrunkRequest(VRPActualRequest request) { if (!isServed()) { throw new IllegalStateException( "The last shrunk request has not been served"); } double[] oldDemands = Arrays.copyOf(mDemands, mDemands.length); // Update the aggregated distance if (!mShrunkNodes.isEmpty()) { mCost += mCostDelegate.getCost(getNodeVisit(), request, mVehicle); } if (request.isDepot()) { mShrunkNodes.clear(); mShrunkNodes.add(request); for (int p = 0; p < mDemands.length; p++) { mDemands[p] = 0; } updateShrunkRequest(oldDemands); // Prevent node repetition } else if (isEmpty() || !mShrunkNodes.getLast().equals(request)) { mShrunkNodes.add(request); for (int p = 0; p < mDemands.length; p++) { if (!(request instanceof VRPSDActualRequest) || ((VRPSDActualRequest) request).isDemandKnown(p)) { // Ignore unknown demands to prevent infeasible shrunk node mDemands[p] += request.getDemand(p); } } mServed = false; updateShrunkRequest(oldDemands); request.addObserver(this); } } /** * Return <code>true</code> if this shrunk node is empty * * @return <code>true</code> if there is no shrunk node in this instance */ public boolean isEmpty() { return mShrunkNodes.isEmpty(); } /** * Size of the shrunk node * * @return the number of request that have been shrunk in this node */ public int size() { return mShrunkNodes.size(); } /** * Getter for <code>served</code> flag * * @return <code>true</code> if the last request has been served */ public boolean isServed() { return mServed; } /** * Mark the last request as served */ public void markAsServed() { mServed = true; } /** * Getter for the first shrunk node visit * * @return the first {@link INodeVisit} of this shrunk node */ public INodeVisit getFirstNode() { return mShrunkNodes.getFirst(); } /** * Getter for the last shrunk node visit * * @return the last {@link INodeVisit} of this shrunk node */ @Override public INodeVisit getNodeVisit() { return mShrunkNodes.getLast(); } /** * Getter for the last shrunk node * * @return the last {@link Node} of this shrunk node */ @Override public Node getNode() { return super.getNode(); } /** * Getter for the vehicle to which this shrunk node is associated * * @return the vehicle to which this shrunk node is associated */ public Vehicle getVehicle() { return mVehicle; } /* * (non-Javadoc) * * @see vroom.optimization.online.jmsa.vrp.VRPMSARequest#getParentRequest() */ @Override public IVRPRequest getParentRequest() { return mShrunkRequest; } /** * Getter for the aggregated cost of this shrunk node * * @return the sum of the cost between the shrunk nodes */ public double getAggregatedCost() { return mCost; } /* * (non-Javadoc) * * @see vroom.optimization.online.jmsa.vrp.VRPMSARequest#getDemand() */ @Override public double getDemand() { return getDemand(0); } /* * (non-Javadoc) * * @see vroom.optimization.online.jmsa.vrp.VRPMSARequest#getDemand(int) */ @Override public double getDemand(int product) { return mDemands[product]; } /** * Getter for the total demand of this node * * @return a copy of the demands array */ public double[] getDemands() { return Arrays.copyOf(mDemands, mDemands.length); } /** * Getter for the list of shrunk nodes * * @return a copy of the shrunk node list */ public List<VRPActualRequest> getShrunkNodes() { return new ArrayList<VRPActualRequest>(mShrunkNodes); } /* * (non-Javadoc) * * @see vroom.common.utilities.Cloneable#cloneObject() */ @Override public INodeVisit clone() { throw new UnsupportedOperationException("Cannot clone a shrunk request"); } /* * (non-Javadoc) * * @see vroom.common.utilities.IObserver#update(vroom.common.utilities * .IObservable, java.lang.Object) */ @Override public void update(IObservable source, Update update) { if (source instanceof VRPSDActualRequest && update instanceof ValueUpdate) { // VRPSDActualRequest req = (VRPSDActualRequest) source; // double[] oldDem = (double[]) ((ValueUpdate) // update).getOldValue(); // double[] newDem = (double[]) ((ValueUpdate) // update).getNewValue(); // // double[] oldCDemands = Arrays.copyOf(this.mDemands, // this.mDemands.length); // // for (int p = 0; p < this.mDemands.length; p++) { // if (req.isDemandKnown(p)) { // if (!req.equals(mShrunkNodes.getLast())) { // // assumes that the demand was ignored at first // this.mDemands[p] -= oldDem[p]; // } // this.mDemands[p] += newDem[p]; // } // } // // updateShrunkRequest(oldCDemands); updateDemands(); } } /** * Recalculate the accumulated demands. * <p> * Note that {@link VRPSDActualRequest} which demand is not known yet will * have a demand of 0 * </p> */ protected void updateDemands() { double[] oldCDemands = Arrays.copyOf(mDemands, mDemands.length); for (int p = 0; p < mDemands.length; p++) { mDemands[p] = 0; } for (VRPActualRequest r : mShrunkNodes) { for (int p = 0; p < mDemands.length; p++) { if (!(r instanceof VRPSDActualRequest) || ((VRPSDActualRequest) r).isDemandKnown(p)) { mDemands[p] += r.getDemand(p); } } } updateShrunkRequest(oldCDemands); } /** * Update the underlying shrunk request * * @param oldDemands * the previous demands */ protected void updateShrunkRequest(double[] oldDemands) { boolean changed = false; for (int p = 0; p < oldDemands.length; p++) { if (oldDemands[p] != getDemand(p)) { changed = true; break; } } mShrunkRequest = new Request(-mShrunkNodes.getLast().getID(), mShrunkNodes.getLast().getNode()); if (mShrunkNodes.getLast().isDepot()) { for (int p = 0; p < mDemands.length; p++) { mDemands[p] = 0; } } mShrunkRequest.setDemands(mDemands); // Ignore if no change if (changed) { notifyObservers(new ValueUpdate(PROP_DEMANDS, oldDemands, mDemands)); } } /** * A shrunk node is always considered to be fixed * * @return <code>true</code> in any case */ @Override public boolean isFixed() { return true; } /* * (non-Javadoc) * * @see vroom.optimization.online.jmsa.vrp.VRPMSARequest#toString() */ @Override public String toString() { return String.format("AG:%s-%s D=%s", toShortString(), getNode() .getLocation(), Arrays.toString(mDemands)); } /* * (non-Javadoc) * * @see vroom.optimization.online.jmsa.vrp.VRPMSARequest#hashCode() */ @Override public int hashCode() { return superHashCode(); } /* * (non-Javadoc) * * @see * vroom.optimization.online.jmsa.vrp.VRPMSARequest#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { return this == obj; } /* * (non-Javadoc) * * @see vroom.optimization.online.jmsa.vrp.VRPMSARequest#toShortString() */ @Override public String toShortString() { StringBuilder b = new StringBuilder(mShrunkNodes.size()); b.append('['); Iterator<VRPActualRequest> it = mShrunkNodes.iterator(); while (it.hasNext()) { b.append(it.next().toShortString()); if (it.hasNext()) { b.append(','); } } b.append(']'); return b.toString(); } /* * (non-Javadoc) * * @see vroom.optimization.online.jmsa.vrp.VRPMSARequest#isDepot() */ @Override public boolean isDepot() { return getNodeVisit() == null || getNodeVisit().isDepot(); } @Override public int getID() { return mShrunkRequest != null ? mShrunkRequest.getID() : 0; } }