PyTimetable
PyTimetable is a domain-independent constraint-based scheduling framework for modeling, generating, and optimizing timetables.
It provides reusable scheduling primitives for activities, participants, facilitators, venues, time, requirements, constraints, and optimization without coupling the scheduling engine to a particular application domain.
PyTimetable can be used for problems such as:
- university timetables
- school timetables
- examination schedules
- training schedules
- meeting schedules
- resource-constrained scheduling problems
Applications can either construct PyTimetable problems directly or adapt their existing domain models into a PyTimetable Problem.
Application Domain
│
▼
ProblemAdapter
│
▼
Problem
│
▼
Solver
│
▼
Solution
│
▼
SolutionAdapter
│
▼
Application Result
The scheduling engine does not need to understand concepts such as courses, semesters, classrooms, or university programs. Applications translate those concepts into generic scheduling requirements.
Installation
Install PyTimetable from PyPI:
pip install pytimetable
For local development:
git clone git clone https://github.com/briansimpo/pytimetable.git
cd pytimetable
python -m venv venv
Activate the virtual environment.
Windows
.\venv\Scripts\Activate.ps1
Linux / macOS
source venv/bin/activate
Install the project in editable mode:
pip install -e .
Quick Start
The following example demonstrates the basic PyTimetable workflow using native scheduling objects.
Timeline
+
Resources
+
Activities
│
▼
Problem
│
▼
Solver
│
▼
Solution
1. Create a Timeline
A Timeline defines the schedulable days and periods available to the solver.
from datetime import time
from pytimetable.temporal.timeline import TimelineBuilder
timeline = TimelineBuilder(
days=5,
start_time=time(8, 0),
end_time=time(17, 0),
period_minutes=60,
).build()
This creates a five-day scheduling timeline with one-hour periods between 08:00 and 17:00.
2. Create Resources
PyTimetable schedules activities against resources such as facilitators and venues.
Create a location and venue type:
from pytimetable.models.location import Location
from pytimetable.models.venuetype import VenueType
main_building = Location(
name="Main Building",
)
lecture_room = VenueType(
name="Lecture Room",
)
Create a venue:
from pytimetable.models.venue import Venue, Venues
from pytimetable.temporal.availability import Availability
room_a = Venue(
name="Room A",
capacity=100,
location=main_building,
venue_type=lecture_room,
availability=Availability(
timeline.timeslots
),
)
venues = Venues([
room_a,
])
Create an eligible facilitator:
from pytimetable.models.facilitator import (
Facilitator,
Facilitators,
)
lecturer = Facilitator(
name="Dr Ada Lovelace",
availability=Availability(
timeline.timeslots
),
)
facilitators = Facilitators([
lecturer,
])
3. Create a Participant
A Participant represents the person or group that must attend an activity.
from pytimetable.models.participant import Participant
students = Participant(
name="Computer Science Y1",
size=40,
availability=Availability(
timeline.timeslots
),
)
4. Create an Event
An Event represents the broader event to which one or more schedulable activities belong.
It can also define the facilitators eligible to conduct those activities.
from pytimetable.models.event import Event
programming = Event(
name="Programming I",
facilitators=facilitators,
)
5. Create an Activity
An Activity is the unit that the solver places into the timetable.
The following activity represents a two-period lecture requiring one facilitator and a lecture room with sufficient capacity.
from pytimetable.requirements.facilitator import (
FacilitatorRequirement,
)
from pytimetable.requirements.timeslot import (
TimeslotRequirement,
)
from pytimetable.requirements.venue import (
VenueRequirement,
)
from pytimetable.scheduler.activity import (
Activity,
Activities,
)
from pytimetable.temporal.duration import Duration
lecture = Activity(
name="Lecture",
event=programming,
participant=students,
duration=Duration(
periods=2,
),
facilitator_requirement=(
FacilitatorRequirement(
minimum=1,
maximum=1,
)
),
venue_requirement=(
VenueRequirement(
capacity=students.size,
venue_type=lecture_room,
)
),
timeslot_requirement=(
TimeslotRequirement()
),
)
activities = Activities([
lecture,
])
The scheduling engine does not need to know what a university lecture means.
The activity simply declares its scheduling requirements:
Duration → 2 periods
Participant → Computer Science Y1
Facilitators → 1 required
Venue capacity → 40
Venue type → Lecture Room
6. Build the Problem
Combine the activities, venues, timeline, and other scheduling information into a Problem.
from pytimetable.problem import Problem
from pytimetable.scheduler.spatial.travel import TravelTimes
problem = Problem(
activities=activities,
venues=venues,
timeline=timeline,
travel_times=TravelTimes(),
)
7. Solve
Pass the problem to Solver:
from pytimetable.solver import Solver
solution = Solver(
seed=42,
).solve(
problem
)
The solver searches for assignments satisfying the activity requirements while respecting scheduling constraints and resource availability.
A seed can be supplied to make randomized solver behaviour reproducible during development and testing.
Core Concepts
PyTimetable represents scheduling problems using a small set of domain-independent concepts.
Activity
An Activity represents something that must be scheduled.
Examples include:
- a lecture
- a laboratory session
- a tutorial
- an examination
- a meeting
- a training session
An activity describes its duration, participant, and resource requirements.
The solver determines where and when the activity should be scheduled.
Event
An Event groups related scheduling information and identifies the facilitators eligible to conduct its activities.
For example:
Event
└── Programming I
├── Lecture
├── Lab
└── Tutorial
Each schedulable component is represented by an Activity.
Participant
A Participant represents the people or group that must attend an activity.
For a university timetable, a participant might represent:
Computer Science
Year 1
Semester 1
Participant availability and conflicts are considered when constructing the timetable.
Facilitator
A Facilitator represents a resource responsible for conducting an activity.
Examples include:
- lecturers
- teachers
- instructors
- supervisors
An event can have multiple eligible facilitators. The scheduler selects an appropriate facilitator while respecting requirements, availability, and conflicts.
Venue
A Venue represents a physical location where an activity can take place.
A venue can describe:
- capacity
- venue type
- location
- availability
For example:
Lecture Room A
Capacity: 120
Type: Lecture Room
Location: Main Building
Venue Type
A VenueType describes the purpose or category of a venue.
Examples include:
Lecture Room
Computer Lab
Tutorial Room
Chemistry Lab
Workshop
Studio
An activity can require a particular venue type.
This allows the scheduling engine to prevent incompatible assignments such as scheduling a computer laboratory activity in an ordinary lecture room.
Time Model
PyTimetable explicitly models scheduling time through days, periods, timeslots, durations, and timelines.
Day
A Day represents a schedulable day of the week.
from pytimetable.temporal.day import Day
monday = Day(1)
print(monday)
# Monday
Period
A Period represents a schedulable period within a day.
It contains both an internal period number and its real-world time range.
from datetime import time
from pytimetable.temporal.period import Period
period = Period(
number=1,
start_time=time(7, 30),
end_time=time(8, 20),
)
print(period)
# 07:30-08:20
The period number provides a convenient internal representation for the scheduling engine, while start_time and end_time represent the actual time range.
Timeslot
A Timeslot combines a Day and a Period.
Timeslot
├── Day
└── Period
For example:
Monday
07:30-08:20
represents one schedulable position in the timetable.
Duration
An activity specifies how many periods it occupies.
from pytimetable.temporal.duration import Duration
duration = Duration(
periods=2,
)
This means the activity requires two consecutive schedulable periods.
Timeline
A Timeline defines the complete temporal search space for a scheduling problem.
It contains the timeslots in which activities may be scheduled.
from datetime import time
from pytimetable.temporal.timeline import TimelineBuilder
timeline = TimelineBuilder(
days=5,
start_time=time(7, 30),
end_time=time(17, 0),
period_minutes=50,
).build()
Applications can therefore define their scheduling calendar independently of the solver.
Availability
Resources can specify when they are available.
For example:
from pytimetable.temporal.availability import Availability
availability = Availability(
timeline.timeslots
)
Availability can be associated with scheduling resources such as:
- participants
- facilitators
- venues
The scheduler uses this information when determining valid assignments.
Requirements
Activities describe what they need through explicit requirements.
This keeps scheduling rules separate from application-specific concepts.
Facilitator Requirement
An activity can specify how many facilitators it requires.
FacilitatorRequirement(
minimum=1,
maximum=1,
)
The scheduler selects from the eligible facilitators associated with the event.
Venue Requirement
An activity can specify venue requirements such as capacity and venue type.
VenueRequirement(
capacity=60,
venue_type=computer_lab,
)
A compatible venue must satisfy those requirements.
Timeslot Requirement
An activity can also describe temporal requirements.
TimeslotRequirement()
Together with the timeline and availability information, timeslot requirements determine when an activity can be placed.
Building Application Integrations
The Quick Start constructs PyTimetable objects directly.
That approach is useful for:
- learning the API
- tests
- scripts
- small scheduling applications
- domains already closely matching the PyTimetable model
Larger applications should normally keep their own domain model and adapt it to PyTimetable.
A university application, for example, might contain:
Course
CourseComponent
Program
Student
Lecturer
Building
Room
CourseRegistration
These are application concepts and do not need to be replaced with PyTimetable classes.
Instead:
University Domain
│
▼
ProblemAdapter
│
▼
PyTimetable Problem
│
▼
Solver
│
▼
PyTimetable Solution
│
▼
SolutionAdapter
│
▼
University Timetable
This allows applications to use Django models, SQLAlchemy models, dataclasses, API objects, or any other data representation without coupling PyTimetable to them.
Domain Context
DomainContext can be used by adapters to maintain relationships between application-domain objects and their PyTimetable equivalents.
Conceptually:
Application PyTimetable
Lecturer ←──────────→ Facilitator
Room ←──────────→ Venue
CourseComponent ←──────────→ Activity
This becomes particularly useful after solving the problem.
A SolutionAdapter can use the same context to recover the application objects associated with each scheduling assignment.
solution_adapter = SolutionAdapter(
context=problem_adapter.context,
)
result = solution_adapter.adapt(
solution
)
Example: University Timetable
Consider a university course with three teaching components:
Programming I
├── Lecture
│ ├── Duration: 2 periods
│ └── Venue type: Lecture Room
│
├── Lab
│ ├── Duration: 2 periods
│ └── Venue type: Computer Lab
│
└── Tutorial
├── Duration: 1 period
└── Venue type: Tutorial Room
The university application can model these requirements explicitly:
programming = Course(
id=uuid4(),
name="Programming I",
components=(
CourseComponent(
name="Lecture",
periods=2,
venue_type=lecture_type,
),
CourseComponent(
name="Lab",
periods=2,
venue_type=lab_type,
),
CourseComponent(
name="Tutorial",
periods=1,
venue_type=tutorial_type,
),
),
)
The application's ProblemAdapter converts each course component into an Activity.
The component's venue requirement becomes a PyTimetable VenueRequirement.
Conceptually:
CourseComponent
│
├── periods
└── venue_type
│
▼
ProblemAdapter
│
▼
Activity
│
├── Duration
└── VenueRequirement
│
▼
Solver
│
▼
Compatible Venue
The resulting timetable might contain:
Day Time Participant Course Component Venue
------------------------------------------------------------------------------------------------
Monday 12:30-14:20 Computer Science Y1 S1 Programming I Lab Computer Lab
Wednesday 08:30-09:20 Computer Science Y1 S1 Programming I Tutorial Tutorial Room
Friday 11:30-13:20 Computer Science Y1 S1 Programming I Lecture Lecture Room B
PyTimetable itself does not contain rules such as:
if activity.name == "Lab":
assign_computer_lab()
Instead, the activity declares a requirement for a particular venue type.
The generic scheduling engine is responsible only for satisfying that requirement.
Feasibility
A timetable is feasible when all required hard scheduling constraints are satisfied.
Typical feasibility requirements include:
- no participant is assigned to overlapping activities
- no facilitator is assigned to overlapping activities
- no venue is double-booked
- assigned venues have sufficient capacity
- assigned venues satisfy venue-type requirements
- resources are available during their assignments
- activities fit within the timeline
- multi-period activities occupy valid consecutive periods
For example:
Programming Lab
↓
VenueRequirement
↓
Computer Lab
is feasible if the assigned venue satisfies the activity's type, capacity, and availability requirements.
A feasible timetable is valid.
It is not necessarily a high-quality timetable.
Timetable Quality
Once feasibility has been achieved, the schedule can be evaluated and optimized for quality.
Typical quality objectives include:
- minimizing participant gaps
- minimizing facilitator gaps
- limiting excessive consecutive teaching periods
- reducing venue changes
- reducing travel between locations
- distributing activities across the week
- avoiding undesirable times
- balancing resource utilization
These objectives may compete with each other.
For example, aggressively minimizing participant gaps may produce:
09:00-10:00 Activity A
10:00-11:00 Activity B
11:00-12:00 Activity C
12:00-13:00 Activity D
This timetable has excellent compactness but potentially undesirable consecutive load.
A different timetable may reduce consecutive load while introducing gaps.
Scheduling quality is therefore an optimization problem rather than a single universal rule.
Feasibility vs Optimization
PyTimetable separates two important concerns:
Scheduling
│
┌──────────┴──────────┐
│ │
Feasibility Quality
│ │
Hard constraints Soft objectives
│ │
Must be valid Should be better
Hard constraints determine whether a schedule can be accepted.
Soft objectives distinguish between multiple feasible schedules.
This allows optimization algorithms to search for increasingly desirable solutions without compromising correctness.
Architecture
At a high level, PyTimetable follows this architecture:
┌─────────────────────────────────────┐
│ Application Domain │
│ │
│ Courses, Students, Rooms, etc. │
└──────────────────┬──────────────────┘
│
│ ProblemAdapter
▼
┌─────────────────────────────────────┐
│ PyTimetable Problem │
│ │
│ Activities │
│ Participants │
│ Facilitators │
│ Venues │
│ Timeline │
│ Requirements │
└──────────────────┬──────────────────┘
│
│ Solver
▼
┌─────────────────────────────────────┐
│ Solution │
│ │
│ Activity assignments │
│ Timeslots │
│ Venues │
│ Facilitators │
└──────────────────┬──────────────────┘
│
│ SolutionAdapter
▼
┌─────────────────────────────────────┐
│ Application Result │
└─────────────────────────────────────┘
The application owns its business domain.
PyTimetable owns the scheduling problem.
The adapter boundary connects the two.
Design Principles
Domain Independent
PyTimetable should not know what a course, semester, curriculum, classroom, or university program is.
Those concepts belong to the application domain.
PyTimetable operates on generic scheduling concepts such as:
Activity
Participant
Facilitator
Venue
Timeline
Requirement
Assignment
Requirements Over Special Cases
Scheduling behaviour should be expressed through requirements and constraints rather than hard-coded domain rules.
Avoid:
if activity.name == "Lab":
use_computer_lab()
Prefer:
Activity
│
└── VenueRequirement
│
└── VenueType: Computer Lab
The engine then satisfies the generic requirement.
Explicit Domain Boundaries
Application models should not need to inherit from or depend directly on PyTimetable scheduling models.
Adapters provide the translation boundary.
This keeps both sides independently evolvable.
Explicit Time Model
Days, periods, timeslots, durations, and timelines are explicit scheduling concepts rather than implicit integer conventions.
A period contains its real-world time range:
Period 1 → 07:30-08:20
Period 2 → 08:30-09:20
Period 3 → 09:30-10:20
The scheduling engine can work efficiently with period numbers while applications can display actual times.
Feasibility Before Quality
Correctness comes first.
Hard constraints establish feasibility.
Optimization then improves the quality of feasible solutions.
Reproducibility
Randomized scheduling behaviour can be seeded:
solution = Solver(
seed=42,
).solve(
problem
)
This makes development, testing, debugging, and performance comparisons reproducible.
Composability
Scheduling concepts should remain small and composable.
Complex scheduling behaviour should emerge from combinations of:
Activities
Resources
Requirements
Constraints
Objectives
Algorithms
rather than increasingly specialized domain-specific classes.
Development
Clone the repository:
git clone https://github.com/briansimpo/pytimetable.git
cd pytimetable
Create a virtual environment:
python -m venv venv
Activate it.
Windows:
.\venv\Scripts\Activate.ps1
Linux / macOS:
source venv/bin/activate
Install PyTimetable in editable mode:
pip install -e .
Run the university timetable example:
python -m examples.timetable
Example Output
A generated university timetable may look like:
Day Time Participant Course Component Venue Lecturer
Monday 12:30-14:20 Computer Science Y1 S1 Programming I Lab Computer Lab Dr Alan Turing
Tuesday 13:30-15:20 Information Technology Y1 S1 Programming I Lab Computer Lab Dr Grace Hopper
Wednesday 07:30-08:20 Computer Science Y1 S1 Mathematics I Tutorial Tutorial Room Dr Ada Lovelace
Wednesday 08:30-09:20 Computer Science Y1 S1 Programming I Tutorial Tutorial Room Dr Alan Turing
Thursday 12:30-14:20 Information Technology Y1 S1 Academic Writing Lecture Lecture Room B Dr Ada Lovelace
Thursday 14:30-15:20 Information Technology Y1 S1 Academic Writing Tutorial Tutorial Room Dr Ada Lovelace
Thursday 15:30-16:20 Information Technology Y1 S1 Programming I Tutorial Tutorial Room Dr Grace Hopper
Friday 07:30-09:20 Information Technology Y1 S1 Programming I Lecture Lecture Room A Dr Grace Hopper
Friday 09:30-11:20 Computer Science Y1 S1 Mathematics I Lecture Lecture Room B Dr Ada Lovelace
Friday 11:30-13:20 Computer Science Y1 S1 Programming I Lecture Lecture Room B Dr Alan Turing
In this example:
- participant conflicts are avoided
- facilitator conflicts are avoided
- venues are not double-booked
- laboratories use compatible laboratory venues
- tutorials use compatible tutorial venues
- lectures use compatible lecture rooms
- multi-period activities occupy consecutive scheduling periods
- eligible facilitators are selected by the scheduler
Optimization can then improve qualities such as gaps, consecutive load, room changes, travel, and weekly distribution.
Project Status
PyTimetable is under active development.
Current areas of development include:
- generic scheduling primitives
- timetable feasibility
- constraint evaluation
- timetable quality evaluation
- local-search optimization
- multi-objective optimization
- resource availability
- spatial and travel constraints
- reusable domain adapters
The public API may evolve while the architecture is being refined.
Contributing
Contributions, bug reports, design discussions, and feature proposals are welcome.
When contributing, try to preserve PyTimetable's central design principle:
Application domains describe what must be scheduled. PyTimetable determines how to schedule it.
Domain-specific behaviour should generally remain outside the scheduling kernel unless it can be expressed as a reusable scheduling concept.
License
See the project license for details.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pytimetable-0.2.0.tar.gz.
File metadata
- Download URL: pytimetable-0.2.0.tar.gz
- Upload date:
- Size: 39.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ffb282c049771d18037b1546d5ca26d728832b188e5686e0a1b9124f8fb40805
|
|
| MD5 |
dc924f0c84bc24506e79945ab0eeb08e
|
|
| BLAKE2b-256 |
63c8c46ff1432ef9e5441924bc89224caffea9e2f83760685e7595343a4e9a76
|
Provenance
The following attestation bundles were made for pytimetable-0.2.0.tar.gz:
Publisher:
publish.yml on briansimpo/pytimetable
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pytimetable-0.2.0.tar.gz -
Subject digest:
ffb282c049771d18037b1546d5ca26d728832b188e5686e0a1b9124f8fb40805 - Sigstore transparency entry: 2396765858
- Sigstore integration time:
-
Permalink:
briansimpo/pytimetable@f85b97ed0b3f4903dc79713d5857d56ba65fa9f3 -
Branch / Tag:
refs/tags/0.2.0 - Owner: https://github.com/briansimpo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f85b97ed0b3f4903dc79713d5857d56ba65fa9f3 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pytimetable-0.2.0-py3-none-any.whl.
File metadata
- Download URL: pytimetable-0.2.0-py3-none-any.whl
- Upload date:
- Size: 71.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d01aa8737824b315580c5a1c8b50406b76d85dabb21d6dfcd7148833d0bc6d37
|
|
| MD5 |
2840dff31f65169151cece6fbaacd037
|
|
| BLAKE2b-256 |
5798b2d0313211310e635b2e71461e2bfbfc7cea8d4ed50f047c7bc2fea59203
|
Provenance
The following attestation bundles were made for pytimetable-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on briansimpo/pytimetable
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pytimetable-0.2.0-py3-none-any.whl -
Subject digest:
d01aa8737824b315580c5a1c8b50406b76d85dabb21d6dfcd7148833d0bc6d37 - Sigstore transparency entry: 2396766268
- Sigstore integration time:
-
Permalink:
briansimpo/pytimetable@f85b97ed0b3f4903dc79713d5857d56ba65fa9f3 -
Branch / Tag:
refs/tags/0.2.0 - Owner: https://github.com/briansimpo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f85b97ed0b3f4903dc79713d5857d56ba65fa9f3 -
Trigger Event:
release
-
Statement type: