You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OptaPlanner中时序链式任务的动态依赖关系建模问询

摘要

如何建模一个任务时长依赖前置任务结束时间的时序链式流程?详情请阅读以下描述:

规划问题描述

首先说明我的规划问题:

约束条件

  • 一台机器拥有x个槽位,每个槽位可生产或冷却不同类型产品。
  • 机器不能同时进行生产和冷却,但可先生产后冷却。
  • 机器同一时间只能生产一种产品,但可依次生产不同类型产品。
  • 机器可同时生产多个同类型产品。
  • 机器可同时冷却不同类型产品。
  • 产品需在目标时间被消耗。
  • 生产出的产品必须冷却至目标时间。

规划目标

  • 最小化生产时间
  • 确定生产必须启动的最晚时间

有效流程示例

下图展示了两个有效的生产流程。由于规划实体从目标时间开始反向按时间链式排列,机器位于流程的右端。

领域模型

由于生产任务的最短时长不足一分钟,我采用时序链式模式建模该问题,需以秒为单位规划,这对于时间粒度模式来说可能过于精细。
以下是我的领域模型片段:抽象类JobOrSlot用于建模以槽位为锚点的任务链。

@PlanningEntity
public abstract class JobOrSlot {

    @Id
    private UUID id = UUID.randomUUID();

    @InverseRelationShadowVariable(sourceVariableName = "previousJobOrSlot")
    
    protected Job nextJob;

    public abstract Integer getStartOffset();
}
@PlanningEntity
public class Job extends JobOrSlot {
    @PlanningVariable(valueRangeProviderRefs = { "slotRange", "jobRange" }, graphType = PlanningVariableGraphType.CHAINED)
    private JobOrSlot previousJobOrSlot;

    @AnchorShadowVariable(sourceVariableName = "previousJobOrSlot")
    private Slot slot;

    @ShadowVariable(variableListenerClass = EndTimeUpdatingVariableListener.class, sourceVariableName = "previousJobOrSlot")
    private Integer startOffset;

    @PiggybackShadowVariable(shadowVariableName = "startOffset")
    private Integer endOffset;

    // problem facts
    @Nullable
    private Job predecessor;

    @Nullable
    private Job successor;

    private Duration duration;
    private JobType type; // produce, hold

    // ... 省略无关的问题事实、getter和setter

}

EndTimeUpdatingVariableListener.class会遍历任务链,根据任务时长和其在链中的位置计算任务的开始和结束时间。

问题难点

挑战在于冷却任务的时长在规划开始前是未知的,只有当生产任务被安排后才能计算,即targetTime - cookingJob.endTime。因此安排生产任务必须改变冷却任务的时长,我无法找到在OptaPlanner中建模此场景的理想方案。我评估了两种可能的解决方案,但或许有更好的实现思路:

  1. 一种方案是在EndTimeUpdatingVariableListener中,每当生产任务发生变化时更新关联冷却任务的开始和结束时间,但这可能引发难以处理的问题。例如,在生产任务和冷却任务之间插入其他任务(如produce A -> someOtherJob -> cool A -> Slot)会导致无限更新循环。可通过实现MoveFilter禁止此类无效移动,但仍较为繁琐。
  2. 另一种方案是实现CustomMoves,在规划问题中添加或移除冷却任务。每次添加冷却任务时,为其设置当前有效的时长,这样实体的时长在规划过程中保持固定。

这两种实现都相当复杂。是否有更好的方法在OptaPlanner中建模此类规划实体依赖关系?


优化建模方案建议

1. 绑定生产-冷却任务为逻辑单元

将生产任务和对应的冷却任务视为一个不可拆分的ProductionCoolingPair规划实体,而非两个独立任务。这样可以确保两者始终连续排列,避免中间插入其他任务的问题:

  • 新增ProductionCoolingPair类,包含生产任务、冷却任务的属性,将其作为链式规划的基本单元。
  • 冷却任务的时长可在生产任务结束时间确定后,直接通过targetTime - productionEndTime计算并存储在ProductionCoolingPair中,无需单独更新。

2. 调整ShadowVariable计算逻辑

若坚持分开建模生产和冷却任务,可修改EndTimeUpdatingVariableListener的逻辑:

  • 为冷却任务添加linkedProductionJob属性,关联对应的生产任务,确保仅当关联生产任务变化时才更新冷却任务时长。
  • 变量更新时,先判断当前任务是否为冷却任务,若是则通过关联生产任务的结束时间计算自身时长,再推导开始/结束偏移量。同时添加硬约束,禁止生产与冷却任务之间插入其他任务,比MoveFilter更可靠。

3. 使用动态问题事实特性

将冷却任务的时长定义为动态问题事实,而非规划实体的固定属性:

  • 通过ProblemFactChange机制,在生产任务安排发生变化时,触发冷却任务时长的更新。该机制会在规划稳定阶段执行,可避免无限循环问题。

方案对比

  • 绑定任务对的方案实现最简单,逻辑清晰,适合生产与冷却必须连续的场景,能有效避免无效移动和循环更新。
  • 调整ShadowVariable逻辑的方案保留了任务独立性,需额外约束校验,适合后续可能扩展任务类型的场景。
  • 动态问题事实的方案灵活性最高,但实现复杂度略高,适合任务依赖关系更复杂的场景。

内容的提问来源于stack exchange,提问作者Jonas Erbe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 04:45:46