Problem with Ruin code

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