Search code examples
optaplanner

How to add a constraint related to the difference between min / max value in optaplanner


I have a Lesson for which I am assigning Teacher & BatchTimeSlot (Which contains => date(LocalDate), startTime(LocalTime) & endTime(LocalTime).

I want to add a constraint where teacher IN and OUT time for any given date does not exceed 10 hrs. For one day a teacher can have multiple lessons.

Logically I am thinking of the below:

Group By Teacher + Date + min(startTime) + max(endTime), and then check difference between min & max

private Constraint maximumTeacherTimeConstraint(ConstraintFactory constraintFactory) {
    return constraintFactory.forEach(Lesson.class)
            .groupBy(lesson -> lesson.getTeacher(),
                    lesson -> lesson.getBatchTimeSlot().getDate(),
                    min(lesson -> lesson.getBatchTimeSlot().getStartTime().toEpochSecond(LocalDate.now(), ZoneOffset.UTC)),
                    max(lesson -> lesson.getBatchTimeSlot().getEndTime().toEpochSecond(LocalDate.now(), ZoneOffset.UTC))
            )
            .filter((teacher, date, minStartTime, maxEndTime) -> maxEndTime - minStartTime > 36000)
            .penalize(HardSoftScore.ONE_SOFT)
            .asConstraint("Time exceeds");
}

The above code gives me a syntax error. It does not even get compiled. If I remove min() & max() then the code gets compiled. What can I try next?


Solution

  • Your approach is correct.

    Try changing

    min(lesson -> ...
    max(lesson -> ...
    

    to

    min((Lesson lesson) -> ...
    max((Lesson lesson) -> ...
    

    to help the compiler get it. We might have put too many overloads on there, so the compiler gets confused easily.

    This seems to compile for me on timefold-quickstarts's school timetabling (not useful this way, just to test the type system):

        return constraintFactory.forEach(Lesson.class)
            .groupBy(lesson -> lesson.getTeacher(),
                lesson -> lesson.getTimeslot().getStartTime(),
                min((Lesson lesson) -> lesson.getTimeslot().getStartTime()),
                max((Lesson lesson) -> lesson.getTimeslot().getEndTime())
            )
    

    And without the explicit Lesson casts, it doesn't.