Gather the domain objects in a planning solution
A Timetable
wraps all Timeslot
, Room
, and Lesson
instances of a single dataset.
Furthermore, because it contains all lessons, each with a specific planning variable state,
it is a planning solution and it has a score:
-
If lessons are still unassigned, then it is an uninitialized solution, for example, a solution with the score
-4init/0hard/0soft
. -
If it breaks hard constraints, then it is an infeasible solution, for example, a solution with the score
-2hard/-3soft
. -
If it adheres to all hard constraints, then it is a feasible solution, for example, a solution with the score
0hard/-7soft
.
-
Java
-
Kotlin
-
Python
Create the src/main/java/org/acme/schooltimetabling/domain/Timetable.java
class:
package org.acme.schooltimetabling.domain;
import java.util.List;
import ai.timefold.solver.core.api.domain.solution.PlanningEntityCollectionProperty;
import ai.timefold.solver.core.api.domain.solution.PlanningScore;
import ai.timefold.solver.core.api.domain.solution.PlanningSolution;
import ai.timefold.solver.core.api.domain.solution.ProblemFactCollectionProperty;
import ai.timefold.solver.core.api.domain.valuerange.ValueRangeProvider;
import ai.timefold.solver.core.api.score.buildin.hardsoft.HardSoftScore;
@PlanningSolution
public class Timetable {
@ValueRangeProvider
@ProblemFactCollectionProperty
private List<Timeslot> timeslots;
@ValueRangeProvider
@ProblemFactCollectionProperty
private List<Room> rooms;
@PlanningEntityCollectionProperty
private List<Lesson> lessons;
@PlanningScore
private HardSoftScore score;
public Timetable() {
}
public Timetable(List<Timeslot> timeslots, List<Room> rooms, List<Lesson> lessons) {
this.timeslots = timeslots;
this.rooms = rooms;
this.lessons = lessons;
}
public List<Timeslot> getTimeslots() {
return timeslots;
}
public List<Room> getRooms() {
return rooms;
}
public List<Lesson> getLessons() {
return lessons;
}
public HardSoftScore getScore() {
return score;
}
}
Create the src/main/kotlin/org/acme/schooltimetabling/TimetableApp.kt
class:
package org.acme.schooltimetabling.domain
import ai.timefold.solver.core.api.domain.solution.PlanningEntityCollectionProperty
import ai.timefold.solver.core.api.domain.solution.PlanningScore
import ai.timefold.solver.core.api.domain.solution.PlanningSolution
import ai.timefold.solver.core.api.domain.solution.ProblemFactCollectionProperty
import ai.timefold.solver.core.api.domain.valuerange.ValueRangeProvider
import ai.timefold.solver.core.api.score.buildin.hardsoft.HardSoftScore
import ai.timefold.solver.core.api.solver.SolverStatus
@PlanningSolution
data class Timetable (
@ProblemFactCollectionProperty
@ValueRangeProvider
val timeslots: List<Timeslot>,
@ProblemFactCollectionProperty
@ValueRangeProvider
val rooms: List<Room>,
@PlanningEntityCollectionProperty
val lessons: List<Lesson>,
@PlanningScore
var score: HardSoftScore? = null) {
// No-arg constructor required for Timefold
constructor() : this(emptyList(), emptyList(), emptyList())
}
Create the Solution
class in src/hello_world/domain.py
:
from timefold.solver.domain import (planning_solution, PlanningEntityCollectionProperty,
ProblemFactCollectionProperty, ValueRangeProvider,
PlanningScore)
from timefold.solver.score import HardSoftScore
from dataclasses import dataclass, field
from typing import Annotated
@planning_solution
@dataclass
class Timetable:
id: str
timeslots: Annotated[list[Timeslot],
ProblemFactCollectionProperty,
ValueRangeProvider]
rooms: Annotated[list[Room],
ProblemFactCollectionProperty,
ValueRangeProvider]
lessons: Annotated[list[Lesson],
PlanningEntityCollectionProperty]
score: Annotated[HardSoftScore, PlanningScore] = field(default=None)
This content is for GitHub only.
The Timetable
class has an @PlanningSolution
annotation,
so Timefold Solver knows that this class contains all of the input and output data.
Specifically, these classes are the input of the problem:
-
The
timeslots
field with all time slots-
This is a list of problem facts, because they do not change during solving.
-
-
The
rooms
field with all rooms-
This is a list of problem facts, because they do not change during solving.
-
-
The
lessons
field with all lessons-
This is a list of planning entities, because they change during solving.
-
Of each
Lesson
:-
The values of the
timeslot
androom
fields are typically stillnull
, so unassigned. They are planning variables. -
The other fields, such as
subject
,teacher
andstudentGroup
, are filled in. These fields are problem properties.
-
-
However, this class is also the output of the solution:
-
The
lessons
field for which eachLesson
instance has non-nulltimeslot
androom
fields after solving. -
The
score
field that represents the quality of the output solution, for example,0hard/-5soft
.
The value range providers
The timeslots
field is a value range provider.
It holds the Timeslot
instances which Timefold Solver can pick from to assign to the timeslot
field of Lesson
instances.
The timeslots
field has an @ValueRangeProvider
annotation to connect the @PlanningVariable
with the @ValueRangeProvider
,
by matching the type of the planning variable with the type returned by the value range provider.
Following the same logic, the rooms
field also has an @ValueRangeProvider
annotation.
The problem fact and planning entity properties
Furthermore, Timefold Solver needs to know which Lesson
instances it can change
as well as how to retrieve the Timeslot
and Room
instances used for score calculation
by your TimetableConstraintProvider
.
The timeslots
and rooms
fields have an @ProblemFactCollectionProperty
annotation,
so your TimetableConstraintProvider
can select from those instances.
The lessons
has an @PlanningEntityCollectionProperty
annotation,
so Timefold Solver can change them during solving
and your TimetableConstraintProvider
can select from those too.