13.10. 配置限制
决定每个约束的正确权重和级别并不容易。它通常涉及不同的拥有者及其优先级。此外,量化软约束的影响通常是业务经理的新体验,因此他们还需要大量的迭代才能获得它。为了更容易做到这一点,请使用 @ConstraintConfiguration 类及约束 weights 和参数。然后,提供 UI,以便业务管理器可以调整约束权重,并视觉化生成的解决方案,如下图所示:

例如,在会议调度问题中,最小暂停约束具有约束权重,但也有一个 constraint 参数,用于定义同一 speaker 的两个对话之间的时间长度。暂停长度取决于会议:在某些大会议中,20 分钟没有足够时间来从一个房间进入一个空间,在小的会议 10 分钟内可以有足够的时间。暂停长度是约束配置中的一个字段,没有 @ConstraintWeight 注释。
每个约束都有一个约束软件包和约束名称,它们组成约束 ID。约束 ID 将约束权重与约束实现连接。对于每个约束 weight,必须使用相同软件包和名称相同的约束实现。
-
@ConstraintConfiguration注释具有一个constraintPackage属性,默认为约束配置类的软件包。带有约束流的情况通常不需要指定它。 -
@ConstraintWeight注释具有一个值,即约束名称(例如"REGION 冲突")。它继承了来自@ConstraintConfiguration的 constraint 软件包,但它可以覆盖它,例如@ConstraintWeight (constraintPackage = "…region.france", …)使用不同于其他权重的约束软件包。
因此,每个约束权重都以约束软件包和约束名称结束。每个约束权重链接带有约束实现,例如在约束流中:
public final class ConferenceSchedulingConstraintProvider implements ConstraintProvider {
@Override
public Constraint[] defineConstraints(ConstraintFactory factory) {
return new Constraint[] {
speakerConflict(factory),
themeTrackConflict(factory),
contentConflict(factory),
...
};
}
protected Constraint speakerConflict(ConstraintFactory factory) {
return factory.forEachUniquePair(...)
...
.penalizeConfigurable("Speaker conflict", ...);
}
protected Constraint themeTrackConflict(ConstraintFactory factory) {
return factory.forEachUniquePair(...)
...
.penalizeConfigurable("Theme track conflict", ...);
}
protected Constraint contentConflict(ConstraintFactory factory) {
return factory.forEachUniquePair(...)
...
.penalizeConfigurable("Content conflict", ...);
}
...
}
每个约束 weights 定义其约束的分数级别和分数权重。约束实现调用 rewardConfigurable () 或 penalizeConfigurable (),约束权重会自动应用。
如果约束实现提供了匹配的权重,则匹配 weight 乘以约束权重。例如,内容冲突 约束权重默认为 100soft,约束实现根据共享内容标签的数量和两个通信的重叠持续时间来分配每个匹配项:
@ConstraintWeight("Content conflict")
private HardMediumSoftScore contentConflict = HardMediumSoftScore.ofSoft(100);Constraint contentConflict(ConstraintFactory factory) {
return factory.forEachUniquePair(Talk.class,
overlapping(t -> t.getTimeslot().getStartDateTime(),
t -> t.getTimeslot().getEndDateTime()),
filtering((talk1, talk2) -> talk1.overlappingContentCount(talk2) > 0))
.penalizeConfigurable("Content conflict",
(talk1, talk2) -> talk1.overlappingContentCount(talk2)
* talk1.overlappingDurationInMinutes(talk2));
}
因此,当 2 重叠仅与 1 个内容标签进行交流,并且与 60 分钟重叠时,分数会受到 -6000soft 的影响。但是,当 2 重叠与共享 3 内容标签通信时,匹配权重为 180,因此分数受到 -18000soft 的影响。
流程
-
创建一个新类来存放约束 weight 和其他约束参数,例如 Conversation
ConstraintConfiguration。 使用
@ConstraintConfiguration注解此类:@ConstraintConfiguration public class ConferenceConstraintConfiguration { ... }在规划解决方案中添加约束配置,并使用
@ConstraintConfigurationProvider注解该字段或属性:@PlanningSolution public class ConferenceSolution { @ConstraintConfigurationProvider private ConferenceConstraintConfiguration constraintConfiguration; ... }在约束配置类中,为每个约束添加一个
@ConstraintWeight属性,并为每个约束赋予一个默认值:@ConstraintConfiguration(constraintPackage = "...conferencescheduling.score") public class ConferenceConstraintConfiguration { @ConstraintWeight("Speaker conflict") private HardMediumSoftScore speakerConflict = HardMediumSoftScore.ofHard(10); @ConstraintWeight("Theme track conflict") private HardMediumSoftScore themeTrackConflict = HardMediumSoftScore.ofSoft(10); @ConstraintWeight("Content conflict") private HardMediumSoftScore contentConflict = HardMediumSoftScore.ofSoft(100); ... }@ConstraintConfigurationProvider注释会自动将约束配置作为问题事实公开。不需要添加@ProblemFactProperty注释。约束权重不能为空。-
在 UI 中公开约束权重,以便业务用户可以调整值。前面的示例使用
ofHard ()、$Medium ()和Soft ()方法进行此操作。请注意,它如何 将内容冲突 约束默认设置为十倍,而不是 主题跟踪冲突 约束。通常,约束权重只使用一个分数级别,但可以使用多个分数级别(以较小的性能成本)。