提问人:greyhairredbear 提问时间:11/15/2023 最后编辑:Geoffrey De Smetgreyhairredbear 更新时间:11/17/2023 访问量:41
为什么对链接计划实体的约束会导致以下分数损坏异常?
Why does a constraint on linked planning entities cause the following score corruption exception?
问:
我目前正在解决带有取货和送货的 VRP。在当前模型中,a 是我的第一个模型,包含 的 ,它可以是 类型 ,也可以是 。每个类都引用了它正在交付的内容,并且该类同时引用了它的 和 。Vehicle
@PlanningEntity
@PlanningListVariable
LoadJobs
PICKUP
DROPOFF
LoadJob
Load
Load
PICKUP
DROPOFF
LoadJob
我编写了以下约束,以确保两者放在相同的位置(当然,尝试交付从未被拾取的负载是没有意义的,并且在不交付的情况下拾取负载也是没有意义的):LoadJobs
Vehicle
fun pickupAndDropoffOnSameVehicle(constraintFactory: ConstraintFactory): Constraint {
return constraintFactory
.forEach(LoadJob::class.java)
.filter { it.load.pickup.vehicle != it.load.dropoff.vehicle }
.penalizeConfigurable()
.asConstraint(PICKUP_AND_DROPOFF_ON_SAME_VEHICLE)
}
当我在模式下运行时,我收到以下异常:FULL_ASSERT
Caused by: java.lang.IllegalStateException: Score corruption (100hard): the workingScore (-19init/-100hard/0medium/-11670soft) is not the uncorruptedScore (-19init/-200hard/0medium/-11670soft) after completedAction (LoadJob(id=DROPOFF-loDKrYTAqF5kIfjFM6n4) {null -> Vehicle(idx=0)[0]}):
Score corruption analysis:
The corrupted scoreDirector has no ConstraintMatch(s) which are in excess.
The corrupted scoreDirector has 1 ConstraintMatch(s) which are missing:
com.cargonexx.vehiclerouting.solver.constraint/pickupAndDropoffOnSameVehicle/[LoadJob(id=PICKUP-loDKrYTAqF5kIfjFM6n4)]=-100hard/0medium/0soft
Maybe there is a bug in the score constraints of those ConstraintMatch(s).
Maybe a score constraint doesn't select all the entities it depends on, but finds some through a reference in a selected entity. This corrupts incremental score calculation, because the constraint is not re-evaluated if such a non-selected entity changes.
Shadow variable corruption in the corrupted scoreDirector:
None
at org.optaplanner.core.impl.score.director.AbstractScoreDirector.assertScoreFromScratch(AbstractScoreDirector.java:637)
at org.optaplanner.core.impl.score.director.AbstractScoreDirector.assertWorkingScoreFromScratch(AbstractScoreDirector.java:613)
at org.optaplanner.core.impl.score.director.AbstractScoreDirector.doAndProcessMove(AbstractScoreDirector.java:204)
at org.optaplanner.core.impl.heuristic.thread.MoveThreadRunner.run(MoveThreadRunner.java:131)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:577)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1589)
由于错误消息,我的猜测可能是由类中两者之间的传递关系引起的。在过滤同一辆车以及加入之前,我只尝试过过滤 s,然后在两个加载作业中过滤相同的车辆,然后才过滤同一辆车,但都无济于事。LoadJobs
Load
PICKUP
LoadJob::class.java
Load
此异常的原因是什么,如何解决?
答:
1赞
Geoffrey De Smet
11/15/2023
#1
这是因为增量分数计算如何与
.filter { it.load.pickup.vehicle != it.load.dropoff.vehicle }
两者都没有选择,因此 ConstraintStreams 实现(无论是 OptaPlanner CS Drools 实现,还是 Timefold 的更快实现)都不知道当取货或下车的车辆变量发生变化时,需要重新评估此约束(以增量方式实现可扩展性)。pickup
dropoff
溶液
像这样:
fun pickupAndDropoffOnSameVehicle(constraintFactory: ConstraintFactory): Constraint {
val pickupStream =
constraintFactory.forEach(LoadJob::class.java).filter { it.type == LoadJobType.PICKUP }
val dropoffStream =
constraintFactory.forEach(LoadJob::class.java).filter { it.type == LoadJobType.DROPOFF }
return pickupStream
.join(dropoffStream, Joiners.equal { it.load.id })
.filter { pickup, dropoff -> pickup.vehicle != dropoff.vehicle }
.penalizeConfigurable()
.asConstraint(PICKUP_AND_DROPOFF_ON_SAME_VEHICLE)
}
评论
0赞
greyhairredbear
11/15/2023
非常感谢您的回答,这对我帮助很大!但是,我并不真正理解潜在的问题(“既不选择也不被选中”)。从我的预期来看,对于在同一辆车上没有上车和下车的每个负载,应该违反两次约束(一次是 loadJob,一次是 )。要么 or 总是等于 .您能否详细说明为什么没有选择规划实体?还是相关计划实体并不总是与选取的实体一起选择?pickup
dropoff
PICKUP
DROPOFF
it.load.pickup
it.load.dropoff
it
1赞
Geoffrey De Smet
11/17/2023
对于增量分数计算,约束流仅计算每次移动的增量。这使得 Timefold 每秒可以计算 10k-500k 的分数,而不是扩展到大型数据集时的几百个分数。但要做到这一点,它需要知道哪些域类实例发生了变化(它确实更改了),以及哪些约束受哪些域类实例的影响。后者是通过 等完成的。forEach(class)
join(class)
1赞
Geoffrey De Smet
11/17/2023
因此,当 没有更改时,约束使用和更改,但拾取从未 forEach()ed 或 join()ed,它没有“看到”更改,delta 是错误的,分数最终损坏。load
load.pickup.vehicle
pickup
0赞
greyhairredbear
11/17/2023
谢谢,我认为这为我清除了它 +1
0赞
Geoffrey De Smet
11/18/2023
不客气:)
评论