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

Timefold优化中硬约束未生效的问题排查求助

Timefold设备分配项目硬约束失效问题排查

项目背景

基于Timefold开发设备分配优化项目,需结合技术员技能、产能约束完成分配。当前技术员工时容量硬约束未生效:部分技术员超出可用工时(rbh_to_plan)却未被惩罚,但软约束(如技术员分配奖励)运行正常。

关键代码片段

1. 领域模型定义(domain.py)

@dataclass
class Technician:
    id: int
    name: str
    rbh_to_plan: float
    rbh_assigned: float
    iums: Set[str]
    # Technician class with available hours and skills

@planning_entity
@dataclass
class Device:
    index: int
    ium: str
    technician: Technician | None = field(default=None)
    # Devices assigned to technicians

2. 约束定义(constraints.py)

def technician_capacity_hard(constraint_factory: ConstraintFactory):
    return (constraint_factory
            .for_each(Device)
            .filter(lambda device: device.technician is not None)
            .group_by(lambda device: device.technician, ConstraintCollectors.sum(lambda device: device.rbh_norm))
            .filter(lambda technician, total_rbh_norm: total_rbh_norm > technician.rbh_to_plan)
            .penalize(HardSoftScore.ONE_HARD)
            .as_constraint("Technician capacity (hard constraint)"))

3. 求解器配置(main.py)

solver_factory = SolverFactory.create(
    SolverConfig(
        solution_class=DeviceSchedule,
        entity_class_list=[Device],
        score_director_factory_config=ScoreDirectorFactoryConfig(
            constraint_provider_function=define_constraints  # Enabling constraints
        ),
        termination_config=TerminationConfig(
            best_score_limit="0hard/1000soft",
            spent_limit=Duration(minutes=1)
        )
    )
)

已完成调试步骤

  • 打印技术员总分配工时(rbh_norm),确认多数情况超出rbh_to_plan;
  • 使用HardSoftScore.ONE_HARD惩罚违规,但未触发;
  • 软约束运行正常。

问题解答

1. 硬约束未生效的核心原因

  • 约束未注册:检查define_constraints函数是否包含technician_capacity_hard的返回结果,如果仅返回软约束列表,硬约束根本未被加载进求解器;
  • rbh_norm字段缺失:Device类定义中无rbh_norm字段,直接调用会导致取值异常(如默认0或None),分组求和结果无法触发total_rbh_norm > technician.rbh_to_plan的过滤条件;
  • 终止条件冲突:best_score_limit="0hard/1000soft"要求求解器找到无硬违规的解后立即停止,若1分钟内未找到可行解,会保留违规解,看起来像约束未生效;
  • rbh_to_plan取值异常:若该字段被误设为极大值(如无穷大),total_rbh_norm永远无法超过它,不会触发惩罚。

2. 分组求和的潜在问题

  • 字段缺失:Device类无rbh_norm是核心问题,求和字段不存在会导致结果异常,无法触发过滤;
  • 分组键正确性:确保Technician类的equals()和hashCode()(Python dataclass默认生成,但可变字段可能影响分组)逻辑正确,避免同一技术员被拆分为多个分组导致求和结果偏小;
  • 过滤逻辑:当前filter(lambda device: device.technician is not None)已正确过滤未分配设备,这部分无问题。

3. Timefold约束调试最佳实践

  • 开启约束日志:设置logging.level.org.optaplanner=DEBUG,查看每个约束的匹配次数和得分贡献,确认硬约束是否被触发;
  • 使用ScoreDirector排查:求解过程或结束后,通过scoreDirector.getJustificationList()获取违规实例详情,直接查看违规的设备/技术员及求和结果;
  • 编写单元测试:构造已知违规场景验证约束,例如:
    def test_technician_capacity_hard():
        tech = Technician(id=1, name="Test", rbh_to_plan=10.0, rbh_assigned=0, iums=set())
        device1 = Device(index=1, ium="test", technician=tech)
        device1.rbh_norm = 6.0
        device2 = Device(index=2, ium="test", technician=tech)
        device2.rbh_norm = 5.0
        solution = DeviceSchedule(devices=[device1, device2], technicians=[tech])
        
        constraint_factory = ConstraintFactory.createFromSolutionClass(DeviceSchedule)
        constraint = technician_capacity_hard(constraint_factory)
        score = constraint.calculateScore(solution)
        assert score == HardSoftScore.ofHard(-1)  # 预期1次硬违规
    
  • 逐步简化约束:拆分复杂约束,先验证分组求和是否正确,再验证过滤逻辑,最后添加惩罚;
  • 检查求解器状态:求解结束后打印最终得分和违规详情,区分是约束未触发(硬得分0)还是求解器未找到可行解(硬得分负)。

内容的提问来源于stack exchange,提问作者Peter Pirog

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 05:43:18