0

This question is related to this topic : Related jobs in JSprit

I'm trying to use the "one before another" constraint but i'm experiencing a java.lang.IllegalArgumentException: arg must not be null . It looks like Capacity cap2 is null when calculating Capacity max. I don't really understand why.

:(

Do you have an idea about this?

For the record, I'm on the 1.6.2 version. TY for your help.

        String before = "2";
        String after = "11";

        final StateManager stateManager = new StateManager(problem);
        stateManager.addStateUpdater(new JobsInRouteMemorizer(stateManager));



        ConstraintManager constraintManager = new ConstraintManager(problem, stateManager);
        constraintManager.addConstraint(new OneJobBeforeAnother(stateManager, before, after));

        final RewardAndPenaltiesThroughSoftConstraints contrib = new RewardAndPenaltiesThroughSoftConstraints(problem, before, after);
        SolutionCostCalculator costCalculator = new SolutionCostCalculator() {

            @Override
            public double getCosts(VehicleRoutingProblemSolution solution) {
                double costs = 0.;
                List<VehicleRoute> routes = (List<VehicleRoute>) solution.getRoutes();
                for(VehicleRoute route : routes){
                    costs+=route.getVehicle().getType().getVehicleCostParams().fix;
                    costs+=stateManager.getRouteState(route, InternalStates.COSTS, Double.class);
                    costs+=contrib.getCosts(route);
                }
                return costs;
            }

        };
        VehicleRoutingAlgorithmBuilder vraBuilder = new VehicleRoutingAlgorithmBuilder(problem,
                "algorithmConfig.xml");
        vraBuilder.addCoreConstraints();
        vraBuilder.setStateAndConstraintManager(stateManager, constraintManager);
        vraBuilder.addDefaultCostCalculators();
        vraBuilder.setObjectiveFunction(costCalculator);
        algorithm = vraBuilder.build();



public class JobsInRouteMemorizer implements StateUpdater, ActivityVisitor {
private StateManager stateManager;
private VehicleRoute route;



public JobsInRouteMemorizer(StateManager stateManager) {
    super();
    this.stateManager = stateManager;
}

@Override
public void begin(VehicleRoute route) {
    this.route=route;
}

@Override
public void visit(TourActivity activity) {
    if(activity instanceof JobActivity){
        String jobId = ((JobActivity) activity).getJob().getId();
        StateId stateId = stateManager.createStateId(jobId);
        System.out.println(stateId.getIndex());
        System.out.println(stateId.toString());
        stateManager.putProblemState(stateId, VehicleRoute.class, this.route);
    }

}

@Override
public void finish() {}

}
Community
  • 1
  • 1

1 Answers1

0

Short answer: You cannot create StateId instances on the fly. All StateId instances have to be generated before the algorithm is run. See longer answer for why doing this is still not a good idea and you should consider a redesign.

Analysis: I ran into the same problem and traced it back to the way StateId instances are created in StateManager:

public StateId createStateId(String name) {
    if (createdStateIds.containsKey(name)) return createdStateIds.get(name);
    if (stateIndexCounter >= activityStates[0].length) {
        activityStates = new Object[vrp.getNuActivities() + 1][stateIndexCounter + 1];
        vehicleDependentActivityStates = new Object[nuActivities][nuVehicleTypeKeys][stateIndexCounter + 1];
        routeStatesArr = new Object[vrp.getNuActivities()+1][stateIndexCounter+1];
        vehicleDependentRouteStatesArr = new Object[nuActivities][nuVehicleTypeKeys][stateIndexCounter+1];
        problemStates = new Object[stateIndexCounter+1];
    }
    StateId id = StateFactory.createId(name, stateIndexCounter);
    incStateIndexCounter();
    createdStateIds.put(name, id);
    return id;
}

Each time you create a new StateId and there is no more space available for states the old state arrays are overwritten with a longer version to make space for your new state (at start there is space for 30 StateIds, a few already used by JSprit itself). As you can see, the old elements aren't copied over, so what happens here is a race condition between UpdateLoads, which sets the state used as cap2, your code, which generates a new StateId and overwrites the current state and UpdateMaxCapacityUtilisationAtActivitiesByLookingForwardInRoute which reads the state (that doesn't exist anymore).

Given that this code only extends the arrays by one it is very inefficient to have many StateIds, as for each new StateId all arrays have to be recreated. To mitigate this I used only one StateId in my code and stored a Map<String, VehicleRoute> in it:

Map<String, VehicleRoute> routeMapping = Optional.ofNullable(stateManager.getProblemState(stateId, Map.class)).orElse(new ConcurrentHashMap<>())

This way you don't run out of StateId instances and can still store relations between an unlimited number of jobs.

sgift
  • 137
  • 1
  • 2