Поле планирования с переменной тенью, изменяемой в случае проблемы, не обновляется — Optaplanner

#java #quarkus #optaplanner

Вопрос:

У меня есть проблема AgentAvailability , Agent которая Agent ссылается на объект теневого планирования ( не имеет переменной планирования, только обратная переменная, указывающая на свойство планирования 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
 

переменная inverseshadow работает так, как ожидалось: когда an Agent назначается a Shift , значение shiftSet из назначенного Agent обновляется с учетом этого сдвига.

Проблема в поле agent от AgentAvailability .

Насколько я понимаю, когда Optaplanner клонирует решение, согласно документации, он повторно использует экземпляры для проблемных объектов и использует клонирование для объектов планирования.

Поскольку AgentAvailability это факт, я предполагаю, что он используется повторно (и в представлении отладчика это так, экземпляр одинаков между исходной проблемой и найденным лучшим решением)

Agent от Schedule правильно клонированы с их shiftSet решением тоже.

Но проблема в том, что agent поле in AgentAvailability по-прежнему является Agent экземпляром исходной проблемы.

Таким образом, поле shiftSet agent в AgentAvailability экземплярах по-прежнему пусто.

Это имеет место в конце в найденном наилучшем решении, но также и во время вычислений.

Поэтому я не могу использовать подобное ограничение:

  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, даже если агент был назначен на смену.

Возможным обходным путем было бы использовать от(Agent.class) предложение и объединяйтесь с AgentAvailabilities и переключайтесь, чтобы «перестроить» набор сдвигов из Agent Сущности. но есть ли какой-либо способ «клонировать» набор сдвигов из Agent сущностей в поле « Agent из», AgentAvailability чтобы я мог использовать поле «агент AgentAvailability » с обновлением shiftSet ?

Ответ №1:

Это проблема с вашей моделью. Вы не можете ожидать 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)
    ...
 

Комментарии:

1. Это имеет смысл, спасибо за ваше предложение, я стараюсь

Ответ №2:

В качестве альтернативного решения добавьте @DeepPlanningClone аннотацию на AgentAvailability :

 @DeepPlanningClone
public class AgentAvailability ... { ... }
 

Но ответ Лукаса лучше.

Комментарии:

1. Я думал, что уже пробовал это, но, должно быть, нет… так как это действительно работает с этой аннотацией. Я буду придерживаться ответа Лукаса, так как вы оба согласны с этим. Спасибо за вашу помощь и ваш продукт 😉