Hi,
I've been using Drools Planner with success for a while for our Conversation
planning tool. It's used by schools to plan conversations between teachers
and parents of schoolgoing children.
Recently I checked on the new developments on Drools (the current version
we're using in production is 5.4.0.FINAL) and started to upgrade to
OptaPlanner 6.0.0.Beta5.
Everything looked fine, and seeing how it's now easier to check which
constraints are broken in a given solution, I decided to have a look at that
as well.
And that's where I noticed that we actually had an issue with score
corruption in one of our rules...
The goal of the rule in case is to minimize the amount of timeslots between
the first and last Conversation of a family (they can have conversations for
multiple children.)
The rule uses custom helper objects, like in the NurseRostering example,
which are added with insertLogical.
Conversation is the planning entity, which has the planning variable:
timeslot. FamilyStart and FamilyEnd are custom helper objects to register
the timeslot of the first and last conversation for a family.
The relevant rules are as follows:
rule "familyStart"
salience 1
when
Conversation(
family != null, $family: family,
timeslot != null, $timeslot: timeslot);
not Conversation(
family == $family,
timeslot < $timeslot);
then
insertLogical(new FamilyStart($family, $timeslot));
end
rule "familyEnd"
salience 1
when
Conversation(
family != null, $family: family,
timeslot != null, $timeslot: timeslot);
not Conversation(
family == $family,
timeslot > $timeslot);
then
insertLogical(new FamilyEnd($family, $timeslot));
end
rule "familyCompact"
when
$start: FamilyStart(
family != null, $family: family,
timeslot != null, $timeslotBegin: timeslot);
$end: FamilyEnd(
family == $family,
(timeslot.id - $timeslotBegin.id > family.minimumTimeslotsRequired),
timeslot != null, $timeslotEnd: timeslot);
then
scoreHolder.addSoftConstraintMatch(kcontext, -(($timeslotEnd.getId() -
$timeslotBegin.getId()) - $family.getMinimumTimeslotsRequired()));
end
When running one of my example schedules in FULL_ASSERT mode, the following
error appears during the LocalSearch phase:
Exception in thread "main" java.lang.IllegalStateException: Score
corruption: the workingScore (-2hard/-119soft) is not the uncorruptedScore
(-2hard/-118soft) after completedAction (Teacher 3 family 11 timeslot 2 =>
12):
The corrupted scoreDirector has 1 ConstraintMatch(s) which are in excess
(and should not be there):
defaultpkg/familyCompact/level1/[FamilyEnd Family 11, Timeslot 12,
FamilyStart Family 11, Timeslot 7]=-1
The corrupted scoreDirector has no ConstraintMatch(s) which are missing.
Check your score constraints.
I use a custom ChangeMove and SwapMove implementation. I've also tried using
the new generic Moves, these have the same problem.
It seems like when a move happens that should update an inserted FamilyStart
or FamilyEnd because of a change in timeslot, this change isn't properly
reflected in the scoreDirector.
Do you have any idea what could cause this corruption? The moves do call
beforeVariableChanged and afterVariableChanged on the scoreDirector, and the
FamilyStart and FamilyEnd classes implement an equals and hashcode based on
the Family and Timeslot fields.
(Actually, I've also tried using the default equals and hashcode of Object
for these 2 classes, which causes corruption much faster and with more
ConstraintOccurrences.)
Thanks for any insights,
Pieter
--
View this message in context:
http://drools.46999.n3.nabble.com/OptaPlanner-score-corruption-when-using...
Sent from the Drools: User forum mailing list archive at
Nabble.com.