graphhopper/jsprit

Solution is crashed if there is any unassigned drivers in solution when using MaxTimeInVehicleConstraint

kienthuan opened this issue · 3 comments

MaxTimeInVehicleConstraint got crashed in fulfilled(...) method if there is any unassigned drivers in solution.

This will cause whole solution got error when we can still have solution with one drive.

Please try MaxTimeInVehicle with multi-vehicle and there is an unassigned driver in solution.

I move whole code in method above inside try/catch block and everything works fine.

I met the same question of Null pointer exception in MaxTimeInVehicleConstraint (line:106), but I think that is not because of the unassigned drivers, but the different types of vehicle.
If I set 2 vehicles with the same vehicle type(only one assigned), the program works fine, but if set 2 vehicles with different type, program crashed.

Here is the code, based on MaxTimeInVehicle_IT
Jsprit version is 1.7.3

@Test
public void maxTimeNullPointer() {
    Shipment s1 = Shipment.Builder.newInstance("s1").setPickupLocation(Location.newInstance(0, 0))
            .setDeliveryLocation(Location.newInstance(100, 0))
            .setDeliveryServiceTime(10).setMaxTimeInVehicle(100d).build();
    Shipment s2 = Shipment.Builder.newInstance("s2").setPickupLocation(Location.newInstance(0, 0))
            .setDeliveryLocation(Location.newInstance(100, 0))
            .setDeliveryServiceTime(10).setMaxTimeInVehicle(100d).build();

    VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("type1").addCapacityDimension(0, 10).build();
    VehicleTypeImpl type2 = VehicleTypeImpl.Builder.newInstance("type2").addCapacityDimension(0, 7).build();

    VehicleImpl v1 = VehicleImpl.Builder.newInstance("v1").setStartLocation(Location.newInstance(0, 0)).setType(type1).build();

    // *****  if the type of v2 change to type1, the program works fine.  ***** 
    VehicleImpl v2 = VehicleImpl.Builder.newInstance("v2").setStartLocation(Location.newInstance(0, 0)).setType(type2).build();

    VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance()
            .addVehicle(v1).addVehicle(v2).addJob(s1).addJob(s2).build();

    StateManager stateManager = new StateManager(vrp);
    StateId id = stateManager.createStateId("max-time");
    StateId openJobsId = stateManager.createStateId("open-jobs-id");
    stateManager.addStateUpdater(new UpdateMaxTimeInVehicle(stateManager, id, vrp.getTransportCosts(), vrp.getActivityCosts(), openJobsId));

    ConstraintManager constraintManager = new ConstraintManager(vrp, stateManager);
    constraintManager.addConstraint(new MaxTimeInVehicleConstraint(vrp.getTransportCosts(), vrp.getActivityCosts(), id, stateManager, vrp, openJobsId), ConstraintManager.Priority.CRITICAL);

    VehicleRoutingAlgorithm vra = Jsprit.Builder.newInstance(vrp)
            .setStateAndConstraintManager(stateManager, constraintManager)
            .buildAlgorithm();
    VehicleRoutingProblemSolution solution = Solutions.bestOf(vra.searchSolutions());

     SolutionPrinter.print(vrp, solution, SolutionPrinter.Print.VERBOSE);
}

And the exception stacktrace

java.lang.NullPointerException
	at com.graphhopper.jsprit.core.problem.constraint.MaxTimeInVehicleConstraint.fulfilled(MaxTimeInVehicleConstraint.java:106)
	at com.graphhopper.jsprit.core.algorithm.recreate.AbstractInsertionCalculator.fulfilled(AbstractInsertionCalculator.java:52)
	at com.graphhopper.jsprit.core.algorithm.recreate.ShipmentInsertionCalculator.getInsertionData(ShipmentInsertionCalculator.java:148)
	at com.graphhopper.jsprit.core.algorithm.recreate.JobCalculatorSwitcher.getInsertionData(JobCalculatorSwitcher.java:40)
	at com.graphhopper.jsprit.core.algorithm.recreate.VehicleTypeDependentJobInsertionCalculator.getInsertionData(VehicleTypeDependentJobInsertionCalculator.java:116)
	at com.graphhopper.jsprit.core.algorithm.recreate.RegretInsertion.getScoredJob(RegretInsertion.java:169)
	at com.graphhopper.jsprit.core.algorithm.recreate.RegretInsertion.nextJob(RegretInsertion.java:140)
	at com.graphhopper.jsprit.core.algorithm.recreate.RegretInsertion.insertUnassignedJobs(RegretInsertion.java:112)
	at com.graphhopper.jsprit.core.algorithm.recreate.AbstractInsertionStrategy.insertJobs(AbstractInsertionStrategy.java:91)
	at com.graphhopper.jsprit.core.algorithm.InsertionInitialSolutionFactory.createSolution(InsertionInitialSolutionFactory.java:56)
	at com.graphhopper.jsprit.core.algorithm.PrettyAlgorithmBuilder$1.informAlgorithmStarts(PrettyAlgorithmBuilder.java:116)
	at com.graphhopper.jsprit.core.algorithm.listener.VehicleRoutingAlgorithmListeners.algorithmStarts(VehicleRoutingAlgorithmListeners.java:128)
	at com.graphhopper.jsprit.core.algorithm.VehicleRoutingAlgorithm.algorithmStarts(VehicleRoutingAlgorithm.java:334)
	at com.graphhopper.jsprit.core.algorithm.VehicleRoutingAlgorithm.searchSolutions(VehicleRoutingAlgorithm.java:222)
	at com.udiannet.jsprit.JspritTest.maxTimeNullPointer(JspritTest.java:396)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:628)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:117)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:184)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:180)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:127)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)

Since vehicle switch does not matter for this constraint, I've used the simple fix to replace line 106 in MaxTimeInVehicleConstraint with the following
Double d = stateManager.getActivityState(nextAct, iFacts.getNewVehicle(), minSlackId, Double.class);
if(d == null) {
d = stateManager.getActivityState(nextAct, iFacts.getRoute().getVehicle(), minSlackId, Double.class);
}
minSlack = d;

Basically to check previous vehicle if minSlack data is not found for new vehicle.

Thanks, @sdTolik that's a huge help. I also needed a similar patch around line 144

					if ( nextAfterPickup != null ) {
						openJobsAtNextOfPickup = stateManager.getActivityState(nextAfterPickup, iFacts.getNewVehicle(), openJobsId, Map.class);
						if (openJobsAtNextOfPickup == null) {
							// same patch as above, use different vehicle to get state
							openJobsAtNextOfPickup = stateManager.getActivityState(nextAfterPickup, iFacts.getRoute().getVehicle(), openJobsId, Map.class);
						}
					}