如何在OptaPy中使用constraint_factory定义列表元素约束
OptaPy 子对象未分配工具约束实现方案
现有类定义
class ToolFact: id : str type : str @planning_entity class SubObjectFact: id : str tool : ToolFact @planning_entity class ObjectFact: id : str sub_objects : list[SubObjectFact]
目标约束规则
遍历所有ObjectFact下挂载的SubObjectFact实例,当SubObjectFact的tool属性为None时施加惩罚,对应伪代码:
for each ObjectFact -> object for each SubOjectFact in object.sub_object -> sub_object penalize if sub_object.tool is None
可行实现
方案1:直接以SubObjectFact为入口(性能最优)
你要校验的核心主体是SubObjectFact的tool规划变量,不需要从ObjectFact层做嵌套遍历,直接匹配所有SubObjectFact事实即可,这是约束流的标准写法:
from optapy.score import HardSoftScore def define_constraints(constraint_factory): return [ # 未分配工具的子对象惩罚 constraint_factory.for_each(SubObjectFact) .filter(lambda sub_object: sub_object.tool is None) .penalize("SubObject missing assigned tool", HardSoftScore.ONE_HARD) ]
该实现会自动利用OptaPlanner的增量分数计算能力,仅当SubObjectFact的tool属性变化时触发重算,性能最高。
方案2:从ObjectFact出发遍历(适合需关联父对象属性的场景)
如果后续规则需要同时用到ObjectFact的属性(比如根据父对象等级调整惩罚权重),可以用flatten_last方法将sub_objects列表展开为单个SubObjectFact实例,完全匹配你伪代码里的两层遍历逻辑:
from optapy.score import HardSoftScore def define_constraints(constraint_factory): return [ constraint_factory.for_each(ObjectFact) # 展开sub_objects列表,流中元素从ObjectFact变为 (ObjectFact, SubObjectFact) 元组 .flatten_last(lambda parent_obj: parent_obj.sub_objects) .filter(lambda parent_obj, sub_object: sub_object.tool is None) .penalize("SubObject missing assigned tool", HardSoftScore.ONE_HARD) ]
注意事项
- 不要在
filter方法内手写嵌套循环遍历sub_objects,这类命令式写法会破坏OptaPlanner的增量计算逻辑,求解时性能会出现严重下降 - 若没有必须读取
ObjectFact属性的需求,优先选择方案1,求解速度更快
内容的提问来源于stack exchange,提问作者Adrien DAVID
相关产品推荐
相关产品推荐

