/** * */ package vroom.trsp.optimization.matheuristic; import java.io.File; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Map; import vroom.common.heuristics.alns.ALNSEventType; import vroom.common.heuristics.alns.AdaptiveLargeNeighborhoodSearch; import vroom.common.heuristics.alns.IDestroy; import vroom.common.heuristics.alns.IRepair; import vroom.common.utilities.MutableInt; import vroom.common.utilities.StatCollector; import vroom.common.utilities.StatCollector.Label; import vroom.common.utilities.callbacks.CallbackBase; import vroom.common.utilities.callbacks.ICallbackEvent; import vroom.trsp.datamodel.ITRSPSolutionHasher; import vroom.trsp.datamodel.ITRSPTour; import vroom.trsp.datamodel.TRSPSolution; import vroom.trsp.datamodel.TRSPTour; /** * JAVADOC <code>SolutionPoolCallBack</code> * <p> * Creation date: Aug 12, 2011 - 11:51:53 AM * * @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 TourPoolStatCallBack extends CallbackBase<AdaptiveLargeNeighborhoodSearch<TRSPSolution>, ALNSEventType> { private final ITRSPSolutionHasher mHasher; private final Map<Integer, TourDef> mStats; private IDestroy<?> mLastDestroy; private int mGenCols; public static final Label<?>[] LABELS = new Label<?>[] { new Label<String>("Operator", String.class),// new Label<Integer>("GenTours", Integer.class),// new Label<Integer>("NonRepeated", Integer.class),// new Label<Integer>("Improvements", Integer.class),// new Label<Integer>("Exlusive", Integer.class), // new Label<Double>("Diversity", Double.class, new DecimalFormat("###0.00000")), // new Label<Double>("AbsoluteContr", Double.class, new DecimalFormat("###0.00000")), // new Label<Double>("RelativeContr", Double.class, new DecimalFormat("###0.00000")), // new Label<Integer>("GenCols", Integer.class), // new Label<Integer>("PoolSize", Integer.class) // }; /** * Getter for the hasher used to hash tours * * @return the hasher used to hash tours */ public ITRSPSolutionHasher getHasher() { return mHasher; } /** * Creates a new <code>SolutionPoolCallBack</code> * * @param instance * @param numIterations */ public TourPoolStatCallBack(ITRSPSolutionHasher hasher) { super(); mHasher = hasher; mStats = new HashMap<Integer, TourDef>(); mGenCols = 0; } @Override public void execute( ICallbackEvent<AdaptiveLargeNeighborhoodSearch<TRSPSolution>, ALNSEventType> event) { if (event.getType() == ALNSEventType.DESTROYED) { mLastDestroy = (IDestroy<?>) event.getParams()[3]; } if (event.getType() == ALNSEventType.REPAIRED) { IRepair<?> repair = (IRepair<?>) event.getParams()[3]; for (TRSPTour tour : (TRSPSolution) event.getParams()[2]) { if (tour.length() > 2) { int hash = mHasher.hash(tour); TourDef def = mStats.get(hash); if (def == null) { def = new TourDef(tour, hash); mStats.put(hash, def); } def.addStat(mLastDestroy, repair, tour.getTotalCost()); mGenCols++; } } } } /** * Store the statistics collected by the callback in a file * * @param statFile * the file in which statistics will be written * @param comment * a comment for the file * @param poolSize * the final size of the tour pool */ public void collectStats(File statFile, String comment, int poolSize) { StatCollector collector = new StatCollector(statFile, false, false, comment, LABELS); Map<String, int[]> stats = new HashMap<String, int[]>(); for (TourDef stat : mStats.values()) { for (String key : stat.mOpCount.keySet()) { int[] val = stats.get(key); if (val == null) { val = new int[4]; stats.put(key, val); } // Gen tours val[0] += stat.mOpCount.get(key).intValue(); // Non-repeated if (stat.mOpCount.get(key).intValue() == 1) val[1]++; // Improvements val[2] += stat.mOpImpCount.get(key).intValue(); // Exclusive if (stat.isUnique()) val[3] += 1; } } ArrayList<String> keys = new ArrayList<String>(stats.keySet()); Collections.sort(keys, new Comparator<String>() { @Override public int compare(String o1, String o2) { boolean c1 = o1.contains("+"); boolean c2 = o2.contains("+"); if ((c1 && c2) || !c1 && !c2) { return o1.compareTo(o2); } return c1 ? 1 : -1; } }); for (String key : keys) { int[] val = stats.get(key); Object[] values = new Object[LABELS.length]; int i = 0; values[i++] = key; values[i++] = val[0];// Gen tours values[i++] = val[1];// Non repeated values[i++] = val[2];// Improv values[i++] = val[3];// Exclusive values[i++] = val[1] / ((double) mGenCols); values[i++] = val[1] / ((double) poolSize); values[i++] = val[3] / ((double) poolSize); values[i++] = mGenCols; values[i++] = poolSize; collector.collect(values); } collector.flush(); collector.close(); } private static class TourDef { private final int mHash; private double mBestCost; private final Map<String, MutableInt> mOpCount; private final Map<String, MutableInt> mOpImpCount; private int mCount; private TourDef(ITRSPTour tour, int hash) { mHash = hash; mOpCount = new HashMap<String, MutableInt>(); mOpImpCount = new HashMap<String, MutableInt>(); mBestCost = Double.POSITIVE_INFINITY; mCount = 0; } private void addStat(IDestroy<?> destroy, IRepair<?> repair, double cost) { String combkey = String.format("%s+%s", destroy.getName(), repair.getName()); boolean added = false; added |= addStat(destroy.getName(), cost); added |= addStat(repair.getName(), cost); added |= addStat(combkey, cost); if (cost < mBestCost) mBestCost = cost; if(added) mCount++; } /** * Update the stats related to key * @param key a key for the operator * @param cost the cost of the generated tour * @return <code>true</code> if a new entry was created; <code>false</code> otherwise */ private boolean addStat(String key, double cost) { MutableInt val = mOpCount.get(key); boolean added =false; if (val == null) { val = new MutableInt(1); mOpCount.put(key, val); val = new MutableInt(0); mOpImpCount.put(key, val); added = true; } else val.increment(); if (cost < mBestCost) { mOpImpCount.get(key).increment(); } return added; } /** * @return <code>true</code> if this tour was found only once */ private boolean isUnique() { return mCount == 1; } } }