【问题标题】:Impossible variableListener corruption with custom variableListener自定义 variableListener 不可能的 variableListener 损坏
【发布时间】:2018-05-08 16:37:08
【问题描述】:

我正在尝试为我的大学优化一个调度问题。

为了优化我的课程的紧凑性,我尝试在同一天在我的课程课程中更新所有课程的列表,这是计划实体。 我通过planningVariableListener 来做到这一点

这是我的课程:

private int id;
private Course course;
private Period period;
private Room room;
private int blockLength = 1;

private boolean prime = false;
private boolean uKW = false;
private boolean gKW = false;
private boolean FWPM = false;
private boolean biWeekly = false;
private boolean pinned = false;

private List<Student> fwpmStudents = new ArrayList<>();
private int altId = 0;
private String collisionReason;
private String group = "NO_GROUP";
public String[] groupArray;

private List<Lesson> sameDay = new ArrayList<>();

public Lesson() {

}

@PlanningPin
public boolean isPinned() {
    return pinned;
}

public void setPin(boolean pin) {
    pinned = pin;
}

public int getId() {
    return id;
}

public void setId(int id) {
    this.id = id;
}

public Course getCourse() {
    return course;
}

public void setCourse(Course course) {
    this.course = course;
}

@PlanningVariable(valueRangeProviderRefs = "periodId")
public Period getPeriod() {
    return period;
}

public void setPeriod(Period period) {
    this.period = period;
}

@PlanningVariable(valueRangeProviderRefs = "roomId") //IDK if nullable is accepted
public Room getRoom() {
    return room;
}

public void setRoom(Room room) {
    this.room = room;
}

public boolean isPrime() {
    return prime;
}

public void setPrime(boolean prime) {
    this.prime = prime;
}

public int getBlockLength() {
    return blockLength;
}

public void setBlockLength(int blockLength) {
    this.blockLength = blockLength;
}

public boolean getUKWFlag() {
    return uKW;
}

public void setUKWFlag(boolean straight) {
    this.uKW = straight;
}

public boolean getGKWFlag() {
    return gKW;
}

public void setGKWFlag(boolean gKW) {
    this.gKW = gKW;
}

public String getGroup() {
    return group;
}

public void setGroup(String group) {
    this.group = group;
}

public int getAltId() {
    return altId;
}

public void setAltId(int altId) {
    this.altId = altId;
}

public boolean getFWPM() {
    return FWPM;
}

public void setFWPM(boolean FWPM) {
    this.FWPM = FWPM;
}

public boolean isBiWeekly() {
    return biWeekly;
}

public void setBiWeekly(boolean biWeekly) {
    this.biWeekly = biWeekly;
}

public String[] getGroupArray() {
    return groupArray;
}

public void setGroupArray(String[] groupArray) {
    this.groupArray = groupArray;
}

public void addFWPMStudent(Student student) {
    this.fwpmStudents.add(student);
}

public List<Student> getFWPMStudents() {
    return this.fwpmStudents;
}

public String getCollisionReason() {
    return collisionReason;
}

public void setCollisionReason(String collisionReason) {
    this.collisionReason = collisionReason;
}

public boolean collides(Lesson lesson) {
    return CollisionDetector.getCollision(this,lesson);
}

public boolean globalCollides(Lesson lesson) {
    return CollisionDetector.getGlobalCollision(this, lesson);
}

public boolean softCollides(Lesson lesson) {
    return CollisionDetector.softFWPMCollision(this, lesson);
}

public boolean prefCollides(Preference preference) {
    return CollisionDetector.getPreferenceCollision(this, preference);
}

public String toString() {
    return this.getId()
            + " " + this.getCourse().getSemester().getShortName()
            + " " + this.getCourse().getLecturer().getShortName()
            + " " + this.getCourse().getSubject().getShortName()
            + " " + this.getRoom().getNumber() + " " + this.getGroup()
            + "\t" + this.getPeriod().getDay() + " " + this.getPeriod().getHour() + " " + this.getBlockLength();
}

public void addSameDay(Lesson lesson) {
    this.sameDay.add(lesson);
}

