Skip to main content

csp_solver/solver/
monotonic.rs

1//! Monotonic propagation for lattice domains.
2//!
3//! Specialized fixed-point loop that skips adjacency graph construction
4//! and undo-log overhead. Domains only grow (join/union), so no backtracking
5//! is needed — simple iteration until convergence.
6
7use crate::constraint::{ConstraintEnum, Revision};
8use crate::domain::Domain;
9use crate::variable::Variable;
10use crate::{SolveStats, Unsatisfiable};
11
12/// Propagate constraints over lattice domains to a fixed point.
13///
14/// Unlike AC-3, this doesn't build an adjacency graph or maintain a worklist
15/// with per-constraint membership tracking. Instead, it repeatedly sweeps all
16/// constraints until none produce changes — appropriate when domains are
17/// monotonic (only grow via join) and the constraint graph is small.
18///
19/// Returns `Err(Unsatisfiable)` if a constraint reports unsatisfiable
20/// (shouldn't happen for well-formed lattice CSPs).
21pub(crate) fn propagate_monotonic<D: Domain>(
22    variables: &mut [Variable<D>],
23    constraints: &[ConstraintEnum<D>],
24    stats: &mut SolveStats,
25) -> Result<(), Unsatisfiable>
26where
27    D::Value: PartialEq + 'static,
28{
29    loop {
30        let mut changed = false;
31        for c in constraints {
32            match c.revise(variables, 0) {
33                Revision::Unchanged => {}
34                Revision::Changed => {
35                    changed = true;
36                    stats.propagations += 1;
37                }
38                Revision::Unsatisfiable => return Err(Unsatisfiable),
39            }
40        }
41        if !changed {
42            break;
43        }
44    }
45    Ok(())
46}