Skip to main content

dotloom_constraints/
lib.rs

1//! # dotloom-constraints
2//!
3//! Constraint solving for Dotloom (ADR-0004).
4//!
5//! * Rules are rows of scalar [`Expr`] residuals with exact derivatives.
6//! * The problem is split into connected components. Purely linear components go to
7//!   **kasuari** (a maintained Cassowary implementation); nonlinear/mixed components
8//!   go to a damped Gauss–Newton backend that solves hard rules as exact equality
9//!   constraints and preferences as least squares inside the hard-feasible set.
10//! * Results distinguish `solved`, `underconstrained`, `conflicting` (with evidence),
11//!   `notConverged` (with or without a suspected conflict), `cancelled` and
12//!   `unsupported`, and carry structured [`Diagnostic`]s.
13//! * [`SolveJob`] runs in budgeted steps so hosts can cancel between steps.
14//! * [`LinearSession`] re-solves a linear problem incrementally while a drag moves
15//!   its target (Cassowary edit variables), verified like a full solve.
16//!
17//! This crate has no document, DOM or GPU dependency.
18//!
19//! ```
20//! use dotloom_constraints::{Problem, Rule, Strength, Variable, Expr, rules, solve, SolveOptions, Status};
21//!
22//! // Shelf: total = w1 + w2 + w3, w1 locked at 600 mm, w2 = w3 ≥ 400 mm.
23//! let mut p = Problem::default();
24//! let total = p.add_var(Variable::new(1800.0).scale(1000.0));
25//! let w1 = p.add_var(Variable::new(600.0).scale(1000.0));
26//! let w2 = p.add_var(Variable::new(600.0).scale(1000.0));
27//! let w3 = p.add_var(Variable::new(600.0).scale(1000.0));
28//! let v = |id| Expr::Var(id);
29//! p.rules.push(Rule::new(1, rules::linear(&[(1.0, v(w1)), (1.0, v(w2)), (1.0, v(w3)), (-1.0, v(total))], rules::Cmp::Eq, 0.0, 1000.0), Strength::Required));
30//! p.rules.push(Rule::new(2, rules::equal(v(w2), v(w3), 1000.0), Strength::Required));
31//! p.rules.push(Rule::new(3, rules::at_least(v(w2), 400.0, 1000.0), Strength::Required));
32//! p.rules.push(Rule::new(4, rules::at_least(v(w3), 400.0, 1000.0), Strength::Required));
33//! p.rules.push(Rule::new(5, rules::fix(v(w1), 600.0, 1000.0), Strength::Required));
34//! p.rules.push(Rule::new(6, rules::fix(v(total), 1600.0, 1000.0), Strength::Required));
35//! let s = solve(&p, &SolveOptions::default());
36//! assert!(s.accepted());
37//! assert!((s.values[w2.index()] - 500.0).abs() < 1e-6);
38//! ```
39
40mod analysis;
41mod expr;
42mod graph;
43mod job;
44mod linear;
45mod numeric;
46mod problem;
47pub mod rules;
48mod session;
49mod solution;
50mod sparse;
51
52pub use expr::{Dual, Expr, LinearForm, PointExpr, VarId};
53pub use graph::{Component, components};
54pub use job::{Progress, SolveJob, solve};
55pub use problem::{Problem, Relation, Row, Rule, STAY_WEIGHT, SolveOptions, Strength, Target, Variable, stay_factor};
56pub use session::LinearSession;
57pub use solution::{Backend, Certainty, ComponentReport, Diagnostic, DiagnosticKind, Solution, Status};