5

I use incremental score calculator for my model. After several hours/days of optimization in "full assert" mode Score corruption exception thrown out:

java.lang.IllegalStateException: Score corruption: the workingScore (-86591/-2765/-422/-591) is not the uncorruptedScore (-86591/-2873/-422/-591) after completedAction [...]:
Uncorrupted: Score calculator 3718238 [schedule=Schedule6422-2015-04-16T09:47:36.932[-86591/-2873/-422/-591], prioritiesScore=-422, timelineGapsScore=-2873, requirementGapsScore=-86591, timelineVersionsScore=-591]
Corrupted: Score calculator 3717237 [schedule=Schedule6422-2015-04-16T09:47:36.932[-86591/-2873/-422/-591], prioritiesScore=-422, timelineGapsScore=-2873, requirementGapsScore=-86591, timelineVersionsScore=-591]

That's scores differs in parameter timelineGapsScore. Score instance is created from score calculator object fields prioritiesScore, timelineGapsScore, requirementGapsScore and timelineVersionsScore. Going by log, instances of both scores are equivalent in these fields, but optaplanner engine find differences (-86591/-2765/-422/-591) vs (-86591/-2873/-422/-591). How it's possible?

I suspect references leaks on solution cloning (it's specific implementation and do deep copying), but careful code check doesn't show such errors.

UPD: I forgot to mention: optaplanner runs in daemon mode, model is capable to change facts in real-time. So I have suspicion on race conditions in model. But I don't know how changes injection realized under hood of optaplanner (it isn't enough info in docs).

injecto
  • 829
  • 1
  • 10
  • 23
  • Race conditions are impossible as long as you properly use `addProblemFactChange()` to modify the model in the solver (as that callback gets executed inside the solver thread). – Geoffrey De Smet Apr 20 '15 at 07:56
  • Do note that `addProblemFactChange()` can be tricky indeed. It often requires doing a deeper clone that just a planning clone. For example, to add a new Computer in the CB example, the computerList needs to be cloned (which doesn't happen in a planning clone). – Geoffrey De Smet Apr 20 '15 at 07:58

2 Answers2

2

"When a Solution changes, incremental score calculation (AKA delta based score calculation), will calculate the delta with the previous state to find the new Score, instead of recalculating the entire score on every solution evaluation.

For example, if a single queen A moves from row 1 to 2, it won't bother to check if queen B and C can attack each other, since neither of them changed."

enter image description here

Score corruption happens when the incremental score and the real score (= uncorrupted score) get out of sync.

There can be several causes. It could even be a bug in Drools if you're using Drools score calculation. If you can isolate it and file a jira with a reproducer, then we give usually look at it quickly.

Isolation means (in this order!):

  1. remove all score rules that aren't needed to reproduce it
  2. remove all planning entities that aren't needed to reproduce it
  3. remove all steps that aren't needed to reproduce it. See Termination.stepCountLimit to save the solution just before it goes wrong and then solve starting from that solution
  4. (optional) remove all moves that aren't needed to reproduce it. Turn on Trace log. Thats hard to do without hacking optaplanner, so we usually do this step.
Geoffrey De Smet
  • 26,223
  • 11
  • 73
  • 120
  • I use plain Java score calculator instead of Drools and, unfortunately, this code is high coupled. According to logs, optaplanner clones original solution S0 to S1, then after several steps on S0 which logical lead to initial solution (e.g. swap moves A <=> B, B <=> A, A <=> C, C <=> A) it check score equivalence score(S0) = score(S1). Such eq, of course, holds in my case, but score instances is differs though I use straightforward code for score creation: `return BendableScore.valueOf(new int[]{requirementGapsScore, timelineGapsScore}, new int[]{prioritiesScore, timelineVersionsScore});` – injecto Apr 16 '15 at 16:15
  • With `EasyJavaScoreCalculator`, score corruption is impossible. I presume you're using `IncrementalJavaScoreCalculator`? Good to hear it's not a bug in Drools :) – Geoffrey De Smet Apr 16 '15 at 18:31
  • In the NQueens example implementation, in the sandwished case, B and C are considered to attack each other. But even with the sandswish-does-not-attack requirement, you can do delta calculation to scale better by keeping track of the sandwish states too. – Geoffrey De Smet Apr 17 '15 at 07:58
  • @GeoffreyDeSmet I added details to question. Btw about docs: [this diagram](http://docs.jboss.org/optaplanner/release/6.2.0.Final/optaplanner-docs/html_single/images/Chapter-Score_calculation/incrementalScoreCalculatorSequenceDiagram.png) misleads: calculateScore() calls not after each swap move. Maybe there are similar inaccuracies in description of real-time model changing? :) – injecto Apr 17 '15 at 12:37
  • OptaPlanner is allowed to skip `calculateScore()` if it doesn't need it (or it can predict it), for performance, so I can do several moves after each other before getting around to calling a `calculateScore()`. This holds even further if CompositeMoves are used (for example if you have 2 variables on your entity). – Geoffrey De Smet Apr 17 '15 at 12:46
0

It was the my implementation bug. Be accurate at incremental score calculator implementation (or try to use Drools).

injecto
  • 829
  • 1
  • 10
  • 23