Reputation: 311
I started to implemented a school timetable scheduling application using OptaPlanner.
Similar to the lesson scheduling example in the documentation, I'd like to optimize a school schedule including constraints such as "A teacher, who works/has less than N hours (per week) should ideally have one (or two, depending on N) days off".
To phrase it differently: Lessons of teachers should not be distributed across the entire week, but their quantity should rather be maximized per day.
Lesson is my only planning entity, the teacher is a problem fact (I guess). Maybe I need to model it in a different way to access the overall distribution of a teacher's timeslots (more specifically: of all the lesson's timeslots belonging to a teacher).
How can I combine the usual constraints, i.e., a teacher can't do two different lessons at the same time etc., defined using the ConstraintFactory creating ConstraintStreams (for Lessons) with a more global property such as the "lesson compactness" for teachers (full days better than distributed over the entire week) who work part time? I can't imagine how to calculate that score by just having access to a constraint stream of lessons.
Upvotes: 0
Views: 193
Reputation: 2013
The normal way to use global information in a constraint is to create a singleton problem fact that stores global information, and use that in your constraint.
For your example, create a ScheduleGlobalProperties
with a partTimeTeacherSet
field:
public class ScheduleGlobalProperties {
Set<String> partTimeTeacherSet;
// other global properties, constructors, getters...
}
Add it as a @ProblemFactProperty
to your @PlanningSolution
class:
@PlanningSolution
public class Schedule {
@ProblemFactProperty
ScheduleGlobalProperties scheduleGlobalProperties;
// Other fields, constructors, methods...
}
Then in your constraint, you join on the global properties problem fact to access the global properties:
Constraint lessonCompactness(ConstraintFactory constraintFactory) {
return constraintFactory.forEach(Lesson.class)
.groupBy(Lesson::getTeacher,
ConstraintCollectors.countDistinct(
lesson -> lesson.getTimeslot().getDay()
))
.join(ScheduleGlobalProperties.class)
.filter((teacher, dayCount, globalProperties) -> globalProperties.getPartTimeTeacherSet().contains(teacher))
.penalize(HardSoftScore.ONE_SOFT, (teacher, dayCount, globalProperties) -> dayCount)
.asConstraint("Lesson Compactness");
}
I don't see how the constraint you specified needed global information though.
An example that uses global information is nurse rostering; NurseRosterParametrization
is the singleton global properties class (with first and last shift date fields).
In the nurse rostering example, the global properties are used in the consecutiveFreeDays
constraint to determine how many free days an employee have before their first shift and after their last shift.
See the nurse rostering source code for the full example.
Upvotes: 2