Hi
I have built a model that makes three consecutive runs, each with an additional set of jobs and each with the routes from the previous run set as initialVehicleRoutes. It looks something like this:
Utils. **LOGGER** .log(Level. **INFO** , "Commencing Run 1 with jobs=" + vrpBuilder.getAddedJobs().size());
VehicleRoutingProblemSolution solution = getSolution(problem, **null** , depotList, jobVehicleMap, maxPickupSitesPerRoute, maxDropsPerRoute, maxIterations);
vrpBuilder_run2.addInitialVehicleRoutes(solution.getRoutes());
problem_run2 = vrpBuilder_run2.build();
Utils. **LOGGER** .log(Level. **INFO** , "Commencing Run 2 with jobs=" + vrpBuilder_run2.getAddedJobs().size());
VehicleRoutingProblemSolution solution2 = getSolution(problem_run2, solution, depotList, jobVehicleMap, maxPickupSitesPerRoute, maxDropsPerRoute, maxIterations);
vrpBuilder_run3.addInitialVehicleRoutes(solution2.getRoutes());
problem_run3 = vrpBuilder_run3.build();
Utils. **LOGGER** .log(Level. **INFO** , "Commencing Run 3 with jobs=" + vrpBuilder_run3.getAddedJobs().size());
VehicleRoutingProblemSolution solution3 = getSolution(problem_run3, solution2, depotList, jobVehicleMap, maxPickupSitesPerRoute, maxDropsPerRoute, maxIterations);
On a sample data set the first run has 54 jobs, the 2nd run has 46 jobs.
The first run always runs ok but the 2nd run always throws an exception as follows:
threw exception [java.lang.ArrayIndexOutOfBoundsException: 48] with root cause
java.lang.ArrayIndexOutOfBoundsException: 48
at com.graphhopper.jsprit.core.algorithm.recreate.RegretInsertionConcurrentFast.updateInsertionData(RegretInsertionConcurrentFast.java:176)
at com.graphhopper.jsprit.core.algorithm.recreate.RegretInsertionConcurrentFast.insertUnassignedJobs(RegretInsertionConcurrentFast.java:149)
at com.graphhopper.jsprit.core.algorithm.recreate.AbstractInsertionStrategy.insertJobs(AbstractInsertionStrategy.java:91)
at com.graphhopper.jsprit.core.algorithm.module.RuinAndRecreateModule.runAndGetSolution(RuinAndRecreateModule.java:55)
at com.graphhopper.jsprit.core.algorithm.SearchStrategy.run(SearchStrategy.java:142)
at com.graphhopper.jsprit.core.algorithm.VehicleRoutingAlgorithm.searchSolutions(VehicleRoutingAlgorithm.java:224)
Note that the exception is on index 48 which I suspect relates to the 1st of the added jobs (pickup & delivery = 47,48).
If I comment out the addInitialVehicleRoutes lines of code and just run them as three runs with distinct jobs, it all works without a hitch.
As I build each “run” I create the vrpBuilder_run object with the original vehicle list. I assume that is the right thing to do.
Note however that I am using a custom ruinEnd routine and If I disable that routine the whole thing runs ok.
clearly there is some problematic interaction between addInitialVehicleRoutes and the ruinEnd. But I cant work out what.
Below is the ruinEnd code.
@Override
public void ruinEnds(Collection<VehicleRoute> routes, Collection<Job> unassignedJobs) {
Map<TourActivity, VehicleRoute> toDeleteActRouteMap = new HashMap<>();
for(VehicleRoute route : routes){
for (int i = 0; i < route.getActivities().size()-1; i++) {
TourActivity act1 = route.getActivities().get(i);
TourActivity act2 = route.getActivities().get(i + 1);
// ruin jobs less than 10 tonne
if(act1.getSize().get(VehicleTypes.CONSTRAINT_CAPACITY) < 10000) // ruin jobs less than 10 tonne
toDeleteActRouteMap.put(act1, route);
// we also check distance to the next delivery here - we ruin the next job
if(act2 instanceof DeliverShipment){
Job j1 = ((JobActivity)act1).getJob();
Job j2 = ((JobActivity)act2).getJob();
Double distance = jobDistance.getDistance(j1,j2);
if(distance > 15000){
toDeleteActRouteMap.put(act2, route);
}
}
}
RouteRuns runs = new RouteRuns(route);
Vehicle v = route.getVehicle();
int[] vehicleData =(int[])v.getUserData();
int minimumLoad = 0;
if(vehicleData == null){
Utils.LOGGER.log(Level.INFO, "UserData was null: " +v.getId());
minimumLoad = 4200;
} else {
minimumLoad = vehicleData[Constants.VEHICLE_MINIMUM_LOAD];
}
for(int i = 0; i < runs.size(); i++){
if(runs.getRunDemand(i) < minimumLoad){
ArrayList<Integer> run = runs.get(i);
for(int idx : run){
if(route.getActivities().get(idx) instanceof DeliverShipment){
toDeleteActRouteMap.put(route.getActivities().get(idx), route);
}
}
}
}
for (Map.Entry<TourActivity, VehicleRoute> entry : toDeleteActRouteMap.entrySet()) {
TourActivity act = entry.getKey();
VehicleRoute r = entry.getValue();
if (act instanceof TourActivity.JobActivity) {
Job job = ((TourActivity.JobActivity) act).getJob();
boolean removed = r.getTourActivities().removeJob(job);
if(removed){
//unassignedJobs.add(job); // this line throws a unsupportedOperationException
// dont know why yet
}
}
}
}
}