timefold-solver icon indicating copy to clipboard operation
timefold-solver copied to clipboard

VariableListener corruption: Impossible VariableListener corruption(-1hard)…but all shadow variables is the same …

Open maker2020 opened this issue 2 years ago • 4 comments

Describe the bug I think I set the variable listener trigger order correctly, and defined a seemly correct constraint.but it told me I have a variable listener corruption.

To Reproduce

This is my Assign class
@PlanningEntity
public class Assign extends AbstractPersistable{

    @PlanningVariable(valueRangeProviderRefs = "customerList",nullable = true)
    protected Customer customer;

    @PlanningVariable(valueRangeProviderRefs = "serverStationList",nullable = true)
    protected ServerStation station;

    @PlanningVariable(valueRangeProviderRefs = "demandChoices",nullable = true)
    protected Long assignedDemand;

    ...
}
----------------------------
This is my Customer class
@PlanningEntity
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Customer extends AbstractPersistable{
    
    protected long maxDemand;
    protected Location location;

    /**
     * 需求等级,对应服务站可服务等级。没确定上限,按越高越好
     */
    protected int demandLevel;

    @InverseRelationShadowVariable(sourceVariableName = "customer")
    @Schema(hidden = true)
    protected List<Assign> assignedStations=new ArrayList<>();

    @ShadowVariable(sourceEntityClass = Customer.class,sourceVariableName = "assignedStations",variableListenerClass = RemainingDemandListener.class)
    // @Schema(hidden = true)
    protected Long remainingDemand;

    …
}

There is also a ServerStation class (Omitted)

Now this is key parts in my RemainingDemandListener.class
@Override
    public void afterVariableChanged(ScoreDirector<FacilityLocationSolution> scoreDirector, Customer customer) {
        updateRemainingDemand(scoreDirector, customer);
    }

    private void updateRemainingDemand(ScoreDirector<FacilityLocationSolution> scoreDirector, Customer customer){
        // if(customer==null){
        //     return;
        // }
        
        long remainingDemand=customer.getMaxDemand();
        for (Assign assign : customer.getAssignedStations()) {
            if(assign.getStation()!=null && assign.getAssignedDemand()!=null){
                remainingDemand-=assign.getAssignedDemand();
            }
        }
        remainingDemand=remainingDemand<0?0:remainingDemand;
        scoreDirector.beforeVariableChanged(customer, "remainingDemand");
        customer.setRemainingDemand(remainingDemand);
        scoreDirector.afterVariableChanged(customer, "remainingDemand");
        
    }

Finally, my constraints about this variable:
Constraint noRestDemand(ConstraintFactory constraintFactory) {
        return constraintFactory.forEach(Customer.class)
                .filter(c->c.getRemainingDemand()!=0)
                .penalizeConfigurable()
                .asConstraint(FacilityLocationConstraintConfig.NO_REST_DEMAND);
    }

Environment

timefold 1.3:

Output of java -version:17

Output of uname -a or ver:

Additional information

Provide any and all other information which might be relevant to the issue.

maker2020 avatar Oct 27 '23 03:10 maker2020

Thanks for reporting, @maker2020! Please provide runnable code for us to reproduce the problem, otherwise there isn't much we could do.

triceo avatar Oct 27 '23 07:10 triceo

problem.zip this is my code of this module, you mean a runnable code, should I upload all maven project? @triceo

maker2020 avatar Oct 27 '23 08:10 maker2020

Yes, a Maven project would be ideal.

triceo avatar Oct 27 '23 08:10 triceo

timefold-demo.zip ok, this is a maven project, you can run Main.java,then the problem reproduce. thanks for your patience @triceo

maker2020 avatar Oct 27 '23 10:10 maker2020