Week 6 - Automatic Scheduler
27 Jun 2014This blog post is a short tutorial on how to solve the operation theater scheduling problem using the optaplanner library.
The goal is to come up with an optimal scheduling that is defined by a set of constraints. In this case the set of constraints is divided into two parts:
- Hard rules: (e.g.: no overlapping surgeries in the same operation theater at the same time)
- Soft rules: (e.g.: first come first served)
The difference between them, is that hard rules must not be broken, while soft rules should not be broken. Based on the rules a score is calculated and maximized by the solver.
Before I will start to describe the implementation I want to introduce a few terms that will be used later:
- Problem fact
used to calculate the score, but does not change during planning (e.g. Procedure) - Planning variable
In contrast to the problem fact, the value of this variable will be changed by the solver. (surgery start time, operation theater) - Shadow variable
Changes its value just as the planning variable. The difference here is that its value is not directly modified by the solver, but calculated depending on a planning variable (surgery end time) - Planning entity
It is a POJO that contains the planning variables and thus changes during solving - Planning solution
A wrapping class that implements the Solution interface, holds all problem facts and the planning entity.
Code walkthrough:
Planning Entity
To define a planning entity one has to add a class annotation
To define a planning variable, just annotate the corresponding getter method
The value range provider reference is a link to a method that returns all possible values of this planning variable
The shadow variable “end” is updated within the setter of the planning variable “start”
Planning Solution
In the following code snippet you can see all attributes
The Solution interface defines three functions: getter and setter of the score attribute and getProblemFacts
Make sure that you don’t add the planning entity. Another common mistake is to use facts.add() instead of facts.addAll()
As we defined our planning variables in the planning entity class, we specified a value range provider reference. Now we have to tell optaplanner which function provide these objects. We do that - yes you are right - by using another annotation
Defining rules
Now that we have defined all relevant classes we can start writing our business rules. We do that by using Drools rule language - The basic syntax is as follows
Here is one hard constraint. If the when condition is met the hard constraint score is decreased by one.
Now I want to shortly describe how this rule works. When part: On the first line PlannedSurgery is stored in the variable ($left). The dollar sign is not needed, but increases readability. The location attribute of the PlannedEntity that is stored in $left is assigned to the variable $location The second line is true for PlannedSurgeries that are not the same one as in the first line, have the same location and do overlapp (isOverlapping is a function defined in PlannedSurgery) The last line just makes sure that all pairs are only processed once (AB, BA)
Before you can execute the solver you have to configure it (e.g. define the optimization algorithm) You can find a basic configuration here.
Now we are ready to..
start the solver
This concluded this basic example.
Stay tuned for future blog posts that will describe how to evolve this solution. You can find the complete code for this tutorial in this commit
References: