首页 > 解决方案 > 未更新 ProblemFact 中具有 InverseRelationShadowVariable 的 PlanningEntity 字段 - Optaplanner

问题描述

我有一个引用影子计划实体的ProblemFact (没有PlanningVariable,只有一个引用PlanningEntity的InverseRelationShadowVariableAgentAvailabilityAgentAgent Shift

这是我的类定义:

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正在按预期工作:当将 an分配Agent给 a时Shift,分配的shiftSetthe 将Agent使用此班次进行更新。

问题出agentAgentAvailability.

据我了解,当 Optaplanner 根据文档克隆解决方案时,它会重用 ProblemFacts 的实例,并使用克隆来规划实体。

因为AgentAvailability这是一个事实,我假设它被重用(在调试器视图中,情况就是这样,初始问题和找到的最佳解决方案之间的实例是相同的)

AgentfromSchedule也被正确地克隆shiftSet到解决方案中。

但问题是agent字段 inAgentAvailability 仍然是Agent原始问题的实例。

因此,实例中的字段仍然shiftSet为空。agentAgentAvailability

它是在最后找到的 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() 即使代理已分配到班次,也始终返回 0。

一种可能的解决方法是使用 from(Agent.class) 子句,并加入 AgentAvailabilities 和 Shift 以从Agent实体“重建”shiftSet。但是有什么方法可以从Agent实体中“克隆”shiftSet Agent,以便我可以使用更新后的AgentAvailability代理字段?AgentAvailabilityshiftSet

标签: javaquarkusoptaplanner

解决方案


这是您的模型的问题。你不能期望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)
    ...

推荐阅读