public void removeSameDay(Lesson lesson) {
    this.sameDay.remove(lesson);
}

@CustomShadowVariable(variableListenerClass = DayLessonVariableListener.class,
        sources = {@PlanningVariableReference( variableName = "period")})
public List<Lesson> getSameDay() {
    return this.sameDay;
}

public void setSameDay(List<Lesson> sameDay) {
    this.sameDay = sameDay;
}

public boolean isOnlyLesson() {
    if (sameDay.size() == 0)
        return true;
    else
        return false;
}

public void print() {
    System.out.println(toString());
}

这是我的自定义变量监听器

private void update(ScoreDirector<ScheduleSolution> scoreDirector, Lesson lesson) {
    List<Lesson> lessons = scoreDirector.getWorkingSolution().getLessons();
    for (Lesson l : lessons) {
        if (l.getPeriod().getDay() == lesson.getPeriod().getDay()){
            if (!l.equals(lesson)) {
                scoreDirector.beforeVariableChanged(lesson, "sameDay");
                lesson.addSameDay(l);
                scoreDirector.afterVariableChanged(lesson, "sameDay");
            }
        }
    }
    Iterator<Lesson> iter = lesson.getSameDay().iterator();
    while (iter.hasNext()) {
        Lesson x = iter.next();
        if (x.getPeriod().getDay() != lesson.getPeriod().getDay()) {
            scoreDirector.beforeVariableChanged(lesson, "sameDay");
            iter.remove();
            scoreDirector.afterVariableChanged(lesson, "sameDay");
        }
    }
}


@Override
public void beforeEntityAdded(ScoreDirector scoreDirector, Lesson lesson) {
}

@Override
public void afterEntityAdded(ScoreDirector scoreDirector, Lesson lesson) {
}

@Override
public void beforeVariableChanged(ScoreDirector scoreDirector, Lesson lesson) {
    update(scoreDirector, lesson);
}

@Override
public void afterVariableChanged(ScoreDirector scoreDirector, Lesson lesson) {
    update(scoreDirector, lesson);
}

@Override
public void beforeEntityRemoved(ScoreDirector scoreDirector, Lesson lesson) {
}

@Override
public void afterEntityRemoved(ScoreDirector scoreDirector, Lesson lesson) {

}

如果我运行它,我会收到以下错误消息:

线程“main”java.lang.IllegalStateException 中的异常:不可能的 VariableListener 损坏:在完成Action( 137 AIF2 Koller üphy HS134 P3 4 2 2 {天:4 小时:2 -> 天:4 小时:2})。 但是所有的影子变量值仍然相同,所以这是不可能的。 如果您还没有,也许可以使用 FULL_ASSERT 运行,以便更早地失败。 在 org.optaplanner.core.impl.score.director.AbstractScoreDirector.assertShadowVariablesAreNotStale(AbstractScoreDirector.java:475) 在 org.optaplanner.core.impl.solver.scope.DefaultSolverScope.assertShadowVariablesAreNotStale(DefaultSolverScope.java:140) 在 org.optaplanner.core.impl.phase.scope.AbstractPhaseScope.assertShadowVariablesAreNotStale(AbstractPhaseScope.java:171) 在 org.optaplanner.core.impl.phase.AbstractPhase.predictWorkingStepScore(AbstractPhase.java:169) 在 org.optaplanner.core.impl.localsearch.DefaultLocalSearchPhase.doStep(DefaultLocalSearchPhase.java:102) 在 org.optaplanner.core.impl.localsearch.DefaultLocalSearchPhase.solve(DefaultLocalSearchPhase.java:92) 在 org.optaplanner.core.impl.solver.AbstractSolver.runPhases(AbstractSolver.java:87) 在 org.optaplanner.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:173) 在 app.Main.solve(Main.java:35) 在 app.Main.main(Main.java:25)

也许我忽略了一些东西,但我不知道如何解决这个问题。 对于我的日程安排,由于这两节课的交替性质,在某些情况下可能会同时安排两节课。

任何帮助表示赞赏。

编辑:所以我已经解决了这个错误消息,在处理我的 customVariableListener 之后,在 scoreDirector 之前和之后调用的方法似乎存在一致性问题。

