/** * */ package vroom.common.modeling.util; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; import vroom.common.modeling.dataModel.INodeVisit; import vroom.common.modeling.dataModel.IRoute; import vroom.common.modeling.dataModel.ImmutableRoute; import vroom.common.modeling.dataModel.VRPSolutionHasher; /** * The class <code>HashRoutePool</code> is represents a pool of {@link IRoute} stored in a hash table * <p> * Creation date: May 2, 2013 - 5:15:06 PM * * @author vpillac, <a href="http://www.nicta.com.au">National ICT Australia</a> * @version 1.0 */ public class HashRoutePool<V extends INodeVisit> implements IRoutePool<V> { /** A flag that will enable the count of the number of hash collisions, default is {@code false} for performance */ public static boolean sCountCollisions = false; /** * The minimum size for routes, default is 3 (<code><depot,node,depot></code>), shorter routes will be ignored */ public static int sMinRouteSize = 3; private final Map<?, ?>[] mRoutePool; private int mSize; private int mCollisionsCount = 0; /* (non-Javadoc) * @see vroom.common.modeling.util.IRoutePool#getCollisionsCount() */ @Override public int getCollisionsCount() { if (!sCountCollisions) throw new IllegalStateException("HashRoutePool.sCountCollisions is set to false"); return mCollisionsCount; } /** The hasher that will be used to calculate the hash of routes */ private final VRPSolutionHasher mHasher; /** * Returns the hasher that will be used to calculate the hash of routes * * @return the hasher that will be used to calculate the hash of routes */ public VRPSolutionHasher getHasher() { return mHasher; } /** * Creates a new <code>HashRoutePool</code> * * @param numVehicles * the number of vehicles * @param expectedRoutesPerVehicle * the expected number of routes per vehicle * @param hasher */ public HashRoutePool(int numVehicles, int expectedRoutesPerVehicle, VRPSolutionHasher hasher) { mRoutePool = new Map<?, ?>[numVehicles]; for (int i = 0; i < mRoutePool.length; i++) { mRoutePool[i] = new HashMap<Integer, ImmutableRoute<V>>( (int) (expectedRoutesPerVehicle / 0.75) + 1, 0.75f); } mSize = 0; mHasher = hasher; } @SuppressWarnings("unchecked") private HashRoutePool(HashRoutePool<V> parent) { mRoutePool = new Map<?, ?>[parent.mRoutePool.length]; for (int i = 0; i < mRoutePool.length; i++) { mRoutePool[i] = new HashMap<Integer, ImmutableRoute<V>>( (HashMap<Integer, ImmutableRoute<V>>) parent.mRoutePool[i]); } mSize = parent.mSize; mHasher = parent.mHasher; } @SuppressWarnings("unchecked") private synchronized Map<Integer, ImmutableRoute<V>> getPool(int technician) { return (Map<Integer, ImmutableRoute<V>>) mRoutePool[technician]; } /* * (non-Javadoc) * @see vroom.trsp.datamodel.RPool#add(java.util.Collection) */ /* (non-Javadoc) * @see vroom.common.modeling.util.IRoutePool#add(java.lang.Iterable) */ @Override public synchronized int add(Iterable<? extends IRoute<V>> routes) { int count = 0; for (IRoute<V> route : routes) { if (route.length() >= sMinRouteSize) { // Check for the presence of a route with the same hash Map<Integer, ImmutableRoute<V>> pool = getPool(route.getVehicle().getID()); Integer hash = mHasher.hash(route); ImmutableRoute<V> prevRoute = pool.get(hash); if (prevRoute != null) { // A route with the same hash already exists, compare objective values if (prevRoute.getCost() > route.getCost()) { // Previous route has a higher cost, replace it pool.put(hash, new ImmutableRoute<V>(route, hash)); } else { // Previous route has a lower cost, keep it } if (sCountCollisions) { checkCollision(prevRoute, route); } } else { pool.put(hash, new ImmutableRoute<V>(route, hash)); count++; mSize++; } } } return count; } /** * Check if {@code prevRoute} visits the same requests as {@code route}, if not increment the collision count * * @param prevRoute * @param route */ private void checkCollision(ImmutableRoute<V> prevRoute, IRoute<V> route) { if (prevRoute.length() != route.length()) { mCollisionsCount++; return; } ListIterator<V> itPrev = prevRoute.iterator(); ListIterator<V> it = route.iterator(); while (itPrev.hasNext()) { if (itPrev.next() != it.next()) { mCollisionsCount++; return; } } } /* (non-Javadoc) * @see vroom.common.modeling.util.IRoutePool#getAllRoutes() */ @Override public synchronized List<ImmutableRoute<V>> getAllRoutes() { ArrayList<ImmutableRoute<V>> routes = new ArrayList<ImmutableRoute<V>>(size()); for (int i = 0; i < mRoutePool.length; i++) { routes.addAll(getPool(i).values()); } return routes; } /* * (non-Javadoc) * @see vroom.trsp.datamodel.RPool#size() */ @Override public int size() { return mSize; } /* (non-Javadoc) * @see vroom.common.modeling.util.IRoutePool#clear() */ @Override public synchronized void clear() { for (int i = 0; i < mRoutePool.length; i++) { getPool(i).clear(); } mSize = 0; } /* (non-Javadoc) * @see vroom.common.modeling.util.IRoutePool#dispose() */ @Override public synchronized void dispose() { clear(); } /* (non-Javadoc) * @see vroom.common.modeling.util.IRoutePool#iterator() */ @Override public Iterator<ImmutableRoute<V>> iterator() { return getAllRoutes().iterator(); } @Override public HashRoutePool<V> clone() { return new HashRoutePool<>(this); } }