CASOON Open Source
Model it in Rust. Solve it anytime.
unifier lets you state a problem as variables, domains, rules and goals, and finds the best answer it can: proven optimal when the search completes, the best so far when time runs out, and a clear Infeasible when no answer exists.
cargo add unifierWho it is for
Planning and scheduling
Jobs on machines, lessons in rooms, shifts for people: activities, resources and calendars compile into scheduling constraints that propagate.
Puzzles and assignments
Colour a map, place eight queens, pick one value per variable so that every rule holds. Hard rules must hold, soft goals are scored.
Rust code that must stop on time
Time limits, node budgets and a cancellation token end any solver with the best answer found so far, ready for a request handler or a UI.
The job shop from the terminal, drawn
Three jobs, three machines, each job a fixed order of operations. unifier keeps every machine to one operation at a time, minimises the end of the last one and proves that 11 is the shortest possible. The chart is drawn from the output above.
- interchangeable solver strategies
- 5
- built-in constraints, three of them global
- 17
- outcome states: Optimal, Feasible, Infeasible, Aborted
- 4
- runtime dependency (pathwise)
- 1
What you get
Global constraints that propagate
AllDifferent, Cumulative and NoOverlap prune the search space before the search gets there, instead of only checking finished answers.
Honest results
Optimal only when Branch & Bound has proven it, Infeasible only when the search space is exhausted, Aborted with a reason otherwise, plus a bound that shows how far from proven a result is.
One model, five solvers
Backtracking, Branch & Bound, local search, large neighbourhood search and a parallel portfolio all solve the same validated model.
Quickstart
From a model to a proven optimum. The quickstart in the documentation walks through the full program and its output.
- Add the crate with
cargo add unifier. - Declare variables, constraints and objectives with
ModelBuilder. - Validate with
build(), then pass the graph to a solver.
let mut model = ModelBuilder::new();
let a = model.new_var("a", 1..=4);
let b = model.new_var("b", 1..=4);
model.add_all_different([a, b]);
model.add_minimize([a, b], 1);
let graph = model.build().expect("valid model");
let outcome = BranchAndBoundSolver::new().solve(&graph, &SolverOptions::default());New in v0.6.0
Changelog →- unifier builds on
pathwise0.3 instead of 0.2.CancellationTokenis re-exported frompathwise, so code that names the 0.2 type needspathwise0.3 as well. ConstraintPenalty: an objective that turns anyConstraintinto a preference. Each violation the constraint counts (Constraint::violations) costsweightsoft points, so a graded constraint such asMaximumBucketLoadorBucketBlockPatternhands the search its own gradient. Its scope is the constraint's, which keeps incremental scoring local.Objective::deltaandConstraint::move_violations: an objective can answer a move with the change it causes, and a constraint can prepare an evaluator once per move that answers each candidate value. Defaults keep the full evaluation;CategorizedObjectiveforwardsdelta,NoOverlapprepares an evaluator (pairs among the other tasks counted once). Local search uses both — on timetable repairs 2.7–5× more steps per second.- Repair from an infeasible baseline builds chains of moves: while the LNS center breaks hard constraints, the conflict search runs on the whole model without a node cap instead of on a frozen neighbourhood (a blocked period in a full timetable went from 7 of 8 conflicts left to none).
- Local search scores moves graded (
Constraint::violations), ascalculate_scoredoes — one more clash inside an already broken constraint now costs something. - Local search picks the conflicted variable from
Constraint::explainwhere available, and collects a move's constraints, objectives and old share once per move instead of per value.
Frequently asked questions
What kind of problems is unifier for?
Constraint Satisfaction and Optimization Problems: variables, domains and rules, plus an objective once something should be optimized rather than just satisfied. Typical instances are scheduling, timetabling and resource allocation, but also puzzles and assignments such as map colouring or eight queens.
How does unifier relate to pathwise?
It does not implement pathwise's Problem or OptimizationProblem traits or route search through its generic algorithms. unifier ships its own CSP/COP-specialized solver stack, and shares only two generic, CSP-independent primitives with pathwise: a cancellation token and a shared incumbent for anytime and parallel search coordination.
What happens when a solver runs out of time?
A SolveOutcome reports Aborted(reason) together with the best solution found so far, not nothing. Branch & Bound additionally reports a score bound, so you can see how far the result is from a proven optimum.
Is unifier ready for production use?
It is early (0.x) but published to crates.io, with the core model, constraint propagation, global constraints, hard/soft scoring and all five solver strategies implemented and tested. General unsat cores, serde-based serialization or a CLI, and independent verification on production-scale scheduling scenarios are not yet covered — evaluate accordingly before relying on it for production planning.