I'm getting a score corruption with quite a simple example:
java.lang.IllegalStateException: Score corruption (-4850000soft): the workingScore (-19init/0hard/0medium/-4850000soft) is not the uncorruptedScore (-19init/0hard/0medium/0soft) after completedAction (Standstill 1 {null -> Vehicle 1[0]}):
Score corruption analysis:
The corrupted scoreDirector has 1 ConstraintMatch(s) which are in excess (and should not be there):
optimiser.allocation.solver/Project minimum weight delivered/[Project 1, 150000]=0hard/0medium/-4850000soft
The corrupted scoreDirector has no ConstraintMatch(s) which are missing.
The uncorrupted score is incorrect, as the soft score should be much closer to 150000 for the first step of construction.
My model is almost the same as the example VRP, but with projects added as a planning fact. Each standstill has a project associated with it.
I originally built the constraint stream around the standstills, which was simpler, but it was missing the projects with no standstills, so the new constraint stream is as follows:
protected Constraint projectMinimumWeightDelivered(ConstraintFactory constraintFactory) {
// A project must deliver at least the minimum weight
return constraintFactory
.forEach(Project.class)
.filter(project -> project.getMinimumWeightToDeliver() != null)
.join(Standstill.class, equal(project -> project, Standstill::getProject))
// Only count standstills that have a real vehicle
.filter((project, standstill) -> standstill.isPickup() && standstill.getVehicle() != null && standstill.getVehicle().isReal())
// group by project, sum weight of standstill
.groupBy((project, standstill) -> project, sum((project, standstill) -> standstill.getWeight()))
.filter(Project::hasNotDeliveredMinimumWeight)
.penalizeConfigurable(Project::amountUnderMinimumWeight)
.asConstraint("Project minimum weight delivered");
}
The constraint stream seems to give the correct values when all the standstills are initialized, but running in FULL_ASSERT mode revealed this error. I have disabled all other constraints to replicate this, along with a couple of custom shadow variable listener classes I was suspicious of.
Is there a clear issue with my constraint stream implementation based on how optaplanner calculates the incremental scores?