package vroom.trsp.datamodel; import static org.junit.Assert.fail; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import org.junit.Before; import org.junit.Test; import umontreal.iro.lecuyer.rng.MRG32k3a; import umontreal.iro.lecuyer.rng.RandomPermutation; import umontreal.iro.lecuyer.rng.RandomStream; import vroom.common.modeling.dataModel.Depot; public abstract class TRSPSolutionHasherTest { private final static int EXP_REP = 100000; private final static int REQ_COUNT = 1000; private static final int TEC_COUNT = 50; private final static double MAX_COLLISIONS = 0.0001; private ITRSPSolutionHasher mHasher; private TRSPInstance mInstance; private Integer[] mNodes; protected abstract ITRSPSolutionHasher getHasher(TRSPInstance instance, RandomStream stream); protected final RandomStream getHasherStream() { MRG32k3a stream = new MRG32k3a("test-hash"); stream.setSeed(new long[] { 1, 2, 3, 4, 5, 6 }); return stream; } protected final RandomStream getTestStream() { MRG32k3a stream = new MRG32k3a("test-hash"); stream.setSeed(new long[] { 10, 20, 30, 40, 50, 60 }); return stream; } @Before public void setup() { mInstance = new TRSPInstance("test", new ArrayList<Technician>(), 0, 0, 0, new ArrayList<Depot>(), new ArrayList<TRSPRequest>(), false) { @Override public int getDepotCount() { return 0; } @Override public int getRequestCount() { return REQ_COUNT; } }; mHasher = getHasher(mInstance, getHasherStream()); mNodes = new Integer[REQ_COUNT]; for (int i = 0; i < mNodes.length; i++) { mNodes[i] = i; } } @Test public void hashTourTest() { RandomStream rnd = getTestStream(); HashMap<Integer, ITRSPTour> pool = new HashMap<Integer, ITRSPTour>(EXP_REP); int collisions = 0; for (int i = 0; i < EXP_REP; i++) { // Generate a tour // Length int l = rnd.nextInt((int) (0.8 * REQ_COUNT), REQ_COUNT); // Tour ITRSPTour tour = generateTour(l, rnd); // Check if tour was already present ITRSPTour prev = pool.put(tour.hashCode(), tour); if (prev != null && !equal(prev, tour)) { collisions++; System.out.printf("hashToutTest: Collision: hash=%s prev=%s new=%s\n", tour.hashCode(), prev.getNodeSeqString(), tour.getNodeSeqString()); } } int maxCol = (int) (MAX_COLLISIONS * EXP_REP); System.out.printf( "hashToutTest: %s collisions out of %s different tours (max: %s - it:%s)\n", collisions, pool.size(), maxCol, EXP_REP); if (collisions > maxCol) { fail(String.format("Number of collisions (%s) exceeds tolerance (%s)", collisions, maxCol)); } } protected abstract boolean equal(ITRSPTour a, ITRSPTour b); private final ITRSPTour generateTour(int length, RandomStream rnd) { RandomPermutation.shuffle(mNodes, rnd); Integer[] tour = Arrays.copyOf(mNodes, length); return new HashTestTour(tour, rnd.nextInt(0, TEC_COUNT)); } protected class HashTestTour extends TRSPTourBase implements Serializable { private static final long serialVersionUID = 1L; private final int mTechnicianId; private final int mHash; private final int[] mNodes; public HashTestTour(Integer[] tour, int tech) { mNodes = new int[tour.length]; for (int i = 0; i < tour.length; i++) { mNodes[i] = tour[i]; } mTechnicianId = tech; mHash = mHasher.hash(this); } @Override public int getTechnicianId() { return mTechnicianId; } @Override public TRSPInstance getInstance() { return mInstance; } @Override public boolean equals(Object obj) { return obj instanceof HashTestTour && ((HashTestTour) obj).mHash == this.mHash; } @Override public int hashCode() { return mHash; } @Override public int hashSolution() { return mHash; } @Override public ITourIterator iterator() { return new SimpleTourIterator(this); } @Override public double getTotalCost() { return 0; } @Override public int length() { return mNodes.length; } @Override public int[] asArray() { return new int[0]; } @Override public String toString() { StringBuilder sb = new StringBuilder(length() * 3); sb.append(String.format("t:%s c:%.2f l:%s <", getTechnicianId(), getTotalCost(), length())); Iterator<Integer> it = iterator(); while (it.hasNext()) { int n = it.next(); sb.append(n); if (it.hasNext()) sb.append(","); } sb.append(">"); return sb.toString(); } @Override public String getNodeSeqString() { StringBuilder sb = new StringBuilder(length() * 3); sb.append("<"); Iterator<Integer> it = iterator(); while (it.hasNext()) { int n = it.next(); sb.append(n); if (it.hasNext()) sb.append(","); } sb.append(">"); return sb.toString(); } @Override public int getFirstNode() { if (length() == 0) return ITRSPTour.UNDEFINED; return mNodes[0]; } @Override public int getLastNode() { if (length() == 0) return ITRSPTour.UNDEFINED; return mNodes[mNodes.length]; } @Override public boolean isVisited(int node) { for (int i = 0; i < mNodes.length; i++) { if (mNodes[i] == node) return true; } return false; } @Override public int getNodeAt(int index) { return mNodes[index]; } @Override public ITRSPTour clone() { return null; } } }