带有分数的 VRP 依赖于 optaplanner 中的多个作业

VRP with score dependens on more than one job in optaplanner

我们正在尝试使用 Optaplanner 求解 VRP。 分数计算通过约束流运行。

现在我有两辆车(A 和 B),想安排两个工作(J1 和 J2)。 构造启发式 (FIRST_FIT_DECREASING) 将 J1 调度到 A 并将 J2 调度到 B,到目前为止正确的是什么。

现在这两个工作也有一个属性“客户”,如果两个工作的客户相同但车辆不同,我想分配一个惩罚。

为此,我在 ConstraintProvider 中创建了一个约束条件,它通过 groupBy 过滤所有具有相同客户但不同车辆的作业。

如果我现在打开FULL_ASSERT_MODE,调度J2后会出现IllegalStateException,因为增量计算的分数与完整计算的分数不同。 我怀疑这是因为重新计算作业时间的 VariableListener 仅告知 ScoreDirector 关于我的影子变量的作业 J2 的更改,因此仅更改与其相关的分数部分。

如何告诉 Optaplanner J1 的分数也必须重新计算?我无法通过 VariableListener 到达作业 J1 以告诉 ScoreDirector 必须在此处更改分数。

或者这个问题需要不同的方法吗?

这是一个有点难以完全解释的问题。 TLDR 版本:约束流仅对来自 from()join()ifExists() 的对象更改做出反应。未通过这些语句进行的对象更改将不会被捕获,因此会导致分数损坏。下面是更长的解释。

考虑这样一个假设的约束流:

constraintFactory.from(Shift.class)
    .join(Shift.class)
    .filter((shift1, shift2) -> shift1.getEmployee() == shift2.getEmployee())
    ...

此约束流将正常工作,因为如果您通过设置不同的员工来更改 Shift,将重新评估 Shift。他们通过 from()join() 进入流,这就是 CS 知道在他们改变时重新评估 Shifts 的方式。

现在考虑这个约束流:

constraintFactory.from(Shift.class)
    .filter(shift -> shift.getEmployee().getName() == "Lukas")
    ...

如果 Shift 发生变化,将重新评估此约束流。但是当Employeename改变时,约束流不会重新求值; Employee 既不在 from() 中也不在 join() 中,对 Employee 的更改不会触发约束流的重新评估。

在您的特定情况下,您需要确保几件事:

  • 可变侦听器将实际更改的所有内容标记为已更改。
  • 如果您修改问题事实,则需要确保您的变量侦听器也处理该问题。
  • 您希望约束流做出反应的对象正在通过 from()join() 进入。