optaplanner 中带有 anchorshadowed 变量的可变侦听器损坏

Variablelistener corruption with anchorshadowedvariable in optaplanner

我正在尝试解决一个类似于 Time window 车辆路径问题的规划问题。

所以我正在寻找包含任务列表的解决方案。任务可以分配给两个工人之一。

我的任务 class 看起来像那样(我没有 cut/paste 这里的所有 getter 和 setter):

@PlanningEntity(difficultyComparatorClass = TaskDifficultyComparator.class)
public class Task implements Location {
    private String location;
    private LocalTime arrivalTime;
    private Location previousLocation;
    private Task nextTask;
    private StartLocal startLocal;


    private Duration taskDuration = Duration.ofMinutes(20);
    private LocalTime readyTime;
    private LocalTime dueTime;

    @PlanningVariable(valueRangeProviderRefs = {"tasks", "startLocal"},
            graphType = PlanningVariableGraphType.CHAINED)
    public Location getPreviousLocation() {
        return previousLocation;
    }
    public void setPreviousLocation(Location previousLocation) {
        this.previousLocation = previousLocation;
    }

    @Override
    public String {return location;}

    @Override
    public void setLocation(String location) {this.location = location;}

    public int getTimeToGoTo(Location location) {
        ....
    }

    @AnchorShadowVariable(sourceVariableName = "previousLocation")
    public StartLocal getStartLocal() {
        return startLocal;
    }
    public void setStartLocal(StartLocal startLocal) {
        this.startLocal = startLocal;
    }

    @CustomShadowVariable(variableListenerClass = ArrivalTimeVariableListener.class,
            sources = {@CustomShadowVariable.Source(variableName = "previousLocation")})
    public LocalTime getArrivalTime() {
        return arrivalTime;
    }
    public void setArrivalTime(LocalTime arrivalTime) {
        this.arrivalTime = arrivalTime;
    }
}

我的变量监听器 class 是:

public class ArrivalTimeVariableListener implements VariableListener<Task> {
    @Override
    public void beforeEntityAdded(ScoreDirector scoreDirector, Task task) {}

    @Override
    public void afterEntityAdded(ScoreDirector scoreDirector, Task task) {
        updateArrivalTime(scoreDirector, task);
    }

    @Override
    public void beforeVariableChanged(ScoreDirector scoreDirector, Task task) {        }

    @Override
    public void afterVariableChanged(ScoreDirector scoreDirector, Task task) {
        updateArrivalTime(scoreDirector, task);
    }

    @Override
    public void beforeEntityRemoved(ScoreDirector scoreDirector, Task task) {        }

    @Override
    public void afterEntityRemoved(ScoreDirector scoreDirector, Task task) {        }

    private void updateArrivalTime(ScoreDirector scoreDirector, Task task){
        LocalTime arrivalTimeInThisLocation = calculateArrivalTime(task);
        Task shadowTask = task;
        while (shadowTask != null) {
            scoreDirector.beforeVariableChanged(shadowTask, "arrivalTime");
            shadowTask.setArrivalTime(arrivalTimeInThisLocation);
            scoreDirector.afterVariableChanged(shadowTask, "arrivalTime");
            shadowTask = shadowTask.getNextTask();
            if(shadowTask!=null)arrivalTimeInThisLocation = calculateArrivalTime(shadowTask);
        }
    }

    @VisibleForTesting
    static public LocalTime calculateArrivalTime(Task task){
        LocalTime whenArriveAtPreviousLocation = task.getPreviousLocation().getArrivalTime();
        int secondsToGo = task.getTimeToGoTo(task.getPreviousLocation());
        long secondsToCompleteLastTask = 0;
        if(task.getPreviousLocation() instanceof Task){
            secondsToCompleteLastTask = ((Task) task.getPreviousLocation()).getTaskDuration().getSeconds();
        }
        LocalTime whenArriveInThisLocationASAP = whenArriveAtPreviousLocation.
                plusSeconds(secondsToCompleteLastTask).
                plusSeconds(secondsToGo);

        if(whenArriveInThisLocationASAP.isAfter(task.getReadyTime())){                
            return whenArriveInThisLocationASAP;
        }
        return task.getReadyTime();
    }
}

当我尝试解决我的问题时出现以下错误:

VariableListener 损坏:实体(Task 的影子变量 (Task.arrivalTime) 的损坏值 (09:32) 在触发所有 VariableListener 后更改为未损坏的值 (09:33)不改变真正的变量。 可能是该影子变量 (Task.arrivalTime) 的 VariableListener class 在其来源之一在 completedAction 之后更改时忘记更新它(计算初始分数)。

我知道这个问题与 重复。 但是我(非常非常)仔细地阅读了这个 SOF 问题的答案,我不明白我做错了什么。

我很好理解,变量侦听器在我的用例中的作用是在我们刚刚更改的任务之后更新任务的"arrivalTime"。不是吗?

updateArrivaltime() 添加到方法 beforeValueChanged()

我猜你在 FULL_ASSERT.

好的,我发现我哪里做错了。

在我的问题中很难看出我做错了什么。问题是我将解决方案的 shadows 变量初始化为非空值。我的单元测试中有以下代码:

Task task1 = new Task();
task1.setPreviousTask(task0);
task0.setNextTask(task1);

//...
solution.computeAllArrivalTimes();

这不是初始化 shadows 变量和 Planning 变量的好主意。如果我将它们设置为 null,那么每件事都非常有效。

我只需要在 "calculateArrivalTime" 中添加一些空检查以允许调用此方法。