Optaplanner 惩罚不使用可空变量
Optaplanner Penalty Not Working With a Nullable Variable
我将 OptaPlanner 与两个规划变量一起使用,其中一个是用 nullable=true 定义的。
按照会议示例(为简单起见),假设房间可以为空,但时间不能为空。
我在 non-null、time
变量上定义了一个约束,但似乎惩罚只在可空 room
变量不为空时有效,否则失败。
下面是我的代码片段:
@PlanningEntity
public class Meeting {
@PlanningVariable(valueRangeProviderRefs = "time")
private LocalDateTime time;
@PlanningVariable(valueRangeProviderRefs = "availableRooms", nullable = true)
private Room room;
private long personId;
...
}
在我的约束提供者 class 中,我定义了以下约束以确保一个人不能参加两个单独的会议:[=20=]
protected Constraint samePersonAndTimeConflict(ConstraintFactory constraintFactory) {
return constraintFactory
// Select each pair of 2 different meetings ...
.forEachUniquePair(Meeting.class,
// ... for the same person ...
equal(Meeting::getPersonId),
// ... in the same time ...
equal(Meeting::getDateTime))
// ... and penalize each pair with a hard weight.
.penalize(SAME_PERSON_AND_TIME_CONFLICT, HardSoftScore.ONE_HARD);
}
当创建两个具有 non-null 值的 object 时,如果同一个人应该同时参加两个单独的会议,则此约束将正常工作并以一个硬分作为惩罚。但是,当使用可空房间变量确实为空的 object 时,没有惩罚。结果是我得到了一个解决方案,其中许多实例对同一个人具有相同的时间分配。
我也尝试以其他方式操纵约束,例如使用“forEachIncludingNullVars”和“forEach”,但我看到了相同的结果:
protected Constraint samePersonAndTimeConflict(ConstraintFactory constraintFactory) {
return constraintFactory
.forEachIncludingNullVars(Meeting.class)
//.filter(meeting -> meeting.getDateTime() != null)
.join(Meeting.class,
lessThan(Meeting::getId),
equal(Meeting::getTime),
equal(Meeting::getPersonId)) //,
.penalize(SAME_PERSON_AND_TIME_CONFLICT, HardSoftScore.ONE_HARD);
我也尝试将分数 class 更改为 HardMediumSoftScore
并受到 ONE_MEDIUM
的惩罚。对于空房间的会议仍然没有惩罚。
OptaPlanner 似乎根本无法正常工作。
在这一点上,我不知道我还能尝试什么。请指教
*** 编辑中 ***
按照下面关于使用带有附加 forEachIncludingNullVars
子句的嵌套 constraintFactory
的建议,我最终按如下方式实现了我的约束 - 现在它起作用了:
protected Constraint samePersonAndTimeConflict(ConstraintFactory constraintFactory) {
final Constraint constraint = constraintFactory.forEachIncludingNullVars(Meeting.class)
.join(
constraintFactory.forEachIncludingNullVars(Meeting.class)
.filter(meeting -> meeting.getTime() != null),
lessThan(WorkDay::getId),
equal(WorkDay::getEmployeeId),
equal(WorkDay::getDate))
.penalize("Same person and time conflict", HardMediumSoftScore.ONE_HARD);
return constraint;
}
forEach()
在这里不起作用,因为它只会为您提供 none 变量为空的实体。 forEachIncludingNullVars()
是去这里的路。但您还需要了解 join
与 forEach
具有相同的行为 - 它不包括具有空变量的实体。
考虑到这一点,您需要加入嵌套流,如下所示:
protected Constraint samePersonAndTimeConflict(ConstraintFactory constraintFactory) {
return constraintFactory
.forEachIncludingNullVars(Meeting.class)
.join(
constraintFactory.forEachIncludingNullVars(Meeting.class),
// add your joiners here
).penalize(SAME_PERSON_AND_TIME_CONFLICT, HardSoftScore.ONE_HARD);
}
这样,联接将创建所有实体的 cross-product,无论它们的任何变量是否为空。然后就变成了 filter
剔除您不希望看到空值的部分的问题。
我将 OptaPlanner 与两个规划变量一起使用,其中一个是用 nullable=true 定义的。 按照会议示例(为简单起见),假设房间可以为空,但时间不能为空。
我在 non-null、time
变量上定义了一个约束,但似乎惩罚只在可空 room
变量不为空时有效,否则失败。
下面是我的代码片段:
@PlanningEntity
public class Meeting {
@PlanningVariable(valueRangeProviderRefs = "time")
private LocalDateTime time;
@PlanningVariable(valueRangeProviderRefs = "availableRooms", nullable = true)
private Room room;
private long personId;
...
}
在我的约束提供者 class 中,我定义了以下约束以确保一个人不能参加两个单独的会议:[=20=]
protected Constraint samePersonAndTimeConflict(ConstraintFactory constraintFactory) {
return constraintFactory
// Select each pair of 2 different meetings ...
.forEachUniquePair(Meeting.class,
// ... for the same person ...
equal(Meeting::getPersonId),
// ... in the same time ...
equal(Meeting::getDateTime))
// ... and penalize each pair with a hard weight.
.penalize(SAME_PERSON_AND_TIME_CONFLICT, HardSoftScore.ONE_HARD);
}
当创建两个具有 non-null 值的 object 时,如果同一个人应该同时参加两个单独的会议,则此约束将正常工作并以一个硬分作为惩罚。但是,当使用可空房间变量确实为空的 object 时,没有惩罚。结果是我得到了一个解决方案,其中许多实例对同一个人具有相同的时间分配。
我也尝试以其他方式操纵约束,例如使用“forEachIncludingNullVars”和“forEach”,但我看到了相同的结果:
protected Constraint samePersonAndTimeConflict(ConstraintFactory constraintFactory) {
return constraintFactory
.forEachIncludingNullVars(Meeting.class)
//.filter(meeting -> meeting.getDateTime() != null)
.join(Meeting.class,
lessThan(Meeting::getId),
equal(Meeting::getTime),
equal(Meeting::getPersonId)) //,
.penalize(SAME_PERSON_AND_TIME_CONFLICT, HardSoftScore.ONE_HARD);
我也尝试将分数 class 更改为 HardMediumSoftScore
并受到 ONE_MEDIUM
的惩罚。对于空房间的会议仍然没有惩罚。
OptaPlanner 似乎根本无法正常工作。 在这一点上,我不知道我还能尝试什么。请指教
*** 编辑中 ***
按照下面关于使用带有附加 forEachIncludingNullVars
子句的嵌套 constraintFactory
的建议,我最终按如下方式实现了我的约束 - 现在它起作用了:
protected Constraint samePersonAndTimeConflict(ConstraintFactory constraintFactory) {
final Constraint constraint = constraintFactory.forEachIncludingNullVars(Meeting.class)
.join(
constraintFactory.forEachIncludingNullVars(Meeting.class)
.filter(meeting -> meeting.getTime() != null),
lessThan(WorkDay::getId),
equal(WorkDay::getEmployeeId),
equal(WorkDay::getDate))
.penalize("Same person and time conflict", HardMediumSoftScore.ONE_HARD);
return constraint;
}
forEach()
在这里不起作用,因为它只会为您提供 none 变量为空的实体。 forEachIncludingNullVars()
是去这里的路。但您还需要了解 join
与 forEach
具有相同的行为 - 它不包括具有空变量的实体。
考虑到这一点,您需要加入嵌套流,如下所示:
protected Constraint samePersonAndTimeConflict(ConstraintFactory constraintFactory) {
return constraintFactory
.forEachIncludingNullVars(Meeting.class)
.join(
constraintFactory.forEachIncludingNullVars(Meeting.class),
// add your joiners here
).penalize(SAME_PERSON_AND_TIME_CONFLICT, HardSoftScore.ONE_HARD);
}
这样,联接将创建所有实体的 cross-product,无论它们的任何变量是否为空。然后就变成了 filter
剔除您不希望看到空值的部分的问题。