From 4efbd1cd1e8c64436df73d10c30ccab2cec2c772 Mon Sep 17 00:00:00 2001 From: oblonski Date: Fri, 24 Apr 2015 19:56:06 +0200 Subject: [PATCH] add new and unique random instance to ensure reproducibility of several run within the same jvm --- .../algorithm/PrettyAlgorithmBuilder.java | 11 ++- .../algorithm/box/InsertionNoiseMaker.java | 10 +- .../jsprit/core/algorithm/box/Jsprit.java | 58 +++++++++-- .../recreate/BestInsertionConcurrent.java | 96 +++++++----------- ...tesServiceInsertionWithTimeScheduling.java | 9 +- .../core/algorithm/ruin/DBSCANClusterer.java | 9 +- .../core/algorithm/ruin/RuinClusters.java | 6 +- .../jsprit/core/algorithm/box/JspritTest.java | 99 +++++++++++++++++++ 8 files changed, 216 insertions(+), 82 deletions(-) diff --git a/jsprit-core/src/main/java/jsprit/core/algorithm/PrettyAlgorithmBuilder.java b/jsprit-core/src/main/java/jsprit/core/algorithm/PrettyAlgorithmBuilder.java index 55313c14..41b33089 100644 --- a/jsprit-core/src/main/java/jsprit/core/algorithm/PrettyAlgorithmBuilder.java +++ b/jsprit-core/src/main/java/jsprit/core/algorithm/PrettyAlgorithmBuilder.java @@ -33,10 +33,7 @@ import jsprit.core.problem.vehicle.VehicleFleetManager; import jsprit.core.problem.vehicle.VehicleTypeKey; import jsprit.core.util.ActivityTimeTracker; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; +import java.util.*; /** * Created by schroeder on 10.12.14. @@ -71,6 +68,11 @@ public class PrettyAlgorithmBuilder { this.searchStrategyManager = new SearchStrategyManager(); } + public PrettyAlgorithmBuilder setRandom(Random random){ + searchStrategyManager.setRandom(random); + return this; + } + public PrettyAlgorithmBuilder withStrategy(SearchStrategy strategy, double weight){ searchStrategyManager.addStrategy(strategy,weight); return this; @@ -164,4 +166,5 @@ public class PrettyAlgorithmBuilder { return this; } + } diff --git a/jsprit-core/src/main/java/jsprit/core/algorithm/box/InsertionNoiseMaker.java b/jsprit-core/src/main/java/jsprit/core/algorithm/box/InsertionNoiseMaker.java index 6c7a768a..f8acbbca 100644 --- a/jsprit-core/src/main/java/jsprit/core/algorithm/box/InsertionNoiseMaker.java +++ b/jsprit-core/src/main/java/jsprit/core/algorithm/box/InsertionNoiseMaker.java @@ -15,6 +15,7 @@ import jsprit.core.util.RandomNumberGeneration; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Random; /** * Created by schroeder on 16/01/15. @@ -31,6 +32,8 @@ class InsertionNoiseMaker implements SoftActivityConstraint, IterationStartsList private double noiseLevel = 0.1; + private Random random = RandomNumberGeneration.getRandom(); + public InsertionNoiseMaker(VehicleRoutingProblem vrp, double noiseLevel, double noiseProbability) { this.vrp = vrp; this.noiseLevel = noiseLevel; @@ -70,7 +73,7 @@ class InsertionNoiseMaker implements SoftActivityConstraint, IterationStartsList @Override public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection solutions) { - if(RandomNumberGeneration.getRandom().nextDouble() < noiseProbability){ + if(random.nextDouble() < noiseProbability){ makeNoise = true; } else makeNoise = false; @@ -79,10 +82,13 @@ class InsertionNoiseMaker implements SoftActivityConstraint, IterationStartsList @Override public double getCosts(JobInsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double prevActDepTime) { if(makeNoise) { - return noiseLevel * maxCosts * RandomNumberGeneration.getRandom().nextDouble(); + return noiseLevel * maxCosts * random.nextDouble(); } return 0; } + public void setRandom(Random random) { + this.random = random; + } } diff --git a/jsprit-core/src/main/java/jsprit/core/algorithm/box/Jsprit.java b/jsprit-core/src/main/java/jsprit/core/algorithm/box/Jsprit.java index 0afe2038..ab74617e 100644 --- a/jsprit-core/src/main/java/jsprit/core/algorithm/box/Jsprit.java +++ b/jsprit-core/src/main/java/jsprit/core/algorithm/box/Jsprit.java @@ -28,6 +28,7 @@ import jsprit.core.util.Solutions; import java.util.Collection; import java.util.Properties; +import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -129,6 +130,8 @@ public class Jsprit { private boolean addConstraints = true; + private Random random = RandomNumberGeneration.newInstance(); + public static Builder newInstance(VehicleRoutingProblem vrp){ return new Builder(vrp); } @@ -186,6 +189,11 @@ public class Jsprit { return this; } + public Builder setRandom(Random random){ + this.random = random; + return this; + } + public Builder setProperty(String key, String value){ properties.put(key,value); return this; @@ -231,15 +239,28 @@ public class Jsprit { private int minShare; + private Random random = RandomNumberGeneration.getRandom(); + + public void setRandom(Random random) { + this.random = random; + } + public RuinShareFactoryImpl(int minShare, int maxShare) { if(maxShare < minShare) throw new IllegalArgumentException("maxShare must be equal or greater than minShare"); this.minShare = minShare; this.maxShare = maxShare; } + public RuinShareFactoryImpl(int minShare, int maxShare, Random random) { + if(maxShare < minShare) throw new IllegalArgumentException("maxShare must be equal or greater than minShare"); + this.minShare = minShare; + this.maxShare = maxShare; + this.random = random; + } + @Override public int createNumberToBeRemoved() { - return (int) (minShare + (maxShare - minShare) * RandomNumberGeneration.getRandom().nextDouble()); + return (int) (minShare + (maxShare - minShare) * random.nextDouble()); } } @@ -260,6 +281,8 @@ public class Jsprit { private Properties properties; + private Random random; + private Jsprit(Builder builder) { this.stateManager = builder.stateManager; this.constraintManager = builder.constraintManager; @@ -268,6 +291,7 @@ public class Jsprit { this.addCoreConstraints = builder.addConstraints; this.properties = builder.properties; this.objectiveFunction = builder.objectiveFunction; + this.random = builder.random; } private VehicleRoutingAlgorithm create(final VehicleRoutingProblem vrp){ @@ -287,33 +311,42 @@ public class Jsprit { double noiseLevel = toDouble(getProperty(Parameter.INSERTION_NOISE_LEVEL.toString())); double noiseProbability = toDouble(getProperty(Parameter.INSERTION_NOISE_PROB.toString())); final InsertionNoiseMaker noiseMaker = new InsertionNoiseMaker(vrp, noiseLevel, noiseProbability); + noiseMaker.setRandom(random); constraintManager.addConstraint(noiseMaker); JobNeighborhoods jobNeighborhoods = new JobNeighborhoodsFactory().createNeighborhoods(vrp, new AvgServiceAndShipmentDistance(vrp.getTransportCosts()), (int) (vrp.getJobs().values().size() * 0.5)); jobNeighborhoods.initialise(); RuinRadial radial = new RuinRadial(vrp,vrp.getJobs().size(),jobNeighborhoods); + radial.setRandom(random); radial.setRuinShareFactory(new RuinShareFactoryImpl( toInteger(properties.getProperty(Parameter.RADIAL_MIN_SHARE.toString())), - toInteger(properties.getProperty(Parameter.RADIAL_MAX_SHARE.toString()))) + toInteger(properties.getProperty(Parameter.RADIAL_MAX_SHARE.toString())), + random) ); final RuinRandom random_for_regret = new RuinRandom(vrp,0.5); + random_for_regret.setRandom(random); random_for_regret.setRuinShareFactory(new RuinShareFactoryImpl( toInteger(properties.getProperty(Parameter.RANDOM_REGRET_MIN_SHARE.toString())), - toInteger(properties.getProperty(Parameter.RANDOM_REGRET_MAX_SHARE.toString()))) + toInteger(properties.getProperty(Parameter.RANDOM_REGRET_MAX_SHARE.toString())), + random) ); final RuinRandom random_for_best = new RuinRandom(vrp,0.5); + random_for_best.setRandom(random); random_for_best.setRuinShareFactory(new RuinShareFactoryImpl( toInteger(properties.getProperty(Parameter.RANDOM_BEST_MIN_SHARE.toString())), - toInteger(properties.getProperty(Parameter.RANDOM_BEST_MAX_SHARE.toString()))) + toInteger(properties.getProperty(Parameter.RANDOM_BEST_MAX_SHARE.toString())), + random) ); final RuinWorst worst = new RuinWorst(vrp, (int) (vrp.getJobs().values().size()*0.5)); + worst.setRandom(random); worst.setRuinShareFactory(new RuinShareFactoryImpl( toInteger(properties.getProperty(Parameter.WORST_MIN_SHARE.toString())), - toInteger(properties.getProperty(Parameter.WORST_MAX_SHARE.toString()))) + toInteger(properties.getProperty(Parameter.WORST_MAX_SHARE.toString())), + random) ); IterationStartsListener noise = new IterationStartsListener() { @Override @@ -321,9 +354,9 @@ public class Jsprit { worst.setNoiseMaker(new NoiseMaker() { public double makeNoise() { - if(RandomNumberGeneration.getRandom().nextDouble() < toDouble(getProperty(Parameter.RUIN_WORST_NOISE_PROB.toString()))) { + if(random.nextDouble() < toDouble(getProperty(Parameter.RUIN_WORST_NOISE_PROB.toString()))) { return toDouble(getProperty(Parameter.RUIN_WORST_NOISE_LEVEL.toString())) - * noiseMaker.maxCosts * RandomNumberGeneration.getRandom().nextDouble(); + * noiseMaker.maxCosts * random.nextDouble(); } else return 0.; } @@ -332,12 +365,14 @@ public class Jsprit { }; final RuinClusters clusters = new RuinClusters(vrp,(int) (vrp.getJobs().values().size()*0.5),jobNeighborhoods); + clusters.setRandom(random); clusters.setRuinShareFactory(new RuinShareFactoryImpl( toInteger(properties.getProperty(Parameter.WORST_MIN_SHARE.toString())), - toInteger(properties.getProperty(Parameter.WORST_MAX_SHARE.toString()))) + toInteger(properties.getProperty(Parameter.WORST_MAX_SHARE.toString())), + random) ); - InsertionStrategy regret; + AbstractInsertionStrategy regret; final RegretInsertion.DefaultScorer scorer; if(noThreads == null){ noThreads = toInteger(getProperty(Parameter.THREADS.toString())); @@ -369,8 +404,9 @@ public class Jsprit { regretInsertion.setScoringFunction(scorer); regret = regretInsertion; } + regret.setRandom(random); - InsertionStrategy best; + AbstractInsertionStrategy best; if(vrp.getJobs().size() < 250 || es == null) { BestInsertion bestInsertion = (BestInsertion) new InsertionBuilder(vrp, fm, stateManager, constraintManager) .setInsertionStrategy(InsertionBuilder.Strategy.BEST) @@ -388,6 +424,7 @@ public class Jsprit { .build(); best = bestInsertion; } + best.setRandom(random); final SchrimpfAcceptance schrimpfAcceptance = new SchrimpfAcceptance(1,toDouble(getProperty(Parameter.THRESHOLD_ALPHA.toString()))); IterationStartsListener schrimpfThreshold = new IterationStartsListener() { @@ -428,6 +465,7 @@ public class Jsprit { PrettyAlgorithmBuilder prettyBuilder = PrettyAlgorithmBuilder.newInstance(vrp, fm, stateManager, constraintManager); + prettyBuilder.setRandom(random); if(addCoreConstraints){ prettyBuilder.addCoreStateAndConstraintStuff(); } diff --git a/jsprit-core/src/main/java/jsprit/core/algorithm/recreate/BestInsertionConcurrent.java b/jsprit-core/src/main/java/jsprit/core/algorithm/recreate/BestInsertionConcurrent.java index 90ec9084..0104f022 100644 --- a/jsprit-core/src/main/java/jsprit/core/algorithm/recreate/BestInsertionConcurrent.java +++ b/jsprit-core/src/main/java/jsprit/core/algorithm/recreate/BestInsertionConcurrent.java @@ -17,14 +17,12 @@ package jsprit.core.algorithm.recreate; import jsprit.core.algorithm.recreate.InsertionData.NoInsertionFound; -import jsprit.core.algorithm.recreate.listener.InsertionListener; import jsprit.core.algorithm.recreate.listener.InsertionListeners; import jsprit.core.problem.VehicleRoutingProblem; import jsprit.core.problem.driver.Driver; import jsprit.core.problem.job.Job; import jsprit.core.problem.solution.route.VehicleRoute; import jsprit.core.problem.vehicle.Vehicle; -import jsprit.core.util.RandomNumberGeneration; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -40,7 +38,7 @@ import java.util.concurrent.*; * */ -public final class BestInsertionConcurrent implements InsertionStrategy{ +public final class BestInsertionConcurrent extends AbstractInsertionStrategy{ static class Batch { List routes = new ArrayList(); @@ -70,8 +68,6 @@ public final class BestInsertionConcurrent implements InsertionStrategy{ } private static Logger logger = LogManager.getLogger(BestInsertionConcurrent.class); - - private Random random = RandomNumberGeneration.getRandom(); private final static double NO_NEW_DEPARTURE_TIME_YET = -12345.12345; @@ -81,23 +77,21 @@ public final class BestInsertionConcurrent implements InsertionStrategy{ private InsertionListeners insertionsListeners; - private Inserter inserter; - private JobInsertionCostsCalculator bestInsertionCostCalculator; private int nuOfBatches; private ExecutorCompletionService completionService; + @Deprecated public void setRandom(Random random) { - this.random = random; + super.random = random; } public BestInsertionConcurrent(JobInsertionCostsCalculator jobInsertionCalculator, ExecutorService executorService, int nuOfBatches, VehicleRoutingProblem vehicleRoutingProblem) { - super(); + super(vehicleRoutingProblem); this.insertionsListeners = new InsertionListeners(); this.nuOfBatches = nuOfBatches; - inserter = new Inserter(insertionsListeners, vehicleRoutingProblem); bestInsertionCostCalculator = jobInsertionCalculator; completionService = new ExecutorCompletionService(executorService); logger.info("initialise " + this); @@ -109,71 +103,53 @@ public final class BestInsertionConcurrent implements InsertionStrategy{ } @Override - public Collection insertJobs(Collection vehicleRoutes, Collection unassignedJobs) { - insertionsListeners.informInsertionStarts(vehicleRoutes,unassignedJobs); - List badJobs = new ArrayList(unassignedJobs.size()); - List unassignedJobList = new ArrayList(unassignedJobs); + public Collection insertUnassignedJobs(Collection vehicleRoutes, Collection unassignedJobs) { + List badJobs = new ArrayList(unassignedJobs.size()); + List unassignedJobList = new ArrayList(unassignedJobs); Collections.shuffle(unassignedJobList, random); - List batches = distributeRoutes(vehicleRoutes,nuOfBatches); - for(final Job unassignedJob : unassignedJobList){ - Insertion bestInsertion = null; + List batches = distributeRoutes(vehicleRoutes,nuOfBatches); + for(final Job unassignedJob : unassignedJobList){ + Insertion bestInsertion = null; double bestInsertionCost = Double.MAX_VALUE; - for(final Batch batch : batches){ + for(final Batch batch : batches){ completionService.submit(new Callable() { - + @Override public Insertion call() throws Exception { return getBestInsertion(batch,unassignedJob); } - + }); - } - try { - for (int i = 0; i < batches.size(); i++) { - Future futureIData = completionService.take(); - Insertion insertion = futureIData.get(); - if (insertion == null) continue; - if (insertion.getInsertionData().getInsertionCost() < bestInsertionCost) { - bestInsertion = insertion; - bestInsertionCost = insertion.getInsertionData().getInsertionCost(); - } - } - } catch(InterruptedException e){ + } + try { + for (int i = 0; i < batches.size(); i++) { + Future futureIData = completionService.take(); + Insertion insertion = futureIData.get(); + if (insertion == null) continue; + if (insertion.getInsertionData().getInsertionCost() < bestInsertionCost) { + bestInsertion = insertion; + bestInsertionCost = insertion.getInsertionData().getInsertionCost(); + } + } + } catch(InterruptedException e){ Thread.currentThread().interrupt(); - } + } catch (ExecutionException e) { e.printStackTrace(); logger.error(e.getCause().toString()); System.exit(1); } - VehicleRoute newRoute = VehicleRoute.emptyRoute(); - InsertionData newIData = bestInsertionCostCalculator.getInsertionData(newRoute, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, bestInsertionCost); - if(newIData.getInsertionCost() < bestInsertionCost){ - bestInsertion = new Insertion(newRoute,newIData); - vehicleRoutes.add(newRoute); - batches.get(random.nextInt(batches.size())).routes.add(newRoute); - } - if(bestInsertion == null) badJobs.add(unassignedJob); - else inserter.insertJob(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute()); + VehicleRoute newRoute = VehicleRoute.emptyRoute(); + InsertionData newIData = bestInsertionCostCalculator.getInsertionData(newRoute, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, bestInsertionCost); + if(newIData.getInsertionCost() < bestInsertionCost){ + bestInsertion = new Insertion(newRoute,newIData); + vehicleRoutes.add(newRoute); + batches.get(random.nextInt(batches.size())).routes.add(newRoute); + } + if(bestInsertion == null) badJobs.add(unassignedJob); + else insertJob(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute()); } - insertionsListeners.informInsertionEndsListeners(vehicleRoutes); - return badJobs; - } - - @Override - public void removeListener(InsertionListener insertionListener) { - insertionsListeners.removeListener(insertionListener); - } - - @Override - public Collection getListeners() { - return Collections.unmodifiableCollection(insertionsListeners.getListeners()); - } - - @Override - public void addListener(InsertionListener insertionListener) { - insertionsListeners.addListener(insertionListener); - + return badJobs; } private Insertion getBestInsertion(Batch batch, Job unassignedJob) { diff --git a/jsprit-core/src/main/java/jsprit/core/algorithm/recreate/CalculatesServiceInsertionWithTimeScheduling.java b/jsprit-core/src/main/java/jsprit/core/algorithm/recreate/CalculatesServiceInsertionWithTimeScheduling.java index 2f8a2abd..61388f7a 100644 --- a/jsprit-core/src/main/java/jsprit/core/algorithm/recreate/CalculatesServiceInsertionWithTimeScheduling.java +++ b/jsprit-core/src/main/java/jsprit/core/algorithm/recreate/CalculatesServiceInsertionWithTimeScheduling.java @@ -12,6 +12,7 @@ import org.apache.logging.log4j.Logger; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Random; class CalculatesServiceInsertionWithTimeScheduling implements JobInsertionCostsCalculator{ @@ -45,6 +46,12 @@ class CalculatesServiceInsertionWithTimeScheduling implements JobInsertionCostsC private List departureTimeKnowledge = new ArrayList(); + public void setRandom(Random random) { + this.random = random; + } + + private Random random = RandomNumberGeneration.getRandom(); + CalculatesServiceInsertionWithTimeScheduling(JobInsertionCostsCalculator jic, double t, double f) { super(); this.jic = jic; @@ -61,7 +68,7 @@ class CalculatesServiceInsertionWithTimeScheduling implements JobInsertionCostsC double departureTime = newVehicleDepartureTime; if(currentRoute.isEmpty()){ if(!departureTimeKnowledge.isEmpty()){ - departureTime = departureTimeKnowledge.get(RandomNumberGeneration.getRandom().nextInt(departureTimeKnowledge.size())); + departureTime = departureTimeKnowledge.get(random.nextInt(departureTimeKnowledge.size())); } } else if(!currentRoute.getVehicle().getId().equals(newVehicle.getId())){ diff --git a/jsprit-core/src/main/java/jsprit/core/algorithm/ruin/DBSCANClusterer.java b/jsprit-core/src/main/java/jsprit/core/algorithm/ruin/DBSCANClusterer.java index d93b0bcb..a0ad4b9f 100644 --- a/jsprit-core/src/main/java/jsprit/core/algorithm/ruin/DBSCANClusterer.java +++ b/jsprit-core/src/main/java/jsprit/core/algorithm/ruin/DBSCANClusterer.java @@ -103,6 +103,12 @@ public class DBSCANClusterer { private Double epsDistance; + private Random random = RandomNumberGeneration.getRandom(); + + public void setRandom(Random random) { + this.random = random; + } + public DBSCANClusterer(VehicleRoutingTransportCosts costs) { this.costs = costs; } @@ -162,14 +168,13 @@ public class DBSCANClusterer { } List> clusterResults = getClusters(route,locations); if(clusterResults.isEmpty()) return Collections.emptyList(); - Cluster randomCluster = RandomUtils.nextItem(clusterResults, RandomNumberGeneration.getRandom()); + Cluster randomCluster = RandomUtils.nextItem(clusterResults, random); return getJobList(randomCluster); } private double sample(VehicleRoutingTransportCosts costs, VehicleRoute r) { double min = Double.MAX_VALUE; double sum = 0; - Random random = RandomNumberGeneration.getRandom(); for(int i=0;i solutions) { - minPts = 1 + RandomNumberGeneration.getRandom().nextInt(2); - epsFactor = 0.5 + RandomNumberGeneration.getRandom().nextDouble(); + minPts = 1 + random.nextInt(2); + epsFactor = 0.5 + random.nextDouble(); } public static class JobActivityWrapper implements Clusterable { @@ -151,6 +150,7 @@ public final class RuinClusters extends AbstractRuinStrategy implements Iteratio break; } DBSCANClusterer dbscan = new DBSCANClusterer(vrp.getTransportCosts()); + dbscan.setRandom(random); dbscan.setMinPts(minPts); dbscan.setEpsFactor(epsFactor); List cluster = dbscan.getRandomCluster(targetRoute); diff --git a/jsprit-core/src/test/java/jsprit/core/algorithm/box/JspritTest.java b/jsprit-core/src/test/java/jsprit/core/algorithm/box/JspritTest.java index a5ac4705..7a3f2c33 100644 --- a/jsprit-core/src/test/java/jsprit/core/algorithm/box/JspritTest.java +++ b/jsprit-core/src/test/java/jsprit/core/algorithm/box/JspritTest.java @@ -511,6 +511,7 @@ public class JspritTest { Assert.assertEquals(Solutions.bestOf(firstSolutions).getCost(),Solutions.bestOf(secondSolutions).getCost()); } + @Test public void whenTerminatingWithVariationCoefficient_terminationShouldBeReproducible(){ @@ -558,4 +559,102 @@ public class JspritTest { Assert.assertEquals(Solutions.bestOf(firstSolutions).getCost(),Solutions.bestOf(secondSolutions).getCost()); } + @Test + public void whenBiggerProblem_insertioPositionsShouldBeReproducibleWithoutResetingRNGExplicitly(){ + + VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance(); + new VrpXMLReader(vrpBuilder).read("src/test/resources/vrpnc1-jsprit-with-deliveries.xml"); + VehicleRoutingProblem vrp = vrpBuilder.build(); + + RandomNumberGeneration.reset(); + VehicleRoutingAlgorithm vra = Jsprit.createAlgorithm(vrp); + vra.setMaxIterations(200); + final List firstRecord = new ArrayList(); + vra.addListener(new BeforeJobInsertionListener() { + @Override + public void informBeforeJobInsertion(Job job, InsertionData data, VehicleRoute route) { + firstRecord.add(data.getDeliveryInsertionIndex()); + } + }); + Collection firstSolutions = vra.searchSolutions(); + +// RandomNumberGeneration.reset(); + VehicleRoutingAlgorithm second = Jsprit.createAlgorithm(vrp); + second.setMaxIterations(200); + final List secondRecord = new ArrayList(); + second.addListener(new BeforeJobInsertionListener() { + @Override + public void informBeforeJobInsertion(Job job, InsertionData data, VehicleRoute route) { + secondRecord.add(data.getDeliveryInsertionIndex()); + } + }); + Collection secondSolutions = second.searchSolutions(); + + Assert.assertEquals(secondRecord.size(),firstRecord.size()); + for(int i=0;i firstRecord = new ArrayList(); + vra.addListener(new RuinListener() { + @Override + public void ruinStarts(Collection routes) { + + } + + @Override + public void ruinEnds(Collection routes, Collection unassignedJobs) { + + } + + @Override + public void removed(Job job, VehicleRoute fromRoute) { + firstRecord.add(job.getId()); + } + }); + vra.searchSolutions(); + +// RandomNumberGeneration.reset(); + VehicleRoutingAlgorithm second = Jsprit.createAlgorithm(vrp); + second.setMaxIterations(200); + final List secondRecord = new ArrayList(); + second.addListener(new RuinListener() { + @Override + public void ruinStarts(Collection routes) { + + } + + @Override + public void ruinEnds(Collection routes, Collection unassignedJobs) { + + } + + @Override + public void removed(Job job, VehicleRoute fromRoute) { + secondRecord.add(job.getId()); + } + }); + second.searchSolutions(); + + Assert.assertEquals(secondRecord.size(),firstRecord.size()); + for(int i=0;i