То, что я на самом деле хотел сделать, - это иметь правило Drools следующим образом:
rule "globalRequiredPredecessorAfterMe"
when
$rpAll: Set(size>1) from accumulate (
Customer(vehicle!= null, vehicle.vehicleTyp != VehicleTyp.DUMMY, $rpAfterMe: requiredPredecessorsAfterMe);
collectSet($rpAfterMe)
)
then
scoreHolder.addMediumConstraintMatch(kcontext, - $rpAll.size()-1);
end
К сожалению, на втором ходу в CH drools награждает меня
Exception in thread "main" java.lang.RuntimeException: java.lang.NullPointerException
at org.drools.core.rule.SingleAccumulate.reverse(SingleAccumulate.java:124)
...
Это особенность?
Я проглотил свою гордость и написал фантастически сложный Listener
, который, как предполагается, соответствовал тому, что делал вышеупомянутое правило Друлов.С assertionScoreDirectorFactory
(EasyScoreCalculator
) я получаю искаженную оценку.При ближайшем рассмотрении я получаю искажение в одном из двух случаев:
- У моего планового объекта
previousXXX
точно установлено то же значение - Движение
Undo
done
Чтобы исследовать, я установил решение для состояния непосредственно перед тем, как происходит искажение счета.Я использую CreateChangeMove
s из класса Optaplanner 7.4.1.Final SolutionBusiness
.(Кстати, было бы очень полезно, если бы можно было также установить MoveCountLimit
в SolverConfig.)
Поэтому я создаю scoreDirector
следующим образом
SolverFactory<MySolution> solverFactory = SolverFactory.createFromXmlResource(
SolverConfigXML);
solver = solverFactory.buildSolver();
ScoreDirectorFactory<MySolution> scoreDirectorFactory = solver.getScoreDirectorFactory();
scoreDirector = scoreDirectorFactory.buildScoreDirector();
scoreDirector.setWorkingSolution(unsolvedSolution);
вJUnit 5
тест, а затем выполните несколько ChangeMoves
, чтобы имитировать решение непосредственно перед тем, как произойдет нарушение.Однако, когда я выполняю оскорбительное движение, как в пуле 1 выше,
// customer SUK0002030's previousVehicleOrCustomer is DUMMY_3
cm = createChangeMove(nameToCustomerMap.get("SUK0002030"), "previousVehicleOrCustomer", nameToVehicleMap.get("DUMMY_3"));
cm.doMove(scoreDirector);
я получаю
java.lang.IllegalStateException: The entity (SUK0002030) has a variable (previousVehicleOrCustomer) with value (DUMMY_3) which has a sourceVariableName variable (nextCustomer) with a value (null) which is not that entity.
Verify the consistency of your input problem for that sourceVariableName variable.
at org.optaplanner.core.impl.domain.variable.inverserelation.SingletonInverseVariableListener.retract(SingletonInverseVariableListener.java:87)
...
Когда я устанавливаю previousVehicleOrCustomer
в null
, а затем возвращаюсьна DUMMY_3
, все в порядке и не происходит искажения счета.
Аналогично, при попытке создать (оскорбляющее) UndoMove
предыдущего Move cm
, пуля 2 выше, как и
cm.createUndoMove(scoreDirector).doMove(scoreDirector)
Я получаю то же сообщение:
java.lang.IllegalStateException: The entity (SUK0002014) has a variable (previousVehicleOrCustomer) with value (Vehicle_0) which has a sourceVariableName variable (nextCustomer) with a value (null) which is not that entity.
Verify the consistency of your input problem for that sourceVariableName variable.
at org.optaplanner.core.impl.domain.variable.inverserelation.SingletonInverseVariableListener.retract(SingletonInverseVariableListener.java:87)
...
Конечно, если я вручную создаю UndoMove
(просто установив previousVehicleOrCustomer
обратно в null
), все в порядке.
Эти шаги действительно должны быть возможны, и мне действительно любопытно выяснить, в чем дело.