Shadow variables, PlanningVariableListener and bi-directional variables

Most types of custom constraints are relatively straightforward to implement in OptaPlanner. There was one notorious exception: time windows for vehicle routing. But OptaPlanner 6.0.0.Beta4 supports shadow variables, which make that a lot easier to implement.

A shadow variable is a variables who’s correct value can be deduced from the state of the genuine planning variables. Even though such a variable violates the principle of normalization by definition, in some use cases it can be very practical to use a shadow variable. For example in vehicle routing with time windows: the arrival time at a customer for a vehicle can be calculated based on the previously visited customers of that vehicle (and the known travel times between 2 locations).


To update the shadow variable(s) correctly, we can annotate a PlanningVariableListener on the genuine planning variable:

    @PlanningVariable(…, variableListenerClasses = {VehicleUpdatingVariableListener.class, ArrivalTimeUpdatingVariableListener.class})
    public Standstill getPreviousStandstill() {
        return previousStandstill;

Bi-directional variables

OptaPlanner reuses that system internally to provide support for bi-directional variables (like JPA), currently only for chained variables:

    @PlanningVariable(mappedBy = “previousStandstill”)
    Customer getNextCustomer();

So if OptaPlanner changes the previousStandstill side, it automatically also changes the nextCustomer side accordingly.

Comments are closed.