如何配置Timefold使规划实体保留未分配值
调度问题求解器优化:无合适技术人员时自动设为未分配
问题背景
我正在处理一个调度问题,需要将技术人员分配给待服务设备。当没有具备所需技能的合适技术人员时,设备应保持未分配状态(即device.pesel设为None)。
领域模型代码
@dataclass class Technician: id: Annotated[int, PlanningId] pesel: str name: str rbh_per_week: int rbh_per_year: float rbh_week_plan: float selected_rbh: float free_rbh: float iums: Set[str] def has_ium(self, ium: str) -> bool: return ium in self.iums def __str__(self) -> str: return (f"Technician(id={self.id}, name={self.name}, pesel={self.pesel}, " f"rbh_per_week={self.rbh_per_week}, rbh_per_year={self.rbh_per_year}, " f"rbh_week_plan={self.rbh_week_plan}, selected_rbh={self.selected_rbh}, " f"free_rbh={self.free_rbh}, iums={self.iums})") @planning_entity @dataclass class Device: index: Annotated[int, PlanningId] ind_rek: str ium: str nazwa: str typ: str nr_fab: str norma_rbh: float data_dostawy: str uzytkownik: str pesel: Annotated[Technician | None, PlanningVariable(value_range_provider_refs=['technicianRange'], nullable=True)] = field(default=None) def __str__(self) -> str: technician_str = str(self.pesel) if self.pesel else "None" return (f"Device(index={self.index}, ind_rek={self.ind_rek}, ium={self.ium}, " f"nazwa={self.nazwa}, typ={self.typ}, nr_fab={self.nr_fab}, " f"norma_rbh={self.norma_rbh}, data_dostawy={self.data_dostawy}, " f"uzytkownik={self.uzytkownik}, assigned_technician={technician_str})") @planning_solution @dataclass class DeviceSchedule: id: str technician_list: Annotated[List[Technician], ProblemFactCollectionProperty, ValueRangeProvider] device_list: Annotated[List[Device], PlanningEntityCollectionProperty] score: Annotated[HardSoftScore, PlanningScore] = field(default=None) def __str__(self): return ( f"DeviceSchedule(" f"id={self.id},\n" f"technician_list={self.technician_list},\n" f"device_list={self.device_list},\n" f"score={self.score}" f")" )
当前约束代码
@constraint_provider def define_constraints(constraint_factory: ConstraintFactory): return [ # HARD constraint to avoid assigning a technician without the required skill technician_skill_conflict(constraint_factory), ] def technician_skill_conflict(constraint_factory: ConstraintFactory): return (constraint_factory .for_each(Device) .filter(lambda device: device.pesel is not None and not device.pesel.has_ium(device.ium)) .penalize(HardSoftScore.ONE_HARD) .as_constraint("Technician skill conflict"))
现存问题
尽管已在PlanningVariable中设置nullable=True,且定义了硬约束惩罚技能不匹配的分配,但求解器仍倾向于为每个设备分配技术人员,即便不分配是更优选择。
解决方案
要让求解器在无合适人员时自动将device.pesel设为None,需要从约束和求解器配置两方面调整:
1. 清理强制分配的隐含约束
检查是否存在其他硬约束强制要求设备必须分配技术人员,若有则直接移除,或改为软约束。当前的硬约束仅惩罚技能不匹配的分配,本身不强制分配,这部分逻辑没问题。
2. 添加软约束引导合理分配逻辑
通过软约束明确优先级:优先分配具备对应技能的人员,其次允许未分配状态。修改后的约束代码如下:
@constraint_provider def define_constraints(constraint_factory: ConstraintFactory): return [ # 硬约束:禁止分配无对应技能的技术人员 technician_skill_conflict(constraint_factory), # 软约束:奖励分配了合适技能的技术人员 reward_skill_matched_assignment(constraint_factory), # 可选软约束:轻微惩罚未分配设备(仅当需要尽可能分配时启用) penalize_unassigned_device(constraint_factory), ] def technician_skill_conflict(constraint_factory: ConstraintFactory): return (constraint_factory .for_each(Device) .filter(lambda device: device.pesel is not None and not device.pesel.has_ium(device.ium)) .penalize(HardSoftScore.ONE_HARD) .as_constraint("Technician skill conflict")) def reward_skill_matched_assignment(constraint_factory: ConstraintFactory): return (constraint_factory .for_each(Device) .filter(lambda device: device.pesel is not None and device.pesel.has_ium(device.ium)) .reward(HardSoftScore.ONE_SOFT) .as_constraint("Reward skill-matched assignment")) def penalize_unassigned_device(constraint_factory: ConstraintFactory): return (constraint_factory .for_each(Device) .filter(lambda device: device.pesel is None) .penalize(HardSoftScore.ONE_SOFT) .as_constraint("Penalize unassigned device"))
3. 优化求解器搜索策略
若求解器仍强行分配,可调整构造启发式策略,例如启用FIRST_FIT_DECREASING或CHEAPEST_INSERTION,让求解器优先为设备匹配合适人员,无匹配时自动留空。
4. 验证技能匹配逻辑
确认Technician.has_ium()方法逻辑正确,检查iums集合是否正确加载了技术人员的技能标签,避免因数据错误导致求解器误判无合适人员。
核心思路总结
- 用硬约束完全禁止无效分配(技能不匹配的技术人员)
- 用软约束引导求解器优先选择有效分配,同时允许未分配状态
- 移除任何强制设备必须分配的硬约束
内容的提问来源于stack exchange,提问作者Peter Pirog
相关产品推荐
相关产品推荐

