TimefoldAI / TimefoldAI/timefold-solver
VariableListener corruption: Impossible VariableListener corruption(-1hard)…but all shadow variables is the same …
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 1.8k
- Forks
- 228
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 46
Description
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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the corruption with the provided Assign and Customer planning entities and the RemainingDemandListener implementation. Inspect the shadow-variable declaration, listener trigger order, and the before/after notifications around remainingDemand, then compare the reproduced failure with the reported corruption message. Done means the supplied model no longer reports variable-listener corruption, or the issue has a documented minimal cause and reproduction.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- ai
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100