A CSP instance is a tuple (X, Φ, ONES, ZEROS) where:
| Component | Definition |
|---|---|
X = {x1, ..., xn} | Variables |
Φ = (Φ1, ..., Φn) | Domains — candidate value sets, mutable during solving |
ONES[(i,v)] | Forbidden partners: if xi=v then xj≠w |
ZEROS[(i,v)] | Compatible partners: xi=v and xj=w can coexist |
The conflict predicate C(i,v,j,w)→{0,1} is called once to build the ONES/ZEROS indices, then discarded. All solving uses indexed lookups — no black-box queries.
Constraints are data, not queries. The ONES index can be walked, filtered, and updated.
Propagation becomes set arithmetic on the index. Learning is native — new forbidden pairs are added
directly to ONES. The vulnerability ratio |ONES[(i,v)]| / (|ZEROS[(i,v)]| + 1) gives a
per-value fragility score for variable/value ordering.
The solver decomposes into three zones, following the architecture established in the Sudoku and SAT papers:
| Zone | Tier | Technique | Type |
|---|---|---|---|
| P-Left | 0 | Singleton propagation (LIFO BCP) | Polynomial |
| 1 | Hidden singles (value unique in constraint group) | Polynomial | |
| 2 | Naked subsets (k vars sharing k values → lock) | Polynomial | |
| 3 | Failed value probing (try → propagate → eliminate) | Polynomial | |
| P-Right | 4 | Symmetry breaking (identical vars → fix one) | Polynomial |
| NP-Strip | 5 | cdcl(a) — budgeted backtracking + chain eval | O(na) |
| 6 | Markov chain walk (from Logistics framework) | Stochastic |
P-Left techniques run in a fixpoint loop — when any technique makes progress, the loop restarts from Tier 0. This maximizes deductive reach before entering the NP-Strip.
When a domain becomes singleton {v}, all forbidden partners from ONES[(i,v)] are removed from neighbor domains. Removals that create new singletons are processed depth-first (LIFO stack), catching contradictions early and maximizing cascade depth.
propagate():
while stack not empty:
(i, v) = stack.pop() # LIFO
if v ∉ Φi or |Φi| ≠ 1: continue
for (j, w) in ONES[(i, v)]:
if w ∈ Φj:
Φj.remove(w)
trail.append((j, w))
if Φj = ∅: return CONTRADICTION
if |Φj| = 1: stack.push((j, sole(Φj)))
return OK
For each variable i with |Φi| > 1, for each value v ∈ Φi: temporarily assign xi=v, propagate. If contradiction: v is dead — permanently remove from Φi. Uses snapshot/restore for clean undo.
This is BCP-1 generalized from SAT to arbitrary CSP domains.
Before committing to a decision in cdcl, a short Markov chain run from the partial assignment reveals the effective difficulty of completing it:
evaluate_branch(i, v):
assignment = partial ∪ {xi = v} ∪ random_fill(undecided)
for step in 1..K:
j = random(conflicted_variables(assignment))
assignment[j] = argminw ∈ Φj conflicts(j, w)
return total_conflicts(assignment) # residual = branch quality
Residual = 0: branch leads to a solution. Low residual: easy subproblem. High residual: likely dead end. This is a Monte Carlo estimate of the remaining search depth, information that propagation alone cannot provide.
Adapted directly from the Logistics framework (Dana, 2023). The walk operates on the reduced domains from P-Left:
1. Greedy init: assign each variable to min-conflict value 2. Repeat: a. Pick random conflicted variable (from incremental counter) b. Sample K candidates from Φi, pick min-conflicts c. Accept: improvement (always), lateral (always), worse (SA probability) d. Adaptive restart when stale
Conflict tracking is incremental using the ONES index: O(degree) per update instead of O(n²). The walk's convergence bound from the Logistics paper: O(na+1 M² ln M).
The Dana Theorem for CSP (2024, extended 2026) states: any indicator function over a finite dataset can be encoded as a CSP in polynomial time. Snake implements this construction; this solver operates on the resulting instances.
The ONES/ZEROS collections are the CSP analogue of AUMA's weighted clause set W. The conflict predicate is the analogue of clause evaluation. The Markov chain walk is the same framework that originated in the Logistics bin packing paper.
| Problem | Scale | Technique | Time | Status |
|---|---|---|---|---|
| Graph coloring | Petersen (χ=3) | cdcl | <1ms | SAT |
| Graph coloring | Petersen, 2 colors | probing | <1ms | UNSAT (proven) |
| Graph coloring | G(100, 0.1), 5 colors | cdcl | 5ms | SAT |
| Sudoku | Easy (30+ clues) | propagation | <1ms | SAT |
| Sudoku | Hard (17+ clues) | cdcl | 4ms | SAT |
| N-Queens | n=50 | cdcl | 1.6s | SAT |
| N-Queens | n=100 | walk | 4.0s | SAT |
The sandwich correctly routes problems to the right tier:
Any CSP is a boolean classification problem on the Cartesian product of its domains. Any boolean classification problem is a CSP. They are the same object.
Let a CSP have variables X1, ..., Xm with finite domains D1, ..., Dm and constraints C. Define:
Every ω ∈ Ω is a complete assignment. The constraint predicate defines an indicator:
The CSP IS a boolean classification on Ω. Licit = positive class. Illicit = negative class. Solving = finding a positive.
Any indicator function over a finite dataset can be encoded as a CSP in polynomial time
(Dana Theorem for CSP, 2024/2026). The boolean tests on features become constraints.
Snake implements this construction: oppose() finds separating tests, the bucket chain
builds constraints, the 30 literal types provide the test library.
The difference is operational:
Same function, different question. Every tool from one domain applies to the other:
| Classification tool | CSP application |
|---|---|
| Snake lookalike probability | Value ordering — "candidates like this tend to be licit" |
| Oppose profiles | Constraint-aware test selection — which boolean tests generalize |
| Core/noise divergence | Structural vs base-rate signal for candidate quality |
| Audit trail | Explanation of why a partial assignment is likely licit or illicit |
| CSP tool | Classification application |
|---|---|
| Arc consistency | Feature domain reduction — eliminate provably irrelevant values |
| Constraint propagation | Deductive feature pruning — if feature_k=v then feature_j≠w |
| Failed value probing | Feature testing — "if I assume this literal, contradiction?" |
| Backtracking | Decision tree with rollback — explore classification hypotheses |
AUMA (2023) — {f: {0,1}n → R} ⊂ W, everything is weighted MAXSAT
Dana Theorem (2024) — indicator → CNF in poly time
Snake — SAT classifier, 30 literal types, oppose profiles
PolySAT — 16 polynomial techniques on SAT
Sudoku sandwich — P/NP/P decomposition for Sudoku
Logistics — Markov chain CSP, superposition + random walk
CSP — universal solver, indexed ONES/ZEROS, P/NP/P sandwich
| Method | Endpoint | Description |
|---|---|---|
| POST | /csp | Universal CSP (variables + domains + ONES constraints) |
| POST | /color | Graph coloring (adjacency + n_colors) |
| POST | /nqueens | N-Queens (board size n) |
| POST | /sudoku | 9×9 Sudoku (81-int grid, 0=empty) |
| GET | /health | Health check |
| GET | /docs | OpenAPI interactive documentation |