当我使用影子变量时,在分数计算过程中出现空指针异常。 我记得在optaplanner文档中,没有赋值的规划变量会被自动过滤掉。
我想要实现的是,当ComposeCourseClazz的规划变量发生变化时,CourseClazz的规划变量也随之变化。
我的代码
Planning entities:
@PlanningEntity
@EqualsAndHashCode(of = "id")
public class ComposeCourseClazz {
@PlanningId
private String id;
@PlanningVariable(valueRangeProviderRefs = {"slots"})
private Timeslot slot;
@ValueRangeProvider(id = "slots")
private List<Timeslot> slotList;
private List<CourseClazz> ccList;
...
}
@EqualsAndHashCode(of = "id")
@PlanningEntity
public class CourseClazz {
@PlanningId
private String id;
@ShadowVariable(variableListenerClass = SlotUpdatingVariableListener.class,
sourceEntityClass = ComposeCourseClazz.class, sourceVariableName = "slot")
private Timeslot slot;
...
}
Listener:
public class SlotUpdatingVariableListener implements VariableListener<CourseSchedule, ComposeCourseClazz> {
@Override
public void afterVariableChanged(ScoreDirector<CourseSchedule> scoreDirector, ComposeCourseClazz composeCourseClazz) {
updateSlotForCC(scoreDirector, composeCourseClazz);
}
@Override
public void afterEntityAdded(ScoreDirector<CourseSchedule> scoreDirector, ComposeCourseClazz composeCourseClazz) {
updateSlotForCC(scoreDirector, composeCourseClazz);
}
protected void updateSlotForCC(ScoreDirector<CourseSchedule> scoreDirector, ComposeCourseClazz composeCourseClazz) {
Timeslot slot = composeCourseClazz.getSlot();
for (CourseClazz cc : composeCourseClazz.getCcList()) {
scoreDirector.beforeVariableChanged(cc, "slot");
cc.setSlot(slot);
scoreDirector.afterVariableChanged(cc, "slot");
}
}
}
异常消息:
org.drools.modelcompiler.constraints.ConstraintEvaluationException: Error evaluating constraint '' in and in more rules
at org.drools.modelcompiler.constraints.LambdaConstraint.isAllowed(LambdaConstraint.java:184) ~[drools-model-compiler-8.44.0.Final.jar:8.44.0.Final]
at org.drools.core.phreak.PhreakAccumulateNode.propagateResult(PhreakAccumulateNode.java:665) ~[drools-core-8.44.0.Final.jar:8.44.0.Final]
at org.drools.core.phreak.PhreakGroupByNode.evaluateResultConstraints(PhreakGroupByNode.java:75) ~[drools-core-8.44.0.Final.jar:8.44.0.Final]
at org.drools.core.phreak.PhreakAccumulateNode.doNode(PhreakAccumulateNode.java:109) ~[drools-core-8.44.0.Final.jar:8.44.0.Final]
at org.drools.core.phreak.RuleNetworkEvaluator.switchOnDoBetaNode(RuleNetworkEvaluator.java:593) ~[drools-core-8.44.0.Final.jar:8.44.0.Final]
at org.drools.core.phreak.RuleNetworkEvaluator.evalBetaNode(RuleNetworkEvaluator.java:562) ~[drools-core-8.44.0.Final.jar:8.44.0.Final]
at org.drools.core.phreak.RuleNetworkEvaluator.evalNode(RuleNetworkEvaluator.java:389) ~[drools-core-8.44.0.Final.jar:8.44.0.Final]
at org.drools.core.phreak.RuleNetworkEvaluator.innerEval(RuleNetworkEvaluator.java:349) ~[drools-core-8.44.0.Final.jar:8.44.0.Final]
at org.drools.core.phreak.RuleNetworkEvaluator.evalStackEntry(RuleNetworkEvaluator.java:247) ~[drools-core-8.44.0.Final.jar:8.44.0.Final]
at org.drools.core.phreak.RuleNetworkEvaluator.outerEval(RuleNetworkEvaluator.java:190) ~[drools-core-8.44.0.Final.jar:8.44.0.Final]
at org.drools.core.phreak.RuleNetworkEvaluator.evaluateNetwork(RuleNetworkEvaluator.java:143) ~[drools-core-8.44.0.Final.jar:8.44.0.Final]
at org.drools.core.phreak.RuleExecutor.evaluateNetwork(RuleExecutor.java:221) ~[drools-core-8.44.0.Final.jar:8.44.0.Final]
at org.drools.core.phreak.RuleExecutor.evaluateNetworkIfDirty(RuleExecutor.java:231) ~[drools-core-8.44.0.Final.jar:8.44.0.Final]
at org.drools.core.phreak.RuleExecutor.evaluateNetworkAndFire(RuleExecutor.java:76) ~[drools-core-8.44.0.Final.jar:8.44.0.Final]
at org.drools.core.concurrent.AbstractGroupEvaluator.evaluateAndFire(AbstractGroupEvaluator.java:46) ~[drools-core-8.44.0.Final.jar:8.44.0.Final]
at org.drools.kiesession.agenda.DefaultAgenda.fireLoop(DefaultAgenda.java:624) ~[drools-kiesession-8.44.0.Final.jar:8.44.0.Final]
at org.drools.kiesession.agenda.DefaultAgenda.internalFireAllRules(DefaultAgenda.java:571) ~[drools-kiesession-8.44.0.Final.jar:8.44.0.Final]
at org.drools.kiesession.agenda.DefaultAgenda.fireAllRules(DefaultAgenda.java:563) ~[drools-kiesession-8.44.0.Final.jar:8.44.0.Final]
at org.drools.kiesession.session.StatefulKnowledgeSessionImpl.internalFireAllRules(StatefulKnowledgeSessionImpl.java:1088) ~[drools-kiesession-8.44.0.Final.jar:8.44.0.Final]
at org.drools.kiesession.session.StatefulKnowledgeSessionImpl.fireAllRules(StatefulKnowledgeSessionImpl.java:1079) ~[drools-kiesession-8.44.0.Final.jar:8.44.0.Final]
at org.drools.kiesession.session.StatefulKnowledgeSessionImpl.fireAllRules(StatefulKnowledgeSessionImpl.java:1071) ~[drools-kiesession-8.44.0.Final.jar:8.44.0.Final]
at org.optaplanner.constraint.streams.drools.DroolsConstraintStreamScoreDirector.calculateScore(DroolsConstraintStreamScoreDirector.java:70) ~[optaplanner-constraint-streams-drools-9.44.0.Final.jar:9.44.0.Final]
at org.optaplanner.core.impl.solver.recaller.BestSolutionRecaller.solvingStarted(BestSolutionRecaller.java:50) ~[optaplanner-core-impl-9.44.0.Final.jar:9.44.0.Final]
at org.optaplanner.core.impl.solver.AbstractSolver.solvingStarted(AbstractSolver.java:65) ~[optaplanner-core-impl-9.44.0.Final.jar:9.44.0.Final]
at org.optaplanner.core.impl.solver.DefaultSolver.solvingStarted(DefaultSolver.java:225) ~[optaplanner-core-impl-9.44.0.Final.jar:9.44.0.Final]
at org.optaplanner.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:192) ~[optaplanner-core-impl-9.44.0.Final.jar:9.44.0.Final]
at org.optaplanner.core.impl.solver.DefaultSolverJob.call(DefaultSolverJob.java:109) ~[optaplanner-core-impl-9.44.0.Final.jar:9.44.0.Final]
at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:317) ~[na:na]
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[na:na]
at java.base/java.lang.Thread.run(Thread.java:1589) ~[na:na]
在评分函数中,我过滤掉未分配的影子变量。并添加新的惩罚函数来惩罚未分配的影子变量。
虽然不是您问题的直接答案,但您可能想查看 Timefold Solver - OptaPlanner 的一个维护良好的分支。与 OptaPlanner 代码库相比,它包含近 15 个月的新功能和错误修复,而且您提到的例外情况在那里甚至是不可能的。
您甚至可能受益于级联更新影子变量,这是最近推出的一项功能,它极大地简化了尾链的更新,并帮助用户在实现变量侦听器时避免错误。