1
0
Fork 0
mirror of https://github.com/graphhopper/jsprit.git synced 2020-01-24 07:45:05 +01:00

add new and unique random instance to ensure reproducibility of several run within the same jvm

This commit is contained in:
oblonski 2015-04-24 19:56:06 +02:00
parent 7016f3baa3
commit 4efbd1cd1e
8 changed files with 216 additions and 82 deletions

View file

@ -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;
}
}

View file

@ -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<VehicleRoutingProblemSolution> 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;
}
}

View file

@ -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();
}

View file

@ -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<VehicleRoute> routes = new ArrayList<VehicleRoute>();
@ -71,8 +69,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;
private final static Vehicle NO_NEW_VEHICLE_YET = null;
@ -81,23 +77,21 @@ public final class BestInsertionConcurrent implements InsertionStrategy{
private InsertionListeners insertionsListeners;
private Inserter inserter;
private JobInsertionCostsCalculator bestInsertionCostCalculator;
private int nuOfBatches;
private ExecutorCompletionService<Insertion> 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<Insertion>(executorService);
logger.info("initialise " + this);
@ -109,16 +103,15 @@ public final class BestInsertionConcurrent implements InsertionStrategy{
}
@Override
public Collection<Job> insertJobs(Collection<VehicleRoute> vehicleRoutes, Collection<Job> unassignedJobs) {
insertionsListeners.informInsertionStarts(vehicleRoutes,unassignedJobs);
List<Job> badJobs = new ArrayList<Job>(unassignedJobs.size());
List<Job> unassignedJobList = new ArrayList<Job>(unassignedJobs);
public Collection<Job> insertUnassignedJobs(Collection<VehicleRoute> vehicleRoutes, Collection<Job> unassignedJobs) {
List<Job> badJobs = new ArrayList<Job>(unassignedJobs.size());
List<Job> unassignedJobList = new ArrayList<Job>(unassignedJobs);
Collections.shuffle(unassignedJobList, random);
List<Batch> batches = distributeRoutes(vehicleRoutes,nuOfBatches);
for(final Job unassignedJob : unassignedJobList){
Insertion bestInsertion = null;
List<Batch> 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<Insertion>() {
@Override
@ -127,18 +120,18 @@ public final class BestInsertionConcurrent implements InsertionStrategy{
}
});
}
try {
for (int i = 0; i < batches.size(); i++) {
Future<Insertion> 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<Insertion> 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) {
@ -146,34 +139,17 @@ public final class BestInsertionConcurrent implements InsertionStrategy{
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<InsertionListener> getListeners() {
return Collections.unmodifiableCollection(insertionsListeners.getListeners());
}
@Override
public void addListener(InsertionListener insertionListener) {
insertionsListeners.addListener(insertionListener);
return badJobs;
}
private Insertion getBestInsertion(Batch batch, Job unassignedJob) {

View file

@ -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<Double> departureTimeKnowledge = new ArrayList<Double>();
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())){

View file

@ -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<Cluster<LocationWrapper>> clusterResults = getClusters(route,locations);
if(clusterResults.isEmpty()) return Collections.emptyList();
Cluster<LocationWrapper> randomCluster = RandomUtils.nextItem(clusterResults, RandomNumberGeneration.getRandom());
Cluster<LocationWrapper> 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<noDistanceSamples;i++){
TourActivity act1 = RandomUtils.nextItem(r.getActivities(), random);
TourActivity act2 = RandomUtils.nextItem(r.getActivities(), random);

View file

@ -22,7 +22,6 @@ import jsprit.core.problem.job.Job;
import jsprit.core.problem.solution.VehicleRoutingProblemSolution;
import jsprit.core.problem.solution.route.VehicleRoute;
import jsprit.core.problem.solution.route.activity.TourActivity;
import jsprit.core.util.RandomNumberGeneration;
import jsprit.core.util.RandomUtils;
import org.apache.commons.math3.ml.clustering.Clusterable;
import org.apache.logging.log4j.LogManager;
@ -43,8 +42,8 @@ public final class RuinClusters extends AbstractRuinStrategy implements Iteratio
@Override
public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> 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<Job> cluster = dbscan.getRandomCluster(targetRoute);

View file

@ -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<Integer> firstRecord = new ArrayList<Integer>();
vra.addListener(new BeforeJobInsertionListener() {
@Override
public void informBeforeJobInsertion(Job job, InsertionData data, VehicleRoute route) {
firstRecord.add(data.getDeliveryInsertionIndex());
}
});
Collection<VehicleRoutingProblemSolution> firstSolutions = vra.searchSolutions();
// RandomNumberGeneration.reset();
VehicleRoutingAlgorithm second = Jsprit.createAlgorithm(vrp);
second.setMaxIterations(200);
final List<Integer> secondRecord = new ArrayList<Integer>();
second.addListener(new BeforeJobInsertionListener() {
@Override
public void informBeforeJobInsertion(Job job, InsertionData data, VehicleRoute route) {
secondRecord.add(data.getDeliveryInsertionIndex());
}
});
Collection<VehicleRoutingProblemSolution> secondSolutions = second.searchSolutions();
Assert.assertEquals(secondRecord.size(),firstRecord.size());
for(int i=0;i<firstRecord.size();i++){
if(!firstRecord.get(i).equals(secondRecord.get(i))){
Assert.assertFalse(true);
}
}
Assert.assertTrue(true);
Assert.assertEquals(Solutions.bestOf(firstSolutions).getCost(),Solutions.bestOf(secondSolutions).getCost());
}
@Test
public void whenBiggerProblem_ruinedJobsShouldBeReproducibleWithoutResetingRNGExplicitly(){
VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance();
new VrpXMLReader(vrpBuilder).read("src/test/resources/vrpnc1-jsprit-with-deliveries.xml");
VehicleRoutingProblem vrp = vrpBuilder.build();
VehicleRoutingAlgorithm vra = Jsprit.createAlgorithm(vrp);
vra.setMaxIterations(200);
final List<String> firstRecord = new ArrayList<String>();
vra.addListener(new RuinListener() {
@Override
public void ruinStarts(Collection<VehicleRoute> routes) {
}
@Override
public void ruinEnds(Collection<VehicleRoute> routes, Collection<Job> 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<String> secondRecord = new ArrayList<String>();
second.addListener(new RuinListener() {
@Override
public void ruinStarts(Collection<VehicleRoute> routes) {
}
@Override
public void ruinEnds(Collection<VehicleRoute> routes, Collection<Job> 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<firstRecord.size();i++){
if(!firstRecord.get(i).equals(secondRecord.get(i))){
Assert.assertFalse(true);
}
}
Assert.assertTrue(true);
}
}