VariableListener corruption

依然范特西╮ 提交于 2019-12-31 03:59:03

问题


I was playing around with the custom shadow variables and custom variable listeners. I found that the "Exam timetabling" example uses custom shadow variables to update "Following exams" when the "Leading Exam" changes. So what i did to assert asking in this question was true Custom VariableListener updating more than one shadow variables i added a second shadow variable in the Following Exam class which references the variable "period" , and then in the variable listener of the LeadingExam planning entity(PeriodUpdatingVariableListener) i update both the variables.

Here is the code for the "FollowingExam" class

@PlanningEntity
@XStreamAlias("FollowingExam")
public class FollowingExam extends Exam {

protected LeadingExam leadingExam;

// Shadow variables
protected Period period;
protected Integer var;

@CustomShadowVariable(variableListenerClass = PeriodUpdatingVariableListener.class, sources = {
        @CustomShadowVariable.Source(entityClass = LeadingExam.class, variableName = "period") })
public Period getPeriod() {
    return period;
}

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

@CustomShadowVariable(variableListenerRef = @PlanningVariableReference(variableName = "period"))
public Integer getVar() {
    return var;
}

public void setVar(Integer var) {
    this.var = var;
}

public LeadingExam getLeadingExam() {
    return leadingExam;
}

public void setLeadingExam(LeadingExam leadingExam) {
    this.leadingExam = leadingExam;
}

}

And in the "PeriodUpdatingVariableListener" i have the next code

public class PeriodUpdatingVariableListener implements VariableListener<LeadingExam> {

public void beforeEntityAdded(ScoreDirector scoreDirector, LeadingExam leadingExam) {
    // Do nothing
}

public void afterEntityAdded(ScoreDirector scoreDirector, LeadingExam leadingExam) {
    updatePeriod(scoreDirector, leadingExam);
}

public void beforeVariableChanged(ScoreDirector scoreDirector, LeadingExam leadingExam) {
    // Do nothing
}

public void afterVariableChanged(ScoreDirector scoreDirector, LeadingExam leadingExam) {
    updatePeriod(scoreDirector, leadingExam);
}

public void beforeEntityRemoved(ScoreDirector scoreDirector, LeadingExam leadingExam) {
    // Do nothing
}

public void afterEntityRemoved(ScoreDirector scoreDirector, LeadingExam leadingExam) {
    // Do nothing
}

protected void updatePeriod(ScoreDirector scoreDirector, LeadingExam leadingExam) {
    Period period = leadingExam.getPeriod();
    for (FollowingExam followingExam : leadingExam.getFollowingExamList()) {
        scoreDirector.beforeVariableChanged(followingExam, "period");
        followingExam.setPeriod(period);
        scoreDirector.afterVariableChanged(followingExam, "period");

        //additional lines of code to update the "var" variable
        Integer var = followingExam.getVar();
        if(var == null){
            var = new Integer(1);
        }
        else var++;
        scoreDirector.beforeVariableChanged(followingExam, "var");
        followingExam.setVar(var);
        scoreDirector.afterVariableChanged(followingExam, "var");

    }
}

}

It also seem like that there is no problem for OptaPlanner registering that this is another shadow variable because i get this message when i run the application

2016-04-11 15:26:15,309 [AWT-EventQueue-0] TRACE     Model annotations parsed for Solution Examination:
2016-04-11 15:26:15,309 [AWT-EventQueue-0] TRACE         Entity Exam:
2016-04-11 15:26:15,309 [AWT-EventQueue-0] TRACE             Variable room (genuine)
2016-04-11 15:26:15,309 [AWT-EventQueue-0] TRACE         Entity LeadingExam:
2016-04-11 15:26:15,309 [AWT-EventQueue-0] TRACE             Variable period (genuine)
2016-04-11 15:26:15,309 [AWT-EventQueue-0] TRACE         Entity FollowingExam:
2016-04-11 15:26:15,309 [AWT-EventQueue-0] TRACE             Variable period (shadow)
2016-04-11 15:26:15,309 [AWT-EventQueue-0] TRACE             Variable var (shadow)

The error that i get is the next error , this error only appears when i change the environment mode to FULL_ASSERT ,but when it is left to the default REPRODUCIBLE it runs without any errors.

Caused by: java.lang.IllegalStateException: VariableListener corruption: the entity (426)'s shadow variable (FollowingExam.var)'s corrupted value (null) changed to uncorrupted value (1) after all VariableListeners were triggered without changes to the genuine variables.
Probably the VariableListener class for that shadow variable (FollowingExam.var) forgot to update it when one of its sources changed after completedAction (Initial score calculated).
at org.optaplanner.core.impl.score.director.AbstractScoreDirector.assertShadowVariablesAreNotStale(AbstractScoreDirector.java:349)
at org.optaplanner.core.impl.solver.recaller.BestSolutionRecaller.solvingStarted(BestSolutionRecaller.java:84)
at org.optaplanner.core.impl.solver.DefaultSolver.solvingStarted(DefaultSolver.java:196)
at org.optaplanner.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:175)
at org.optaplanner.examples.common.business.SolutionBusiness.solve(SolutionBusiness.java:307)
at org.optaplanner.examples.common.swingui.SolverAndPersistenceFrame$SolveWorker.doInBackground(SolverAndPersistenceFrame.java:287)
at org.optaplanner.examples.common.swingui.SolverAndPersistenceFrame$SolveWorker.doInBackground(SolverAndPersistenceFrame.java:1)
at javax.swing.SwingWorker$1.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at javax.swing.SwingWorker.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)

回答1:


This code in a variable listener is bad:

    Integer var = followingExam.getVar();
    ...
    var++;
    ...
    followingExam.setVar(var);
    ...

If its called multiple times, the var changes, even if the genuine var hasn't changed. If that var is used in score rules, the solution's score isn't always the same for the same solution.

A shadow variable is the caching of the result of a formula based on at least 1 genuine variable (directly or indirectly). For example shadow C = genuine A + problem property B. So if B is 10 and ranges A from 1 to 5, then C will be 11 if A is 1, 12 if A is 2, etc.



来源:https://stackoverflow.com/questions/36550460/variablelistener-corruption

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!