How can you code the teachers' preferences constraint for a Timetable application in Timefold Solver using Spring Boot?

67 Views Asked by At

Let's say that I have an application for generating a university timetable and I have as a constraint a preference of some teachers to teach only in certain periods of a specific day. How can I implement that type of constraint in Timefold? What would be the best practices to add such situations in the scheduling algorithm in Spring Boot/Java?

I saw an example on the Timefold's GitHub Repo for the following scenario that may be similar to what I need: A teacher prefers to teach sequential lessons and dislikes gaps between lessons. And the code for this constraint looks like this:

Constraint teacherTimeEfficiency(ConstraintFactory constraintFactory) {
    // A teacher prefers to teach sequential lessons and dislikes gaps between lessons.
    return constraintFactory
            .forEachUniquePair(Lesson.class,
                    Joiners.equal(Lesson::getTeacher),
                    Joiners.equal((lesson) -> lesson.getTimeslot().getDayOfWeek()))
            .filter((lesson1, lesson2) -> {
                Duration between = Duration.between(lesson1.getTimeslot().getEndTime(),
                        lesson2.getTimeslot().getStartTime());
                return !between.isNegative() && between.compareTo(Duration.ofMinutes(30)) <= 0;
            })
            .reward(HardSoftScore.ONE_SOFT)
            .justifyWith((lesson1, lesson2, score) -> new TeacherTimeEfficiencyJustification(lesson1.getTeacher(), lesson1, lesson2))
            .asConstraint("Teacher time efficiency");
}
1

There are 1 best solutions below

3
Christopher Chianelli On

Do you know the exact Timeslots that are preferred? If so, I would model Teacher like this:

// preferredTimeslots is nullable
public record Teacher(String name, List<Timeslot> preferredTimeslots) {}

And then the constraint would be

Constraint maximizePreferredTimeslotAssignments(ConstraintFactory constraintFactory) {
    return constraintFactory.forEach(Lesson.class)
               .join(Teacher.class, Joiners.equal(Lesson::getTeacher, Function.identity()))
               .filter((lesson, teacher) -> teacher.preferredTimeslots() != null && !teacher.preferredTimeslots().contains(lesson.getTimeslot()))
               .penalize(HardSoftScore.ONE_SOFT)
               .asConstraint("Teacher was assigned unpreferred timeslot");
}

This will penalize ALL non-preferred timeslot assignments for a Teacher (provided the Teacher has preferred timeslots). If a Teacher has no preferred timeslots on Monday, and you do not want to penalize all Monday assignments for that teacher, you would want to change the List<Timeslot> to a Map<DayOfWeek, List<Timeslot>> (with the corresponding change in the filter):

(lesson, teacher) -> teacher.preferredTimeslots().get(lesson.getTimeslot().getDayOfWeek()) != null && !teacher.preferredTimeslots().get(lesson.getTimeslot().getDayOfWeek()).contains(lesson.getTimeslot())