0

Good morning.

I'm working on Optaplanner and trying to implement a rule that maximizes consecutive shifts assigned to the same employee. Let's take this simple case for example:

starting example of the problem set

I have 10 hour long shifts and only two employees with a part time contract (therefore they can work a maximum of 4 hours a day). So, what I expect to see from the result (regardless of the order in which the two shifts in excess are left empty) is something like that:

expected output

I'm still running into some problems on the correct implementation of the rule, which I post below:

    Constraint maximumConsecutiveShifts(ConstraintFactory constraintFactory) {
        return constraintFactory.forEach(Shift.class)
                .join(Shift.class,
                        equal(Shift::getEmployee),
                        lessThan(Shift::getEndDateTime, Shift::getStartDateTime), equal(Shift::getDay))
                .ifNotExists(Shift.class,
                        equal((s1, s2) -> s1.getEmployee(), Shift::getEmployee),
                        lessThanOrEqual((s1, s2) -> s1.getEndDateTime(), Shift::getStartDateTime),
                        greaterThanOrEqual((s1, s2) -> s2.getStartDateTime(), Shift::getEndDateTime))
                .filter((s1, s2) -> !Objects.equals(s1, s2))
                .penalizeConfigurableLong(CONSTRAINT_MAXIMUM_CONSECUTIVE_SHIFTS_SAME_DAY, (s1, s2) -> {
                    long breakLength = s1.getEndDateTime().until(s2.getStartDateTime(), ChronoUnit.MINUTES);
                    return (24 * 60) - breakLength;
                });
    }

Could you please help me on how to proceed in an optimal way to understand where I'm wrong? Any kind of input would be of great help to me

Many thanks

Geoffrey De Smet
  • 26,223
  • 11
  • 73
  • 120
byse
  • 1
  • I've familiarized myself with the EasyScoreCalculator. Java streams is too complicated for me :( If you go with EasyScoreCalculator I can share how I'd do it. – MattWolc24 Jul 06 '23 at 16:17
  • Thanks a lot for the answer. One question: have you noticed big performance differences compared to java streams? – byse Jul 10 '23 at 15:38
  • I haven't scaled up the data it's working with to the full scale of what the application will need to handle, so I'm not sure. Fingers crossed though! I'll let you know. – MattWolc24 Jul 10 '23 at 17:34
  • I've scaled it up and it's unacceptably slow. Guess it's time to learn Java Streams – MattWolc24 Jul 10 '23 at 22:08

1 Answers1

0

The Timefold examples's nurse rostering has this constraint (and similar ones):

// Min/Max consecutive working days
Constraint consecutiveWorkingDays(ConstraintFactory constraintFactory) {
    return constraintFactory.forEach(MinMaxContractLine.class)
            .filter(minMaxContractLine -> minMaxContractLine
                    .getContractLineType() == ContractLineType.CONSECUTIVE_WORKING_DAYS &&
                    minMaxContractLine.isEnabled())
            .join(ShiftAssignment.class,
                    Joiners.equal(ContractLine::getContract, ShiftAssignment::getContract))
            .groupBy((contract, shift) -> shift.getEmployee(),
                    (contract, shift) -> contract,
                    ExperimentalConstraintCollectors.consecutive((contract, shift) -> shift.getShiftDate(),
                            ShiftDate::getDayIndex))
            .flattenLast(ConsecutiveInfo::getConsecutiveSequences)
            .map((employee, contract, shiftList) -> employee,
                    (employee, contract, shiftList) -> contract,
                    (employee, contract, shiftList) -> contract.getViolationAmount(shiftList.getLength()))
            .filter((contract, employee, violationAmount) -> violationAmount != 0)
            .penalize(HardSoftScore.ONE_SOFT, (contract, employee, violationAmount) -> violationAmount)
            .indictWith((contract, employee, violationAmount) -> Arrays.asList(employee, contract))
            .asConstraint("consecutiveWorkingDays");
}
Geoffrey De Smet
  • 26,223
  • 11
  • 73
  • 120
  • thank you very much I will take a look to understand in my reasoning what I am wrong – byse Jul 10 '23 at 15:36