what causes ConstraintMatchTotal could not add constraintMatch, when issue is tied to a .drl 'or' clause?
Clash Royale CLAN TAG#URR8PPP
what causes ConstraintMatchTotal could not add constraintMatch, when issue is tied to a .drl 'or' clause?
In extending code from OptaPlanner nurse rostering sample code. What causes the "constraintMatchTotal could not add constraintMatch" (Illegal state?) error to be thrown, that would be related to the parsing of a .drl rule with an 'or' clause, please? It is occurring immediately at import of data into .drl-based ruleset... but does NOT error if either of the two 'or' clauses is commented out. I believe that as they individually are acceptable, the system should handle them in the 'or' setup.
The rule is below, followed by the error, and the domain object used in the 'or' clause. I confirmed that:
preferredSequenceStart == true,
.drl rule:
rule "Highlight irregular shifts"
when
EmployeeWorkSameShiftTypeSequence(
employee != null,
$firstDayIndex : firstDayIndex,
$lastDayIndex : lastDayIndex,
$employee : employee,
$dayLength : dayLength)
(
BoundaryDate(
dayIndex == $firstDayIndex,
preferredSequenceStart == false // does not start on a boundary start date
)
or // or
BoundaryDate(
dayIndex == $firstDayIndex,
$dayLength != preferredCoveringLength // is incorrect length for exactly one block
)
)
StaffRosterParametrization($lastDayIndex >= planningWindowStartDayIndex) // ignore if assignment is in (fixed) prior data
// non-functional identification drives desired indictment display on ShiftAssignment planning objects
ShiftAssignment(employee == $employee, shiftDateDayIndex >= $firstDayIndex, shiftDateDayIndex <= $lastDayIndex)
then
scoreHolder.addSoftConstraintMatch(kcontext, -1);
end
Exception executing consequence for rule "Highlight irregular shifts" in westgranite.staffrostering.solver: java.lang.IllegalStateException: The constraintMatchTotal (westgranite.staffrostering.solver/Highlight irregular shifts=0hard/-274soft) could not add constraintMatch (westgranite.staffrostering.solver/Highlight irregular shifts/[2020-01-02/D/6, 2018-12-25 - 2020-01-06, 2020-01-02, ...
[continues on with a list of constraint matches]
Exception executing consequence for rule "Highlight irregular shifts" in westgranite.staffrostering.solver: java.lang.IllegalStateException: The constraintMatchTotal (westgranite.staffrostering.solver/Highlight irregular shifts=0hard/-274soft) could not add constraintMatch (westgranite.staffrostering.solver/Highlight irregular shifts/[2020-01-02/D/6, 2018-12-25 - 2020-01-06, 2020-01-02, ...
The BoundaryData.java is below, so the methods being called from the rule are visible:
package westgranite.staffrostering.domain;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import westgranite.common.domain.AbstractPersistable;
@XStreamAlias("BoundaryDate")
public class BoundaryDate extends AbstractPersistable
/**
*
*/
private static final long serialVersionUID = -7393276689810490427L;
private static final DateTimeFormatter LABEL_FORMATTER = DateTimeFormatter.ofPattern("E d MMM");
private int dayIndex;
private LocalDate date;
private boolean preferredSequenceStart; // true means "this date is a preferred start to assignment sequences"
private boolean preferredSequenceEnd; // true means "this date is a preferred end for assignment sequences"
private int nextPreferredStartDayIndex; // MAX_VALUE means "none"; if preferredSequenceStart is true, then this ref is still to the FUTURE next pref start date
private int prevPreferredStartDayIndex; // MIN_VALUE means "none"; if preferredSequenceStart is true, then this ref is still to the PREVIOUS next pref start date
// magic value that is beyond reasonable dayIndex range and still allows delta of indices to be an Integer
public static final int noNextPreferredDayIndex = Integer.MAX_VALUE/3;
public static final int noPrevPreferredDayIndex = Integer.MIN_VALUE/3;
public int getDayIndex()
return dayIndex;
public void setDayIndex(int dayIndex)
this.dayIndex = dayIndex;
public LocalDate getDate()
return date;
public void setDate(LocalDate date)
this.date = date;
public boolean isPreferredSequenceStart()
return preferredSequenceStart;
public void setPreferredSequenceStart(boolean preferredSequenceStart)
this.preferredSequenceStart = preferredSequenceStart;
public boolean isPreferredSequenceEnd()
return preferredSequenceEnd;
public void setPreferredSequenceEnd(boolean preferredSequenceEnd)
this.preferredSequenceEnd = preferredSequenceEnd;
public int getNextPreferredStartDayIndex()
return nextPreferredStartDayIndex;
public void setNextPreferredStartDayIndex(int nextPreferredStartDayIndex)
this.nextPreferredStartDayIndex = nextPreferredStartDayIndex;
public int getPrevPreferredStartDayIndex()
return prevPreferredStartDayIndex;
public void setPrevPreferredStartDayIndex(int prevPreferredStartDayIndex)
this.prevPreferredStartDayIndex = prevPreferredStartDayIndex;
// ===================== COMPLEX METHODS ===============================
public int getCurrOrPrevPreferredStartDayIndex()
return (isPreferredSequenceStart() ? dayIndex : prevPreferredStartDayIndex);
public int getCurrOrNextPreferredStartDayIndex()
return (isPreferredSequenceStart() ? dayIndex : nextPreferredStartDayIndex);
public int getCurrOrPrevPreferredEndDayIndex()
return (isPreferredSequenceEnd() ? dayIndex : (isPreferredSequenceStart() ? dayIndex-1 : prevPreferredStartDayIndex-1));
public int getCurrOrNextPreferredEndDayIndex()
return (isPreferredSequenceEnd() ? dayIndex : nextPreferredStartDayIndex-1);
public boolean isNoNextPreferred()
return getNextPreferredStartDayIndex() == noNextPreferredDayIndex;
public boolean isNoPrevPreferred()
return getPrevPreferredStartDayIndex() == noPrevPreferredDayIndex;
/**
* @return if this is a preferred start date, then the sequence length that will fill from this date through the next end date; otherwise the days filling the past preferred start date through next end date
*/
public int getPreferredCoveringLength()
if (isPreferredSequenceStart())
return nextPreferredStartDayIndex - dayIndex;
return nextPreferredStartDayIndex - prevPreferredStartDayIndex;
/**
* @return if this is a preferred start boundary, then "today", else day of most recent start boundary
*/
public DayOfWeek getPreferredStartDayOfWeek()
if (isPreferredSequenceStart())
return getDayOfWeek();
if (isNoPrevPreferred())
throw new IllegalStateException("No prev preferred day of week available for " + toString());
return date.minusDays(dayIndex - getPrevPreferredStartDayIndex()).getDayOfWeek();
public DayOfWeek getPreferredEndDayOfWeek()
if (isPreferredSequenceEnd())
return getDayOfWeek();
if (isNoNextPreferred())
throw new IllegalStateException("No next preferred day of week available for " + toString());
return date.plusDays((getNextPreferredStartDayIndex()-1) - dayIndex).getDayOfWeek();
public DayOfWeek getDayOfWeek()
return date.getDayOfWeek();
public int getMostRecentDayIndexOf(DayOfWeek targetDayOfWeek)
return dayIndex - getBackwardDaysToReach(targetDayOfWeek);
public int getUpcomingDayIndexOf(DayOfWeek targetDayOfWeek)
return dayIndex + getForwardDaysToReach(targetDayOfWeek);
public LocalDate getMostRecentDateOf(DayOfWeek targetDayOfWeek)
return date.minusDays(getBackwardDaysToReach(targetDayOfWeek));
public LocalDate getUpcomingDateOf(DayOfWeek targetDayOfWeek)
return date.plusDays(getForwardDaysToReach(targetDayOfWeek));
public int getForwardDaysToReach(DayOfWeek targetDayOfWeek)
return getForwardDaysToReach(this.getDayOfWeek(), targetDayOfWeek);
public static int getForwardDaysToReach(DayOfWeek startDayOfWeek, DayOfWeek targetDayOfWeek)
if (startDayOfWeek == targetDayOfWeek)
return 0;
int forwardDayCount = 1;
while (startDayOfWeek.plus(forwardDayCount) != targetDayOfWeek)
forwardDayCount++;
if (forwardDayCount > 10)
throw new IllegalStateException("counting forward in days from " + startDayOfWeek + " never found target day of week: " + targetDayOfWeek);
return forwardDayCount;
public int getBackwardDaysToReach(DayOfWeek targetDayOfWeek)
return getBackwardDaysToReach(this.getDayOfWeek(), targetDayOfWeek);
public static int getBackwardDaysToReach(DayOfWeek startDayOfWeek, DayOfWeek targetDayOfWeek)
if (startDayOfWeek == targetDayOfWeek)
return 0;
int backwardDayCount = 1;
while (startDayOfWeek.minus(backwardDayCount) != targetDayOfWeek)
backwardDayCount++;
if (backwardDayCount > 10)
throw new IllegalStateException("counting backward in days from " + startDayOfWeek + " never found target day of week: " + targetDayOfWeek);
return backwardDayCount;
public String getLabel()
return date.format(LABEL_FORMATTER);
@Override
public String toString()
return date.format(DateTimeFormatter.ISO_DATE);
Activation.getObjectDeep()
I've created a jira to track status but without a reproducer this probably won't get prioritized.
– Geoffrey De Smet
Aug 14 at 9:09
Thanks @GeoffreyDeSmet. I don't have the test infrastructure from the default optaplanner distribution (eclipse) projects package running properly yet. It will be a couple weeks before I can create a simple reproducer (after I get through the upcoming scheduling crunch using the workaround). I will hold this query open and then add to the jira once I have the simplified reproducer.
– timeNtrack
Aug 21 at 6:31
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
This might maybe be a bug in drools method
Activation.getObjectDeep()
that is used here in OptaPlanner. To prove that theory, isolate it into a small reproducer and create a jira.– Geoffrey De Smet
Aug 13 at 6:55