19.2. Planning Values and Value Ranges

19.2.1. ValueRangeProvider

If you have a @ValueRangeProvider that returns a collection of numbers (for example List<Integer> or List<BigDecimal>), then you should switch to a ValueRange, which uses less memory and offers additional opportunities.

For example:

@ValueRangeProvider(id = "delayRange")
public List<Integer> getDelayRange() {
    List<Integer> = new ArrayList<Integer>(5000);
    for (int i = 0; i < 5000; i++) {
        delayRange.add(i);
    }
    return delayRange;
}

Is changed to:

@ValueRangeProvider(id = "delayRange")
public CountableValueRange<Integer> getDelayRange() {
    return ValueRangeFactory.createIntValueRange(0, 5000);
}
Note

The annotation @ValueRangeProvider has been moved into another package, from:

import org.optaplanner.core.api.domain.value.ValueRangeProvider;

to

import org.optaplanner.core.api.domain.valuerange.ValueRangeProvider;

19.2.2. Planning Variables

The interface PlanningVariableListener has been renamed to VariableListener.

Previously in *.java :

public class VehicleUpdatingVariableListener implements PlanningVariableListener<Customer> {

Now in *.java :

public class VehicleUpdatingVariableListener implements VariableListener<Customer> {

The class AbstractPlanningVariableListener has been removed.

Previously in *.java :

public class VehicleUpdatingVariableListener extends AbstractPlanningVariableListener<Customer> {

Now in *.java :

public class VehicleUpdatingVariableListener implements VariableListener<Customer> {

The VariableListener is now declared on the shadow side, instead of the @PlanningVariable side. This way, Business Rules Planner recognizes the shadow variables, and all shadow variables are declared in a consistent matter. Furthermore, it allows a shadow variable to based on other shadow variable.

Previously in *.java :

@PlanningVariable(valueRangeProviderRefs = {"vehicleRange", "customerRange"},
        graphType = PlanningVariableGraphType.CHAINED,
        variableListenerClasses = {VehicleUpdatingVariableListener.class, ArrivalTimeUpdatingVariableListener.class})
public Standstill getPreviousStandstill() {
    return previousStandstill;
}

public Vehicle getVehicle() {
    return vehicle;
}

public Integer getArrivalTime() {
    return arrivalTime;
}

Now in *.java :

@PlanningVariable(...)
public Standstill getPreviousStandstill() {
    return previousStandstill;
}

@CustomShadowVariable(variableListenerClass = VehicleUpdatingVariableListener.class,
        sources = {@CustomShadowVariable.Source(variableName = "previousStandstill")})
public Vehicle getVehicle() {
    return vehicle;
}

@CustomShadowVariable(variableListenerClass = ArrivalTimeUpdatingVariableListener.class,
        sources = {@CustomShadowVariable.Source(variableName = "previousStandstill")})
public Integer getArrivalTime() {
    return arrivalTime;
}

There is now out-of-the-box support for a shadow variable representing the anchor of a chained variable. For example, in a VRP each Customer (= entity) needs to know to which Vehicle (= anchor) it belongs. This declarative support allows build-in selectors to reuse that knowledge without duplicating the calculation.

Previously in *.java :

@PlanningEntity
public class Customer implements Standstill {

    @PlanningVariable(...)
    public Standstill getPreviousStandstill() {...}

    @CustomShadowVariable(variableListenerClass = VehicleUpdatingVariableListener.class,
            sources = {@CustomShadowVariable.Source(variableName = "previousStandstill")})
    public Vehicle getVehicle() {...}

}
public class VehicleUpdatingVariableListener implements VariableListener<Customer> {
    ...
}

Now in *.java :

@PlanningEntity
public class Customer implements Standstill {

    @PlanningVariable(...)
    public Standstill getPreviousStandstill() {...}

    @AnchorShadowVariable(sourceVariableName = "previousStandstill")
    public Vehicle getVehicle() {...}
}

To scale VRP cases, Nearby Selection is critical. It is now finally completely supported and documented.