从 OptaPlanner 7.46.0 切换到 8.0.0 时约束流中断

Contstraint stream breaks when switching from OptaPlanner 7.46.0 to 8.0.0

我有一个约束在最新的 OptaPlanner 8.0.0 中崩溃,但过去在 7.46.0 上工作正常。
正如预期的那样,IntelliJ 的代码检查(和调试器)显示在第一次加入后,流是 TriConstraintStream。运行时 class 对我来说比 class OptaPlanner 试图投射到的更有意义。
当省略最后一个 groupBy 时,错误消失,因此该子句似乎导致了问题。

join 和 groupby 的工作方式有什么变化吗?
看来 OptaPlanner 的底层代码已针对 8.0.0 进行了重构,所以我很难看出 OptaPlanner 到底发生了什么变化。
我应该添加一些东西来确保使用 TriJoin 而不是 BiJoin 吗? 我在迁移文档中找不到任何相关注释。

protected Constraint preventProductionShortage(ConstraintFactory factory) {
    return factory.from(Demand.class)
            .groupBy(Demand::getSKU,
                    Demand::getWeekNumber
            )//BiConstraintStream
            .join(Demand.class,
                    equal((sku, weekNumber)-> sku, Demand::getSKU),
                    greaterThanOrEqual((sku, weekNumber)-> weekNumber, Demand::getWeekNumber)//TriConstraintStream
            )
            .groupBy((sku, weekNumber, totalDemand) -> sku,
                    (sku, weekNumber, totalDemand) -> weekNumber,
                    sum((sku, weekNumber, totalDemand) -> totalDemand.getOrderQuantity())
            )//TriConstraintStream
            .penalize("Penalty", HardMediumSoftScore.ONE_MEDIUM,
                    (sku_weekNumber, demandQty, productionQty) -> 1);
}

堆栈跟踪:

java.lang.ClassCastException: class org.optaplanner.core.impl.score.stream.tri.CompositeTriJoiner cannot be cast to class org.optaplanner.core.impl.score.stream.bi.AbstractBiJoiner (org.optaplanner.core.impl.score.stream.tri.CompositeTriJoiner and org.optaplanner.core.impl.score.stream.bi.AbstractBiJoiner are in unnamed module of loader 'app')

    at org.optaplanner.core.impl.score.stream.drools.common.rules.BiJoinMutator.<init>(BiJoinMutator.java:40)
    at org.optaplanner.core.impl.score.stream.drools.common.rules.UniRuleAssembler.join(UniRuleAssembler.java:70)
    at org.optaplanner.core.impl.score.stream.drools.common.rules.AbstractRuleAssembler.join(AbstractRuleAssembler.java:179)
    at org.optaplanner.core.impl.score.stream.drools.common.ConstraintSubTree.getRuleAssembler(ConstraintSubTree.java:94)
    at org.optaplanner.core.impl.score.stream.drools.common.ConstraintSubTree.getRuleAssembler(ConstraintSubTree.java:89)
    at org.optaplanner.core.impl.score.stream.drools.common.ConstraintGraph.generateRule(ConstraintGraph.java:431)
    at org.optaplanner.core.impl.score.stream.drools.common.ConstraintGraph.lambda$generateRule(ConstraintGraph.java:423)
    at java.base/java.util.stream.ReferencePipeline.accept(ReferencePipeline.java:195)
    at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
    at org.optaplanner.core.impl.score.stream.drools.common.ConstraintGraph.generateRule(ConstraintGraph.java:424)
    at org.optaplanner.core.impl.score.stream.drools.DroolsConstraintFactory.buildSessionFactory(DroolsConstraintFactory.java:101)
    at org.optaplanner.core.impl.score.director.stream.ConstraintStreamScoreDirectorFactory.<init>(ConstraintStreamScoreDirectorFactory.java:77)
    at org.optaplanner.test.impl.score.stream.DefaultConstraintVerifier.verifyThat(DefaultConstraintVerifier.java:63)
    at org.optaplanner.test.impl.score.stream.DefaultConstraintVerifier.verifyThat(DefaultConstraintVerifier.java:32)
    at com.ohly.planner.constraints.ConstraintsTest.weekShortageSingleSKU(ConstraintsTest.java:61)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access[=11=]0(ParentRunner.java:53)
    at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)


Process finished with exit code -1

[edit] 为了完整起见,

建议的新函数
    protected Constraint preventProductionShortage(ConstraintFactory factory) {
        return factory.from(Demand.class)
                .join(Demand.class,
                        equal(Demand::getSKU),
                        greaterThanOrEqual(demand -> demand.getWeekNumber()))
                .groupBy((d, d2) -> d.getSKU(),
                        (d, d2) -> d.getWeekNumber(),
                        sum((d,d2) -> d2.getOrderQuantity())
                )
                ...

[/edit]

这是一个 unfortunate bug 未被现有测试覆盖率捕获的问题。 The fix 针对 OptaPlanner 8.0.1,包括。测试覆盖率改进。

也就是说,我认为约束不是很有效。除非我遗漏了一些关键含义,否则以下内容在语义上非常相似,但速度更快:

protected Constraint preventProductionShortage(ConstraintFactory factory) {
    return factory.from(Demand.class)
        .join(Demand.class,
                equal(Demand::getSKU),
                greaterThanOrEqual(demand -> demand.getWeekNumber()))
        .groupBy(..., ..., sum((demand, demand2) -> ...))
        .penalize("Penalty", HardMediumSoftScore.ONE_MEDIUM);

}

请注意我是如何消除第一次使用 groupBy() 的。尽管以这种方式惩罚了多少元组,但可能存在一些差异,这可能是您想要的,也可能不是您想要的。随时打开另一个问题。