Skip to main content

Crate schedulr

Crate schedulr 

Source
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 structured Conflict types 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;
  • compilesolve plus analyze, evaluate_move, suggest, compare, explain, and baseline-aware repair paths.

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 compilesolveexplain 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§

AcademicPeriod
Activity
Scheduling demand independent of any concrete solution assignment.
ActivityId
Analysis
Assignment
Concrete placement of one activity.
Bottleneck
BreakTemplate
CompileError
CompiledProblem
Conflict
Structured scheduling conflict suitable for direct display or app-side localization.
DayTemplate
GroupMembership
MoveEvaluation
Participant
Person or group whose simultaneous activities can be detected.
ParticipantGroup
Domain-neutral participant group. Memberships are stored separately so groups can overlap.
ParticipantGroupId
ParticipantId
ParticipantPool
Named set of interchangeable participants.
ParticipantPoolId
ParticipantRequirement
One participant chosen from an exact id, a named pool, or an explicit candidate set.
ProposedActivity
Exact single-activity change checked against a crate::SchedulingState.
RepairOptions
Resource
Capacity-constrained entity consumed by activities.
ResourceId
ResourcePool
Named set of interchangeable resources.
ResourcePoolId
ResourceRequirement
Exact capacity demand on a resource.
ScheduleTemplate
Periodic slot model (one week, A/B weeks, or block cycle) plus absolute exceptions.
SchedulingProblem
In-memory batch scheduling input.
SchedulingState
In-memory snapshot used for synchronous single-activity feasibility checks.
Score
Public score independent of the underlying solver representation.
ScoreComponent
ScoreRule
Named, inspectable scoring rule. Components are attached to each produced solution.
SlotTemplate
One reusable slot inside a periodic schedule template.
Solution
A solved set of activity placements and its aggregate score.
SolutionComparison
SolveResult
SolveStatistics
Suggestion
TimeWindow
Half-open integer time interval [start, end).

Enums§

AssignmentChange
ConflictSeverity
EntityRef
GroupMember
ScoreLevel
ScoreRuleKind
SolveStatus

Constants§

DEFAULT_CAPACITY_DIMENSION

Functions§

compare
compile