schedulrv0.8.0

CASOON Open Source

Describe the plan, let the solver place it.

schedulr is a scheduling framework for Rust. You describe activities, resources, people and time windows as plain data; schedulr compiles them into a constraint model, solves it with unifier, and explains every conflict in the terms you used.

cargo add schedulr
schedulr = "0.8"
MITcrates.io 0.8.0Rust 1.87+built on unifier 0.3
cargo run --example workshop_plan
Input: 3 resources, 3 participants, 5 activities, 1 resource pool, 1 participant pool, 1 group
Calendar: 24-hour cycle, starts at 09 10 11 13 14 15, Tue 13:00-14:59 closed

Status: Feasible (optimal: true)

When              Activity              Resource      Participants
Mon 09:00-10:00   a1 Kickoff            Room Aurora   Ana, Ben, Chris
Mon 10:00-12:00   a4 Solver deep dive   Room Aurora   Chris
Mon 13:00-16:00   a2 Rust fundamentals  Room Aurora   Ben
Tue 09:00-11:00   a3 Hands-on lab       Lab 1         Ben
Tue 15:00-16:00   a5 Q&A                Room Aurora   Ana, Ben, Chris

Timeline (one column per hour, 09-16)
              Mon                   Tue
              09 10 11 12 13 14 15  09 10 11 12 13 14 15
Room Aurora   a1 a4 a4 .. a2 a2 a2  .. .. .. .. .. .. a5
Room Birch    .. .. .. .. .. .. ..  .. .. .. .. .. .. ..
Lab 1         .. .. .. .. .. .. ..  a3 a3 .. .. .. .. ..
Ana           a1 .. .. .. .. .. ..  .. .. .. .. .. .. a5
Ben           a1 .. .. .. a2 a2 a2  a3 a3 .. .. .. .. a5
Chris         a1 a4 a4 .. .. .. ..  .. .. .. .. .. .. a5

Score: hard 0, strong 0, medium 0, weak 0
  Strong  kickoff on Monday at 09:00  a1  0
  Medium  Q&A on Tuesday afternoon    a5  0
  Weak    lab on Tuesday morning      a3  0
dependency: the unifier constraint solver
1
score levels, compared lexicographically
3
solver searches for a single booking check
0
unit and integration tests on master
54

What it does

  1. Domain types, not solver variables

    Resources, participants, activities, requirements and time windows are the whole public model. Solutions and conflicts come back in the same vocabulary, with your ids and names.

  2. Let the solver choose rooms and people

    Requirements can name an exact resource or ask for any resource of a type, with features, a minimum capacity or from a named pool. Participants come from pools, candidate lists or nested, overlapping groups.

  3. Calendars and preferences

    Periodic slot templates with absolute exceptions decide when activities may start. Score rules on Strong, Medium and Weak levels express preferences, and every solution lists how each rule scored.

  4. Book, explain and repair

    SchedulingState checks one create, move or cancel without a search. For batch plans there are explain, analyze, evaluate_move, suggest, compare and a repair that changes as little as possible.

Generated at build time

All examples →
cargo run --example explain_conflicts
1. Two fixed lab sessions overlap in the same lab, with the same trainer
   Status: Infeasible
   Blocking NoOverlap [a1, a2]: Lab 1: Intervals [9, 11) and [10, 12) overlap
   Advisory NoOverlap [a1, a2]: Chris: Intervals [9, 11) and [10, 12) overlap

2. Three sessions at 09:00 need a room, but there are only two rooms
   Status: Infeasible
   Blocking AlternativeResourceCapacity [a1, a2, a3]: Room Aurora: capacity 1 is insufficient for the unresolved alternatives
   Blocking AlternativeResourceCapacity [a1, a2, a3]: Room Birch: capacity 1 is insufficient for the unresolved alternatives

3. Invalid input is rejected before any solving
   CompileError: resource r1 has zero capacity
   CompileError: activity a1 has an invalid time window
   CompileError: activity a2 requires unknown resource r9
   CompileError: duplicate activity id a2

Captured from the example programs in the repository by scripts/regenerate-examples.sh. Every output on this site comes from schedulr itself.

examples/output/explain_conflicts.txt

Quickstart

Three steps from a problem description to a plan or an explanation. The documentation covers the full DSL.

  1. Add schedulr to your crate.
  2. Describe resources, participants and activities, then compile the problem.
  3. Call solve() and read the assignments, or explain() why no plan exists.
main.rsRust
use schedulr::{
    Activity, ActivityId, Resource, ResourceId, ResourceRequirement,
    SchedulingProblem, SolveStatus, TimeWindow, compile,
};

let room = Resource::new(ResourceId(1), "Physics lab", 1);
let first = Activity::new(ActivityId(1), "first", TimeWindow::new(10, 20), 10)
    .with_requirement(ResourceRequirement::new(room.id(), 1));
let second = Activity::new(ActivityId(2), "second", TimeWindow::new(15, 25), 10)
    .with_requirement(ResourceRequirement::new(room.id(), 1));

let compiled = compile(&SchedulingProblem::new(vec![room], vec![], vec![first, second]))
    .expect("problem compiles");
let result = compiled.solve();
if result.status != SolveStatus::Feasible {
    for conflict in compiled.explain(&result) {
        println!("{}: {}", conflict.constraint_name, conflict.message);
    }
}