Expand description
§schedulr
Scheduling framework (activity/resource/interval DSL) for Rust, built
on unifier’s CSP/COP constraint
model and solvers, which in turn build on
pathwise’s generic search and
optimization traits.
pathwise → unifier → schedulr → (application: timetabling, appointment booking, ...)§Status
Version 0.8 is implemented. It provides:
- domain-neutral
Resource,Participant,Activity,Assignment,Score, and structuredConflicttypes without exposing solver internals; - synchronous create/move/cancel/participant-update checks through
SchedulingState, including self-exclusion when moving an activity; - resource matching by type, capacity and feature, named resource and participant pools, and hierarchical/overlapping participant groups;
- periodic slot calendars with absolute exceptions and lexicographic Strong/Medium/Weak score components;
compile→solveplusanalyze,evaluate_move,suggest,compare,explain, and baseline-awarerepairpaths.
The single-activity path evaluates only constraints affected by the proposed change and does not start a solver search. Persistence remains an application concern; solutions carry score components so applications can store them with their immutable schedule versions.
§Problem class
Scheduling / timetabling / appointment booking on top of unifier’s
CSP/COP model: activities placed against resources over time, subject
to hard constraints (no double-booking, capacity, precedence,
calendar/opening-hours exclusions) and soft preferences (e.g.
proximity to a requested time slot).
§Usage
Synchronous single-activity checks (e.g. a live booking desk), via
SchedulingState — no solver search, only the constraints touching the
proposed activity are evaluated:
use schedulr::{
Participant, ParticipantId, ProposedActivity, Resource, ResourceId,
ResourceRequirement, SchedulingState, TimeWindow,
};
let mut state = SchedulingState::new(
[Resource::new(ResourceId(1), "room", 1)],
[Participant::new(ParticipantId(1), "Alex")],
[],
);
let proposal = ProposedActivity::new("appointment", TimeWindow::new(10, 20))
.with_requirement(ResourceRequirement::new(ResourceId(1), 1))
.with_participant(ParticipantId(1));
// Blocking conflicts (e.g. room double-booked) prevent commit; advisory
// conflicts (e.g. a participant already booked elsewhere) do not.
let activity_id = state.commit(proposal).expect("room and participant are free");
// Moving an activity excludes its own prior booking from the check via
// `excluding`, so it does not conflict with itself:
let moved = ProposedActivity::new("appointment", TimeWindow::new(15, 25))
.with_requirement(ResourceRequirement::new(ResourceId(1), 1))
.with_participant(ParticipantId(1))
.excluding(activity_id);
state.commit(moved).expect("new slot is free");Batch scheduling with a minimal compile → solve → explain path for
hard resource conflicts:
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);
}
}§Installation
schedulr = "0.8"§License
MIT — see LICENSE.
Structs§
- Academic
Period - Activity
- Scheduling demand independent of any concrete solution assignment.
- Activity
Id - Analysis
- Assignment
- Concrete placement of one activity.
- Bottleneck
- Break
Template - Compile
Error - Compiled
Problem - Conflict
- Structured scheduling conflict suitable for direct display or app-side localization.
- DayTemplate
- Group
Membership - Move
Evaluation - Participant
- Person or group whose simultaneous activities can be detected.
- Participant
Group - Domain-neutral participant group. Memberships are stored separately so groups can overlap.
- Participant
Group Id - Participant
Id - Participant
Pool - Named set of interchangeable participants.
- Participant
Pool Id - Participant
Requirement - One participant chosen from an exact id, a named pool, or an explicit candidate set.
- Proposed
Activity - Exact single-activity change checked against a
crate::SchedulingState. - Repair
Options - Resource
- Capacity-constrained entity consumed by activities.
- Resource
Id - Resource
Pool - Named set of interchangeable resources.
- Resource
Pool Id - Resource
Requirement - Exact capacity demand on a resource.
- Schedule
Template - Periodic slot model (one week, A/B weeks, or block cycle) plus absolute exceptions.
- Scheduling
Problem - In-memory batch scheduling input.
- Scheduling
State - In-memory snapshot used for synchronous single-activity feasibility checks.
- Score
- Public score independent of the underlying solver representation.
- Score
Component - Score
Rule - Named, inspectable scoring rule. Components are attached to each produced solution.
- Slot
Template - One reusable slot inside a periodic schedule template.
- Solution
- A solved set of activity placements and its aggregate score.
- Solution
Comparison - Solve
Result - Solve
Statistics - Suggestion
- Time
Window - Half-open integer time interval
[start, end).