In many real-world scenarios, we often face problems that involve making a series of decisions subject to a set of constraints. For example, assigning employees to shifts, organizing school timetables, or optimizing routes for delivery services. These are known as combinatorial optimization problems, and they can be quite challenging to solve with brute force or simple heuristics.
This is where constraint programming (CP) comes in — a paradigm designed for solving such problems efficiently. Google’s OR-Tools is a powerful open-source toolkit that provides a constraint solver along with other optimization engines. In this article, we’ll explore how to use OR-Tools in Python to model and solve a scheduling problem with hard constraints.
Our example: Assigning subjects to weekly time slots in classrooms, where each subject has a required number of hours, and subjects must not overlap within the same classroom.
Let’s dive in!
2 Defining the Scheduling Problem
We are given a list of subjects and classrooms. Each subject has a required number of hours per week. Our goal is to assign subjects to weekly time slots such that:
Each subject gets exactly the number of hours it requires.
Each classroom has only one subject per slot.
Let’s define our subjects and classrooms:
Code
subjects = {"PAI": {"hour": 2},"Al-Qur'an": {"hour": 3},"Olahraga": {"hour": 2},}classrooms = ["Kelas1_C0", "Kelas1_C1"]weekdays = ["Senin", "Selasa", "Rabu", "Kamis", "Jumat"]hours = ["07.00", "08.00", "09.00", "10.00", "11.00", "13.00"]slots = [(d, h) for d in weekdays for h in hours]
3 Building the Constraint Model
We’ll use cp_model from OR-Tools to define our variables and constraints.
Code
from ortools.sat.python import cp_modelmodel = cp_model.CpModel()# Variable: is_subject_assigned[(classroom, subject, slot)] = 1 if subject is scheduled in that slotis_subject_assigned = {}for cls in classrooms:for subj in subjects:for slot in slots: is_subject_assigned[(cls, subj, slot)] = model.NewBoolVar(f"{cls}_{subj}_{slot}")
4 Adding Constraints to the Model
4.1 1. Each subject must be assigned the exact number of required hours:
Code
subject_slots_per_classroom_subject = {}for cls in classrooms:for subj, subject in subjects.items(): assigned_slots = [is_subject_assigned[(cls, subj, slot)] for slot in slots] var = model.NewIntVar(0, len(slots), f"hours_{cls}_{subj}") model.Add(var ==sum(assigned_slots)) model.Add(var == subject["hour"]) subject_slots_per_classroom_subject[(cls, subj)] = var
4.2 2. Each classroom can only have one subject per slot:
Code
for cls in classrooms:for slot in slots: one_subject_per_slot = [is_subject_assigned[(cls, subj, slot)] for subj in subjects] model.Add(sum(one_subject_per_slot) <=1)
5 Solving the Model
Now we’ll solve the model and print the schedule if it exists.
Code
solver = cp_model.CpSolver()status = solver.Solve(model)if status == cp_model.FEASIBLE or status == cp_model.OPTIMAL:for cls in classrooms:print(f"\nSchedule for {cls}:")for day in weekdays:print(f" {day}:")for hour in hours: slot = (day, hour) subject_found =Falsefor subj in subjects:if solver.Value(is_subject_assigned[(cls, subj, slot)]) ==1:print(f" {hour}: {subj}") subject_found =Trueifnot subject_found:print(f" {hour}: -")else:print("No solution found.")
Schedule for Kelas1_C0:
Senin:
07.00: PAI
08.00: Al-Qur'an
09.00: Al-Qur'an
...
(Your results may vary based on how the solver assigns time slots.)
7 Conclusion
We’ve just modeled a simple weekly scheduling problem using constraint programming with OR-Tools. We:
Defined subjects and classrooms
Created binary decision variables
Applied hard constraints for hours and exclusivity
Solved and printed the solution
This is only scratching the surface — you can add more constraints like teacher availability, subject preferences, or soft constraints (e.g., minimizing gaps). Constraint programming gives you a powerful, declarative way to solve such scheduling challenges.
---title: "Constraint Programming with OR-Tools"author: "Sulthan A. Karimov"date: todayformat: html: toc: true code-fold: true code-tools: true code-overflow: wrap number-sections: true df-print: paged engine: knitr---## Introduction: Why Constraint Programming?In many real-world scenarios, we often face problems that involve making a series of decisions subject to a set of constraints. For example, assigning employees to shifts, organizing school timetables, or optimizing routes for delivery services. These are known as **combinatorial optimization problems**, and they can be quite challenging to solve with brute force or simple heuristics.This is where **constraint programming (CP)** comes in — a paradigm designed for solving such problems efficiently. Google’s **OR-Tools** is a powerful open-source toolkit that provides a constraint solver along with other optimization engines. In this article, we’ll explore how to use OR-Tools in Python to model and solve a scheduling problem with hard constraints.Our example: **Assigning subjects to weekly time slots in classrooms**, where each subject has a required number of hours, and subjects must not overlap within the same classroom.Let’s dive in!## Defining the Scheduling ProblemWe are given a list of subjects and classrooms. Each subject has a required number of hours per week. Our goal is to assign subjects to weekly time slots such that:1. Each subject gets exactly the number of hours it requires.2. Each classroom has only one subject per slot.Let’s define our subjects and classrooms:```{python}subjects = {"PAI": {"hour": 2},"Al-Qur'an": {"hour": 3},"Olahraga": {"hour": 2},}classrooms = ["Kelas1_C0", "Kelas1_C1"]weekdays = ["Senin", "Selasa", "Rabu", "Kamis", "Jumat"]hours = ["07.00", "08.00", "09.00", "10.00", "11.00", "13.00"]slots = [(d, h) for d in weekdays for h in hours]```## Building the Constraint ModelWe’ll use `cp_model` from OR-Tools to define our variables and constraints.```{python}from ortools.sat.python import cp_modelmodel = cp_model.CpModel()# Variable: is_subject_assigned[(classroom, subject, slot)] = 1 if subject is scheduled in that slotis_subject_assigned = {}for cls in classrooms:for subj in subjects:for slot in slots: is_subject_assigned[(cls, subj, slot)] = model.NewBoolVar(f"{cls}_{subj}_{slot}")```## Adding Constraints to the Model### 1. Each subject must be assigned the exact number of required hours:```{python}subject_slots_per_classroom_subject = {}for cls in classrooms:for subj, subject in subjects.items(): assigned_slots = [is_subject_assigned[(cls, subj, slot)] for slot in slots] var = model.NewIntVar(0, len(slots), f"hours_{cls}_{subj}") model.Add(var ==sum(assigned_slots)) model.Add(var == subject["hour"]) subject_slots_per_classroom_subject[(cls, subj)] = var```### 2. Each classroom can only have one subject per slot:```{python}for cls in classrooms:for slot in slots: one_subject_per_slot = [is_subject_assigned[(cls, subj, slot)] for subj in subjects] model.Add(sum(one_subject_per_slot) <=1)```## Solving the ModelNow we’ll solve the model and print the schedule if it exists.```{python}solver = cp_model.CpSolver()status = solver.Solve(model)if status == cp_model.FEASIBLE or status == cp_model.OPTIMAL:for cls in classrooms:print(f"\nSchedule for {cls}:")for day in weekdays:print(f" {day}:")for hour in hours: slot = (day, hour) subject_found =Falsefor subj in subjects:if solver.Value(is_subject_assigned[(cls, subj, slot)]) ==1:print(f" {hour}: {subj}") subject_found =Trueifnot subject_found:print(f" {hour}: -")else:print("No solution found.")```## Sample OutputHere's an example output for `Kelas1_C0`:```{txt}Schedule for Kelas1_C0: Senin: 07.00: PAI 08.00: Al-Qur'an 09.00: Al-Qur'an ...```(Your results may vary based on how the solver assigns time slots.)## ConclusionWe’ve just modeled a simple weekly scheduling problem using constraint programming with OR-Tools. We:* Defined subjects and classrooms* Created binary decision variables* Applied hard constraints for hours and exclusivity* Solved and printed the solutionThis is only scratching the surface — you can add more constraints like teacher availability, subject preferences, or soft constraints (e.g., minimizing gaps). Constraint programming gives you a powerful, declarative way to solve such scheduling challenges.### 🔗 Resources* [Google OR-Tools Documentation](https://developers.google.com/optimization)* [OR-Tools Python Examples](https://github.com/google/or-tools/tree/stable/examples/python)* [MiniZinc](https://www.minizinc.org/) – another popular CP systemLet me know if you’d like to expand this tutorial to cover optimization goals or soft constraints!