【问题标题】:OptaPlanner's mythical score corruptionOptaPlanner 的神话分数损坏
【发布时间】:2015-04-16 10:35:35
【问题描述】:

我为我的模型使用增量分数计算器。 在“完全断言”模式下优化几个小时/天后抛出分数损坏异常:

java.lang.IllegalStateException: Score corruption: the workingScore (-86591/-2765/-422/-591) is not the uncorruptedScore (-86591/-2873/-422/-591) after completedAction [...]:
Uncorrupted: Score calculator 3718238 [schedule=Schedule6422-2015-04-16T09:47:36.932[-86591/-2873/-422/-591], prioritiesScore=-422, timelineGapsScore=-2873, requirementGapsScore=-86591, timelineVersionsScore=-591]
Corrupted: Score calculator 3717237 [schedule=Schedule6422-2015-04-16T09:47:36.932[-86591/-2873/-422/-591], prioritiesScore=-422, timelineGapsScore=-2873, requirementGapsScore=-86591, timelineVersionsScore=-591]

参数timelineGapsScore 的分数不同。分数实例是从分数计算器对象字段prioritiesScoretimelineGapsScorerequirementGapsScoretimelineVersionsScore 创建的。根据日志,这两个分数的实例在这些字段中是相等的,但 optaplanner 引擎发现差异 (-86591/-2765/-422/-591) 与 (-86591/-2873/-422/-591)。怎么可能?

我怀疑解决方案克隆的引用泄漏(它是特定的实现并进行深度复制),但仔细的代码检查不会显示此类错误。

UPD:我忘了说:optaplanner 在守护进程模式下运行,模型能够实时改变事实。所以我怀疑模型中的竞争条件。但我不知道变化注入是如何在 optaplanner 的引擎下实现的(信息不足in docs)。

【问题讨论】:

  • 只要您正确使用addProblemFactChange() 在求解器中修改模型(因为该回调在求解器线程中执行),就不可能出现竞争条件。
  • 请注意addProblemFactChange() 确实很棘手。它通常需要进行更深层次的克隆,而不仅仅是计划克隆。例如,要在 CB 示例中添加新计算机,需要克隆 computerList(这在计划克隆中不会发生)。

标签: java optimization optaplanner


【解决方案1】:

“当解决方案更改时,增量分数计算(AKA 基于增量的分数计算)将计算与先前状态的增量以找到新分数,而不是在每次解决方案评估时重新计算整个分数。

例如,如果单个皇后 A 从第 1 行移动到第 2 行,则无需检查皇后 B 和 C 是否可以互相攻击,因为它们都没有改变。”

当增量分数和实际分数(= 未损坏的分数)不同步时会发生分数损坏。

可能有多种原因。如果您使用 Drools 分数计算,它甚至可能是 Drools 中的错误。如果您可以将其隔离并使用复制器归档 jira,那么我们通常会快速查看它。

隔离意味着(按此顺序!):

  1. 删除所有不需要重现的分数规则
  2. 删除不需要重现它的所有规划实体
  3. 删除不需要重现它的所有步骤。请参阅 Termination.stepCountLimit 以在解决方案出错之前保存解决方案,然后从该解决方案开始解决
  4. (可选)删除所有不需要的动作来重现它。打开跟踪日志。如果不破解 optaplanner 就很难做到这一点,所以我们通常会执行此步骤。

【讨论】:

  • 我使用纯 Java 分数计算器而不是 Drools,不幸的是,这段代码是高耦合的。根据日志,optaplanner 将原始解决方案 S0 克隆到 S1,然后在 S0 上执行几个步骤后逻辑导致初始解决方案(例如交换移动 A B、B A、A C、C A)它检查分数等价分数(S0)=分数(S1)。当然,这样的 eq 在我的情况下是成立的,但是尽管我使用简单的代码来创建分数,但分数实例是不同的:return BendableScore.valueOf(new int[]{requirementGapsScore, timelineGapsScore}, new int[]{prioritiesScore, timelineVersionsScore});
  • 使用EasyJavaScoreCalculator,分数损坏是不可能的。我想你正在使用IncrementalJavaScoreCalculator?很高兴听到这不是 Drools 中的错误 :)
  • 在 NQueens 示例实现中,在 Sandwished 情况下,B 和 C 被认为相互攻击。但即使有 Sandswish-does-not-attack 要求,您也可以通过跟踪 Sandwish 状态来进行增量计算以更好地扩展。
  • @GeoffreyDeSmet 我添加了问题的详细信息。顺便说一句,关于文档:this diagram 误导:calculateScore() 不会在每次交换移动之后调用。也许对实时模型变化的描述也有类似的不准确之处? :)
  • OptaPlanner 允许跳过 calculateScore() 如果它不需要它(或者它可以预测它),以提高性能,所以我可以在调用 @ 之前一个接一个地做几个动作987654327@。如果使用 CompositeMove(例如,如果您的实体上有 2 个变量),这一点会更进一步。
【解决方案2】:

这是我的实现错误。在增量分数计算器实现时要准确(或尝试使用 Drools)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-24
    • 2011-02-13
    • 2016-12-22
    • 1970-01-01
    • 2010-11-21
    相关资源
    最近更新 更多