Hi Folks
I hope you are all enjoying the holiday season.
I am having a problem with a ruinEnds routine where an “UnsupportedOperationException” is being thrown on the line that reads “unassignedJobs.add(job);”. I cant work out why. Can anyone see what I am doing wrong ?
Thanks in advance
Grant
@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);
}
}
}
}
}
Hi @grantm009,
Ok, after reading your other post, I understand that this error is thrown not in the first run but in subsequent one. I’ve been able to reproduce the problem. See the codes I attach at the end.
The problem is, in the second run, all jobs are in the initial routes, thus number of (ruinable) jobs is 0. As a result, in radial ruin, nOfJobs2BeRemoved is 0, and it returns Collections.emptyList(), which is immutable. Thus you got the UnsupportedOperationException error.
What you can do is to deactivate radial ruin by:
algoBuilder.setProperty(Jsprit.Strategy.RADIAL_REGRET, "0");
or make a PR to make it return new ArrayList<Job>() instead.
Best regards,
He
private static void testRuinListeners() {
VehicleType vehicleType = VehicleTypeImpl.Builder.newInstance("car").build();
final Vehicle vehicle = VehicleImpl.Builder.newInstance("car1")
.setType(vehicleType)
.setStartLocation(Location.newInstance(0, 1))
.build();
Shipment s1 = Shipment.Builder.newInstance("s1")
.setPickupLocation(Location.newInstance(0, 0))
.setDeliveryLocation(Location.newInstance(0, 0))
.setPriority(1)
.build();
Shipment s2 = Shipment.Builder.newInstance("s2")
.setPickupLocation(Location.newInstance(0, 0))
.setDeliveryLocation(Location.newInstance(0, 0))
.setPriority(2)
.build();
Shipment s3 = Shipment.Builder.newInstance("s3")
.setPickupLocation(Location.newInstance(0, 0))
.setDeliveryLocation(Location.newInstance(0, 0))
.setPriority(3)
.build();
VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance();
vrpBuilder.addVehicle(vehicle);
vrpBuilder.setFleetSize(VehicleRoutingProblem.FleetSize.FINITE);
vrpBuilder.addJob(s1).addJob(s2);
VehicleRoutingProblem vrp = vrpBuilder.build();
VehicleRoutingProblemSolution solution = solveVrpWithRuinListeners(vrp);
VehicleRoute route = solution.getRoutes().iterator().next();
VehicleRoutingProblem.Builder newVrpBuilder = VehicleRoutingProblem.Builder.newInstance();
newVrpBuilder.setFleetSize(VehicleRoutingProblem.FleetSize.FINITE);
newVrpBuilder.addInitialVehicleRoute(route);
//newVrpBuilder.addJob(s3);
solveVrpWithRuinListeners(newVrpBuilder.build());
}
private static VehicleRoutingProblemSolution solveVrpWithRuinListeners(VehicleRoutingProblem vrp) {
Jsprit.Builder algoBuilder = Jsprit.Builder.newInstance(vrp);
VehicleRoutingAlgorithm algo = algoBuilder.buildAlgorithm();
algo.addListener(new RuinListener() {
@Override
public void ruinStarts(Collection<VehicleRoute> routes) {
}
@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);
toDeleteActRouteMap.put(act1, route);
toDeleteActRouteMap.put(act2, 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);
}
}
}
}
@Override
public void removed(Job job, VehicleRoute fromRoute) {
}
});
VehicleRoutingProblemSolution solution = Solutions.bestOf(algo.searchSolutions());
SolutionPrinter.print(vrp, solution, SolutionPrinter.Print.VERBOSE);
return solution;
}