为具有可为空计划变量的实体切换到 BAVET 时出现意外行为

问题描述 投票:0回答:1

OptaPlanner 8.36.0.Final

使用 ConstraintStreamImplType.DROOLS 时,以下约束的行为符合预期:

return constraintFactory
    .forEach(Ewe.class)
    .groupBy(ConstraintCollectors.count())
    .penalize(level, 
    (count) -> {
        return activeRange.check(count));
    }
    )
    .asConstraint(name);

Ewe 是一个 PlanningEntity,它有一个可为空的 PlanningVariable Ram。有 100 只母羊,其中 50 到 75 只应该分配给公羊。此约束会惩罚分配不足或分配过多。约束产生预期的结果。

当我更改为 ConstraintStreamImplType.BAVET 时,求解器结束时没有分配任何母羊并且分数为零,因此选择的解决方案是一个没有分配任何母羊并且未评估约束的解决方案,因此分数为零。

BAVET 似乎没有评估此约束,因为匹配的实体为零。另一方面,当匹配项为零时,DROOLS 会评估约束条件。

DROOLS和BAVET之间的这种差异是预期的吗?

我通过仅在 ConstraintStreamImplType.BAVET 和 ConstraintStreamImplType.DROOLS 之间切换而没有其他更改来验证此行为更改

编辑: 最终我想计算条目甚至包括零计数作为可能的答案。

nullable optaplanner
1个回答
0
投票

根据您在其中一条评论中提交的代码,这是我的分析。

BAVET
行为正确,原因如下:

  • 如果
    groupBy
    没有任何输入,它不会评估任何东西。
    count()
    什么都不是
    0
    ,它根本不会发生。因此,所有未初始化实体的最终得分为
    0
  • 一旦你初始化了一个entitiy,计数突然是
    1
    ,使总分成为
    -4
    。这个比
    0
    差,所以没摘

在某种程度上,这可以被认为是一个得分陷阱。在过度约束问题(可空变量的问题)中,我们通常通过引入另一个约束来解决它,该约束会惩罚未分配实体的数量。这使求解器有动力实际分配实体。 (反过来,这会触发

groupBy(...)
。)

现在的问题变成了——为什么

DROOLS
的行为不同?这几乎肯定看起来像一个错误。
groupBy()
违反了自己的合同。但是,出于向后兼容性的原因,我认为我们不会修复它;这种行为现在太暴露了,我们无法改变它,即使它是不正确的。

© www.soinside.com 2019 - 2024. All rights reserved.