@GeoffreyDeSmet @petrovicky@jvm.social
by Geoffrey De Smet
and Lukáš Petrovický
Imagine if we optimize
for efficiency gains without sacrifices
Expected: -1% driving time
Result: -25% driving time
⇒ -10 million kg CO² emission per year
⇒ -100 million $ cost per year
March 2023
May 2023
No hard constraints broken.
Optimal is hard.
August 2023
Generative AI + Solver AI
work together
public long calculateScore(TimeTable timeTable) {
long hardScore = 0;
for (Lesson lesson1 : timeTable.lessons) {
for (Lesson lesson2 : timeTable.lessons) {
if (lesson1.room == lesson2.room
&& lesson1.timeslot == lesson2.timeslot) {
hardScore--;
}
if (lesson1.teacher == lesson2.teacher
&& lesson1.timeslot == lesson2.timeslot) {
hardScore--;
}
if (lesson1.studentGroup == lesson2.studentGroup
&& lesson1.timeslot == lesson2.timeslot) {
hardScore--;
}
}
}
return hardScore;
}
for (Lesson lesson : timeTable.lessons) {
long bestScore = Long.MIN_VALUE;
Room bestRoom = null;
Timeslot bestTimeslot = null;
for (Timeslot timeslot : timeTable.timeslots) {
lesson.setTimeslot(timeslot);
for (Room room : timeTable.rooms) {
lesson.setRoom(room);
long score = calculateScore(timeTable);
if (score > bestScore) {
bestScore = score;
bestRoom = room;
bestTimeslot = timeslot;
}
}
}
lesson.setTimeslot(bestTimeslot);
lesson.setRoom(bestRoom);
}
Greedy algorithm
Greedy | ||
---|---|---|
Fast | ||
Scalable | ||
Far from optimal |
long bestScore = Long.MIN_VALUE;
for (Timeslot timeslotLesson1 : timeslots) {
lesson1.setTimeslot(timeslotLesson1);
for (Room roomLesson1 : rooms) {
lesson1.setRoom(roomLesson1);
for (Timeslot timeslotLesson2 : timeslots) {
lesson2.setTimeslot(timeslotLesson2);
for (Room roomLesson2 : rooms) {
lesson2.setRoom(roomLesson2);
...
for (Timeslot timeslotLessonN : timeslots) {
lessonN.setTimeslot(timeslotLessonN);
for (Room roomLessonN : rooms) {
lessonN.setRoom(roomLessonN);
int score = calculateScore(timeTable);
if (score > bestScore)
...
Brute Force
Search space for n lessons: nn
Search space for 400 lessons: 101040
Greedy | Brute Force | |
---|---|---|
Fast | Slow | |
Scalable | Not scalable | |
Far from optimal | Optimal |
+ advanced subsystem algorithms
public HardSoftScore calculateScore(TimeTable timeTable) {
long hardScore = 0;
for (Lesson lesson1 : timeTable.lessons) {
for (Lesson lesson2 : timeTable.lessons) {
if (lesson1.room == lesson2.room
&& lesson1.timeslot == lesson2.timeslot) {
hardScore--;
}
if (lesson1.teacher == lesson2.teacher
&& lesson1.timeslot == lesson2.timeslot) {
hardScore--;
}
if (lesson1.studentGroup == lesson2.studentGroup
&& lesson1.timeslot == lesson2.timeslot) {
hardScore--;
}
}
}
return HardSoftScore.of(hardScore, 0);
}
Metaheuristics
Greedy | Brute Force | Metaheuristics |
---|---|---|
Fast | Slow | Fast |
Scalable | Not scalable | Scalable |
Far from optimal | Optimal | (Near) Optimal |
Humans | Greedy | Brute Force | Meta heuristics |
---|---|---|---|
Slow | Fast | Slow | Fast |
Scalable | Scalable | Not scalable | Scalable |
Far from optimal | Far from optimal | Optimal | (Near) Optimal |
Humans | Generative AI | Greedy | Brute Force | Meta heuristics |
---|---|---|---|---|
Slow | Fast | Fast | Slow | Fast |
Scalable | Scalable? | Scalable | Not scalable | Scalable |
Far from optimal | Far from optimal | Far from optimal | Optimal | (Near) Optimal |
after the break
Employee scheduling
Get incremental score calculation
without writing incremental score calculation.
Constraint Streams
Winston Churchill
(Prime Minister UK 1940-1945)
Mike Tyson
(Boxer, heavyweight champion 1987-1990)
"We can not automate planning
because the plans will change."
⇒
"We need to automate (re)planning
because the plans will change."
But how?
Each planning stage feeds into the next.
Especially for trains, airplanes, etc
"Any organization that designs a (software) system
will produce a design whose structure
is a copy of the organization's communication structure."
⇒
Planning problems solved by different groups
should use different Solver instances
(during the first year in production).
@Path("/trainWagon")
public class TrainWagonDeciderResource {
@Inject
SolverManager<TrainWagonSolution, ...> wagonSolverManager;
... wagonSolverManager.solve(...)
}
@Path("/trainPlatform")
public class TrainPlatformDeciderResource {
@Inject
SolverManager<TrainCrewSolution, ...> platformSolverManager;
// Uses TrainWagonSolution's output as problem facts:
// Length of train impacts eligible platforms
... platformSolverManager.solve(...)
}
For example:
No java.time
import
import java.time.LocalDate;
@PlanningEntity
public class Patient {
private String name;
private LocalDate arrivalDate;
private LocalDate departureDate;
...
@PlanningVariable(...)
private Bed bed;
...
}
Hospital bed planning does not change
a patient's arrival or departure date.
Don't use java.util.Date
for time manipulation.
It's like asking your bartender how to treat cancer.
Don't use java.util.Calendar
either,
It's like asking your dog how to treat cancer.
Use java.time.LocalDate
or java.time.LocalDateTime
instead!
Pick a good domain model.
@Entity
public class Shift {
private LocalDateTime start;
private LocalDateTime end;
@PlanningPin
private boolean history;
...
@PlanningVariable(...)
private Employee employee;
...
}
@PlanningEntity
public class Shift {
private LocalDateTime start;
private LocalDateTime end;
@PlanningPin
// every historic shift is published
private boolean published;
...
@PlanningVariable(...)
private Employee employee;
...
}
Maybe
Assign all respiratory specialists
to the last shift of your planning window
(even in other wards as normal nurses).
⇒
draft length >= 2 * publish length
import java.time
@PlanningEntity
public class Talk {
private Timeslot publishedTimeslot;
...
@PlanningVariable(...)
private Timeslot timeslot;
}
Constraint publishedTimeslot(ConstraintFactory f) {
return f.forEach(Talk.class)
.filter(talk -> talk.getPublishedTimeslot() != null
&& talk.getTimeslot()
!= talk.getPublishedTimeslot())
.penalize(HardMediumSoft.ONE_MEDIUM)
.asConstraint(PUBLISHED_TIMESLOT);
}
DEMO
solver.addProblemFactChange(...)
@PlanningVariable(nullable = true)
class Employee {
boolean virtual;
}
Combine them as needed.
$ git clone https://github.com/TimefoldAI/timefold-quickstarts.git
...
$ cd timefold-quickstarts/java/vehicle-routing
$ mvn quarkus:dev
...
Learn more | timefold.ai |
---|---|
Feedback |
@GeoffreyDeSmet @petrovicky@jvm.social |
Get started |