ProblemFact 中具有 InverseRelationShadowVariable 的 PlanningEntity 字段未更新 - Optaplanner
PlanningEntity field with an InverseRelationShadowVariable in a ProblemFact is not updated - Optaplanner
我有一个 ProblemFact AgentAvailability
引用影子计划实体 Agent
(Agent
没有 PlanningVariable,只有一个 InverseRelationShadowVariable 引用 PlanningEntity Shift
)
这是我的 class 定义:
Shift.class
@PlanningEntity
public class Shift extends AbstractPersistable implements Comparable<Shift> {
@PlanningVariable(valueRangeProviderRefs = { "agentRange" }, nullable = true)
private Agent agent;
private Spot spot;
private LocalDateTime startDateTime;
private LocalDateTime endDateTime;
//Constructor, getters, setters and JPA annotations removed for clarity
Agent.class
@PlanningEntity
public class Agent implements Comparable<Agent> {
@PlanningId
private long registrationNumber;
private String username;
private boolean pro;
private SortedSet<Skill> skillSet = new TreeSet<>();
@InverseRelationShadowVariable(sourceVariableName = "agent")
private SortedSet<Shift> shiftSet = new TreeSet<>();
//Constructor, getters, setters and JPA annotations removed for clarity
AgentAvailability.class
public class AgentAvailability extends AbstractPersistable implements Comparable<AgentAvailability> {
private Agent agent;
private LocalDateTime startDateTime;
private LocalDateTime endDateTime;
//Constructor, getters, setters and JPA annotations removed for clarity
这是我的解决方案定义:
Schedule.class
@PlanningSolution
public class Schedule extends AbstractPersistable {
// ...
@ProblemFactCollectionProperty
@ValueRangeProvider(id = "agentRange")
private List<Agent> agentList = new ArrayList<>();
@ProblemFactCollectionProperty
private List<AgentAvailability> agentAvailabilities = new ArrayList<>();
@PlanningEntityCollectionProperty
private List<Shift> shifts = new ArrayList<>();
// ...
//Constructor, getters, setters and JPA annotations removed for clarity
inverseShadowVariable 按预期工作:当 Agent
分配给 Shift
时,shiftSet
来自分配的 Agent
随此班次更新。
问题出在 AgentAvailability
中的字段 agent
。
根据我的理解,当 Optaplanner 克隆解决方案时,根据 documentation,它会重用 ProblemFacts 的实例,并使用克隆来规划实体。
因为 AgentAvailability
是一个事实,我假设它被重用(在调试器视图中,情况就是这样,初始问题和找到的最佳解决方案之间的实例是相同的)
Agent
来自 Schedule
的 shiftSet
也被正确克隆到解决方案中。
但问题是 AgentAvailability
中的 agent
字段仍然是原始问题中的 Agent
实例。
因此,AgentAvailability
个实例中 agent
的字段 shiftSet
仍然是空的。
找到bestSolution最后是这样,计算的时候也是这样。
因此,我不能像这样使用 约束:
return constraintFactory.from(AgentAvailability.class)
.groupBy(AgentAvailability::getAgent, count())
.filter((agent, availabilities) -> availabilities - agent.getShiftSet().size() > 0)
.penalizeConfigurableLong(ShiftSchedulingConstraintConfiguration.PRO_HAS_SHIFT, (agent, availabilities) -> availabilities - agent.getShiftSet().size());
因为 agent.getShiftSet().size()
始终 return 0,即使代理已分配班次。
一种可能的解决方法是使用 from(Agent.class) 子句,并与 AgentAvailabilities 和 Shift 结合以从 Agent
实体“重建”shiftSet。但是有什么方法可以从 AgentAvailability
的 Agent
中的 Agent
实体中“克隆” shiftSet 这样我就可以使用来自 AgentAvailability
的代理字段和更新的 shiftSet
?
这是您的模型存在的问题。您不能期望 AgentAvailability
既被克隆又不被克隆。如果 AgentAvailability
是一个问题事实,它不会被克隆,因此 agent
的值不会被改变。
(旁注:也许我们应该检测一个实体是否从规划事实中引用,并快速失败?我看不出这种情况除了麻烦之外还会导致什么。)
这个问题有几种解决方法。我建议您将 Agent
设为问题事实并创建一个新实体 AgentAssignment
,将 1 对 1 映射到 Agent
。在此模型中,您可以在其他事实中自由引用 Agent
个事实实例。
这样约束就变得相对容易写了。
from(AgentAssignment.class)
.join(AgentAvailability.class,
Joiners.equal(
AgentAssignment::getAgent,
AgentAvailability::getAgent))
.groupBy(
(agentAssignment, agentAvailability) -> agentAssignment,
countBi())
.filter((agentAssignment, availabilities) -> availabilities - agentAssignment.getShiftSet().size() > 0)
...
作为替代解决方案,在 AgentAvailability
上添加 @DeepPlanningClone
注释:
@DeepPlanningClone
public class AgentAvailability ... { ... }
不过 Lukas 的回答更好。
我有一个 ProblemFact AgentAvailability
引用影子计划实体 Agent
(Agent
没有 PlanningVariable,只有一个 InverseRelationShadowVariable 引用 PlanningEntity Shift
)
这是我的 class 定义:
Shift.class
@PlanningEntity
public class Shift extends AbstractPersistable implements Comparable<Shift> {
@PlanningVariable(valueRangeProviderRefs = { "agentRange" }, nullable = true)
private Agent agent;
private Spot spot;
private LocalDateTime startDateTime;
private LocalDateTime endDateTime;
//Constructor, getters, setters and JPA annotations removed for clarity
Agent.class
@PlanningEntity
public class Agent implements Comparable<Agent> {
@PlanningId
private long registrationNumber;
private String username;
private boolean pro;
private SortedSet<Skill> skillSet = new TreeSet<>();
@InverseRelationShadowVariable(sourceVariableName = "agent")
private SortedSet<Shift> shiftSet = new TreeSet<>();
//Constructor, getters, setters and JPA annotations removed for clarity
AgentAvailability.class
public class AgentAvailability extends AbstractPersistable implements Comparable<AgentAvailability> {
private Agent agent;
private LocalDateTime startDateTime;
private LocalDateTime endDateTime;
//Constructor, getters, setters and JPA annotations removed for clarity
这是我的解决方案定义:
Schedule.class
@PlanningSolution
public class Schedule extends AbstractPersistable {
// ...
@ProblemFactCollectionProperty
@ValueRangeProvider(id = "agentRange")
private List<Agent> agentList = new ArrayList<>();
@ProblemFactCollectionProperty
private List<AgentAvailability> agentAvailabilities = new ArrayList<>();
@PlanningEntityCollectionProperty
private List<Shift> shifts = new ArrayList<>();
// ...
//Constructor, getters, setters and JPA annotations removed for clarity
inverseShadowVariable 按预期工作:当 Agent
分配给 Shift
时,shiftSet
来自分配的 Agent
随此班次更新。
问题出在 AgentAvailability
中的字段 agent
。
根据我的理解,当 Optaplanner 克隆解决方案时,根据 documentation,它会重用 ProblemFacts 的实例,并使用克隆来规划实体。
因为 AgentAvailability
是一个事实,我假设它被重用(在调试器视图中,情况就是这样,初始问题和找到的最佳解决方案之间的实例是相同的)
Agent
来自 Schedule
的 shiftSet
也被正确克隆到解决方案中。
但问题是 AgentAvailability
中的 agent
字段仍然是原始问题中的 Agent
实例。
因此,AgentAvailability
个实例中 agent
的字段 shiftSet
仍然是空的。
找到bestSolution最后是这样,计算的时候也是这样。
因此,我不能像这样使用 约束:
return constraintFactory.from(AgentAvailability.class)
.groupBy(AgentAvailability::getAgent, count())
.filter((agent, availabilities) -> availabilities - agent.getShiftSet().size() > 0)
.penalizeConfigurableLong(ShiftSchedulingConstraintConfiguration.PRO_HAS_SHIFT, (agent, availabilities) -> availabilities - agent.getShiftSet().size());
因为 agent.getShiftSet().size()
始终 return 0,即使代理已分配班次。
一种可能的解决方法是使用 from(Agent.class) 子句,并与 AgentAvailabilities 和 Shift 结合以从 Agent
实体“重建”shiftSet。但是有什么方法可以从 AgentAvailability
的 Agent
中的 Agent
实体中“克隆” shiftSet 这样我就可以使用来自 AgentAvailability
的代理字段和更新的 shiftSet
?
这是您的模型存在的问题。您不能期望 AgentAvailability
既被克隆又不被克隆。如果 AgentAvailability
是一个问题事实,它不会被克隆,因此 agent
的值不会被改变。
(旁注:也许我们应该检测一个实体是否从规划事实中引用,并快速失败?我看不出这种情况除了麻烦之外还会导致什么。)
这个问题有几种解决方法。我建议您将 Agent
设为问题事实并创建一个新实体 AgentAssignment
,将 1 对 1 映射到 Agent
。在此模型中,您可以在其他事实中自由引用 Agent
个事实实例。
这样约束就变得相对容易写了。
from(AgentAssignment.class)
.join(AgentAvailability.class,
Joiners.equal(
AgentAssignment::getAgent,
AgentAvailability::getAgent))
.groupBy(
(agentAssignment, agentAvailability) -> agentAssignment,
countBi())
.filter((agentAssignment, availabilities) -> availabilities - agentAssignment.getShiftSet().size() > 0)
...
作为替代解决方案,在 AgentAvailability
上添加 @DeepPlanningClone
注释:
@DeepPlanningClone
public class AgentAvailability ... { ... }
不过 Lukas 的回答更好。