/** * */ package vroom.trsp.datamodel; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import vroom.common.utilities.IntegerSet; import vroom.trsp.util.TRSPLogging; /** * <code>HashTourPool</code> is an implementation of {@link ITRSPTourPool} based on a {@link HashSet} * <p> * Creation date: Aug 16, 2011 - 3:27:31 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 HashTourPool implements ITRSPTourPool { /** 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 tours, default is 3 (<code><depot,node,depot></code>), shorter tours will be ignored */ public static int sMinTourSize = 3; private final Map<?, ?>[] mTourPool; private int mSize; private int mCollisionsCount = 0; /** * Returns the number of hash collisions detected The result is only valid is {@link #sCountCollisions} is set to * {@code true} * * @return the number of hash collisions detected * @throws IllegalStateException * if HashTourPool.sCountCollisions is set to false */ public int getCollisionsCount() { if (!sCountCollisions) throw new IllegalStateException("HashTourPool.sCountCollisions is set to false"); return mCollisionsCount; } /** The hasher that will be used to calculate the hash of tours */ private final ITRSPSolutionHasher mHasher; /** * Returns the hasher that will be used to calculate the hash of tours * * @return the hasher that will be used to calculate the hash of tours */ public ITRSPSolutionHasher getHasher() { return mHasher; } /** * Creates a new <code>HashTourPool</code> * * @param techCount * the number of available technicians * @param expectedToursPerTech * the expected number of tours per technician * @param hasher */ public HashTourPool(int techCount, int expectedToursPerTech, ITRSPSolutionHasher hasher) { mTourPool = new Map<?, ?>[techCount]; for (int i = 0; i < mTourPool.length; i++) { mTourPool[i] = new HashMap<Integer, TRSPSimpleTour>((int) (expectedToursPerTech / 0.75) + 1, 0.75f); } mSize = 0; mHasher = hasher; } @SuppressWarnings("unchecked") private synchronized Map<Integer, TRSPSimpleTour> getPool(int technician) { return (Map<Integer, TRSPSimpleTour>) mTourPool[technician]; } /* * (non-Javadoc) * @see vroom.trsp.datamodel.ITRSPTourPool#add(java.util.Collection) */ @Override public synchronized int add(Iterable<? extends ITRSPTour> tours) { int count = 0; for (ITRSPTour tour : tours) { // Check the tour feasibility String err = TRSPSolutionChecker.INSTANCE.checkTour(tour); if (err.isEmpty() && tour.length() >= sMinTourSize) { // Check for the presence of a tour with the same hash Map<Integer, TRSPSimpleTour> pool = getPool(tour.getTechnicianId()); Integer hash = mHasher.hash(tour); TRSPSimpleTour prevTour = pool.get(hash); if (prevTour != null) { // A tour with the same hash already exists, compare objective values if (prevTour.getTotalCost() > tour.getTotalCost()) { // Previous tour has a higher cost, replace it pool.put(hash, new TRSPSimpleTour(tour, hash)); } else { // Previous tour has a lower cost, keep it } if (sCountCollisions) { checkCollision(prevTour, tour); } } else { pool.put(hash, new TRSPSimpleTour(tour, hash)); count++; mSize++; } } else if (!err.isEmpty()) { TRSPLogging.getOptimizationLogger().warn("HashTourPool.add: ignoring infeasible tour %s (%s)", tour, err); } } return count; } /** * Check if {@code prevTour} visits the same requests as {@code tour}, if not increment the collision count * * @param prevTour * @param tour */ private void checkCollision(TRSPSimpleTour prevTour, ITRSPTour tour) { IntegerSet prevSet = new IntegerSet(tour.getSolution().getInstance().getMaxId()); for (int n : prevTour) prevSet.add(n); for (int n : tour) if (!prevSet.remove(n)) { mCollisionsCount++; break; } if (!prevSet.isEmpty()) mCollisionsCount++; } /* * (non-Javadoc) * @see vroom.trsp.datamodel.ITRSPTourPool#getAllTours() */ @Override public synchronized Collection<ITRSPTour> getAllTours() { ArrayList<ITRSPTour> tours = new ArrayList<ITRSPTour>(size()); for (int i = 0; i < mTourPool.length; i++) { tours.addAll(getPool(i).values()); } return tours; } /* * (non-Javadoc) * @see vroom.trsp.datamodel.ITRSPTourPool#size() */ @Override public int size() { return mSize; } @Override public synchronized void clear() { for (int i = 0; i < mTourPool.length; i++) { getPool(i).clear(); } mSize = 0; } @Override public synchronized void dispose() { clear(); } @Override public Iterator<ITRSPTour> iterator() { return getAllTours().iterator(); } }