Skip to main content

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

pytimetable-0.2.0.tar.gz (39.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pytimetable-0.2.0-py3-none-any.whl (71.6 kB view details)

Uploaded Python 3

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

Hashes for pytimetable-0.2.0.tar.gz
Algorithm Hash digest
SHA256 ffb282c049771d18037b1546d5ca26d728832b188e5686e0a1b9124f8fb40805
MD5 dc924f0c84bc24506e79945ab0eeb08e
BLAKE2b-256 63c8c46ff1432ef9e5441924bc89224caffea9e2f83760685e7595343a4e9a76

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytimetable-0.2.0.tar.gz:

Publisher: publish.yml on briansimpo/pytimetable

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for pytimetable-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d01aa8737824b315580c5a1c8b50406b76d85dabb21d6dfcd7148833d0bc6d37
MD5 2840dff31f65169151cece6fbaacd037
BLAKE2b-256 5798b2d0313211310e635b2e71461e2bfbfc7cea8d4ed50f047c7bc2fea59203

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytimetable-0.2.0-py3-none-any.whl:

Publisher: publish.yml on briansimpo/pytimetable

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.4.0

2 files

0.3.0

2 files

This release

0.2.0 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page