现在我想做的是基于阴影变量调用 softConstraint。 这是不允许的吗?这是某种并发问题吗?

线程“main”java.lang.IllegalStateException 中的异常:UndoMove 损坏:beforeMoveScore (0hard/-20soft) 不是工作解决方案的 uncorruptedScore (0hard/-10soft) 的 undoScore (0hard/-10soft)。 1) 启用 EnvironmentMode FULL_ASSERT(如果您还没有)以更快地失败,以防分数损坏。 2)检查moveClass(类util.helpers.optaplanner.moves.ResistantSwapMove)的Move.createUndoMove(...)方法。移动(70 AIF2 Mareczek peme LS103 EM7/EM8 {Day:1 Hour:3} 136 AIF2 Koller phys A0.02 NO_GROUP {Day:5 Hour:5})可能有损坏的 undoMove(Undo(70 AIF2 Mareczek peme LS103 EM7/EM8 {Day:1 Hour:3} 136 AIF2 Koller phys A0.02 NO_GROUP {Day:5 Hour:5}))。 3) 检查您的自定义变量侦听器(如果有的话),以查看分数约束使用的阴影变量,在 beforeMoveScore (0hard/-20soft) 和 undoScore (0hard/-10soft) 之间具有不同的分数权重。

这是导致问题的规则。因为 onlyLesson 在我的实现中只能是 0 或 1,所以这会将 -10 或 0 添加到 scoreDirector。

rule "avoidDaysWithOneLesson"
    when
        $lesson : Lesson($only : onlyLesson)
    then
        scoreHolder.addSoftConstraintMatch(kcontext, -$lesson.getOnlyLesson() * 10);
end

这些是使用的影子变量。原来只有两个,sameDay 和 previousPeriod。 如果我只是更新一个整数(仅限课程),我只是试图摆弄它是否会得到解决。但这并没有解决问题。

@CustomShadowVariable(variableListenerClass = DayLessonVariableListener.class,
        sources = {@PlanningVariableReference(variableName = "period")})
public List<Lesson> getSameDay() {
    return this.sameDay;
}

@CustomShadowVariable(variableListenerRef = @PlanningVariableReference(variableName = "sameDay"))
public Period getPreviousPeriod() {
    return previousPeriod;
}

public void setPreviousPeriod(Period period) {
    this.previousPeriod = period;
}

public void addSameDay(Lesson lesson) {
    if (this.getCourse().getSemester().getShortName().equals(lesson.getCourse().getSemester().getShortName())) {
        this.sameDay.add(lesson);
    }
}

public void setSameDay(List<Lesson> sameDay) {
    this.sameDay = sameDay;
}

@CustomShadowVariable(variableListenerRef = @PlanningVariableReference(variableName = "sameDay"))
public Integer getOnlyLesson() {
    return onlyLesson;
}

我的问题是,是否允许以 shadowVariables 为原因调用约束,或者这会导致移动和 scoreDirector 之间的一致性问题?

任何帮助表示赞赏。

【问题讨论】:

  • 在使用 FULL_ASSERT 运行时是否会得到相同的结果?
  • @GeoffreyDeSmet 是的,这是在 FULL_ASSERT 中运行的,我刚刚更新了帖子,显然解决了我最初的问题。如果你能看一下,我会很高兴。

标签: java optaplanner


【解决方案1】:

现在我想做的是基于阴影调用 softConstraint 变量。这是不允许的吗?

软(和硬)约束可以使用影子变量(这就是影子变量的重点)。

这是某种并发问题吗?

这一切都发生在求解器线程中(或分区搜索中的部分线程,或多线程求解中的移动线程)。无论如何,在一个 ScoreDirector 中,它是单线程的,因此您不必担心并发问题。

【讨论】:

  • 好,至少我将它们用于它们应该被使用的东西,我担心我错过了一些东西。我真的被困住了。关键是,我有那个软约束,它只是找出那天的其他课程列表是否为空。由于某种原因,SwapMove 的 undoMove 被该规则破坏了。我尝试实现自己的 SwapMove,但这与默认 SwapMove 的作用相同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多