Overview
A Constraint Satisfaction Problem (CSP) encodes a decision problem as a set of variables with domains and a set of constraints restricting which combinations of values are allowed. A solution is a total assignment of all variables that satisfies all constraints. CSPs unify problems like map coloring, scheduling, timetabling, Sudoku, and n-queens under a common modeling and algorithmic toolkit.
CSP algorithms exploit structure—constraint graphs, variable/constraint orderings, and local consistency—to prune search and reduce combinatorial explosion. When exact search is expensive, local search (e.g., min-conflicts) often finds high-quality solutions quickly.
Motivation
Expressing a problem as a CSP yields reusable machinery:
-
Inference (node/arc/path consistency) to shrink domains before or during search.
-
Heuristics (MRV, degree, least-constraining value) to prioritize promising choices.
-
Decomposition (tree/low-treewidth structures) to enable polynomial-time solutions on special cases.
-
Local search strategies that scale on large instances where backtracking stalls.
CSPs separate what must be satisfied from how to find it, improving clarity and reuse across domains.
Definition and Formalism
A CSP is a triple ((X, D, C)):
-
(X = {X_1, \dots, X_n}) variables.
-
(D = {D_1, \dots, D_n}), where (D_i) is the finite domain for (X_i).
-
(C = {C_1, \dots, C_m}) constraints; each scope (S_k \subseteq X) with a relation (R_k \subseteq \prod_{X_i \in S_k} D_i) of satisfying tuples.
A (partial) assignment (\theta) maps a subset of variables to values in their domains. (\theta) is consistent if it does not violate any constraint whose scope is fully assigned; it is complete if all variables are assigned. A solution is a complete, consistent assignment.
-
Constraint graph: nodes are variables; an edge ((X_i, X_j)) indicates any binary constraint involving the pair (or co-membership in a higher-arity constraint’s scope when forming a primal graph).
-
Factor graph: bipartite graph with variable nodes and constraint nodes.
Tip
When constraints are binary, the primal graph captures all interactions; use factor graphs for higher-arity constraints to avoid losing scope information.
Example or Illustration
Map coloring (4-coloring): Variables are regions (X_i); each (D_i = {\text{red, green, blue, yellow}}). For each adjacent pair ((X_i, X_j)), add a constraint (X_i \ne X_j). A valid coloring is a solution.
Sudoku (9×9): Variables (X_{r,c}) for cells; each domain ({1..9}) (restricted by clues). Constraints enforce all-different per row, column, and 3×3 block.
Properties and Relationships
-
Tractability via structure:
-
Trees: If the primal graph is a tree, dynamic programming solves the CSP in linear time in the number of variables (and polynomial in domain size).
-
Low treewidth: Bounded-treewidth graphs are solvable in time exponential in treewidth but polynomial in problem size.
-
-
Consistency notions:
-
Node consistency (NC): Remove domain values that violate unary constraints.
-
Arc consistency (AC): For every binary constraint (X_i \leftrightarrow X_j), each value (a \in D_i) must have some support (b \in D_j) so that ((a,b)) satisfies the constraint.
-
Path / k-consistency: Generalize support across longer scopes.
-
-
Global constraints: Compact, powerful constraints like all-different, cumulative, and element have specialized propagation algorithms stronger than decomposing them into binaries.
Warning
Propagation ≠ completeness. Enforcing arc consistency can leave a CSP with non-empty domains that is still unsatisfiable. Propagation prunes; search decides.
Implementation or Practical Context
Backtracking with Forward Checking & MRV
Use backtracking to build a consistent assignment while pruning with forward checking (remove values from unassigned neighbors inconsistent with the chosen value) and MRV (minimum-remaining-values) for variable ordering, plus degree and least-constraining value (LCV) tie-breakers.
function BACKTRACK(assignment):
if all variables assigned: return assignment
X = argmin_{unassigned} |D_X| // MRV
break ties by max degree in constraint graph // DEGREE
for v in order_by_LCV(X, assignment): // value that rules out fewest neighbor values first
if consistent(X = v, assignment):
assignment[X] = v
removed = FORWARD_CHECK(X, v) // prune neighbors' domains
result = BACKTRACK(assignment)
if result ≠ failure: return result
UNDO(removed) // restore domains
remove assignment[X]
return failureForward checking runs arc revision from ((X, \cdot)) to its neighbors after assigning (X=v).
Arc Consistency (AC-3)
AC-3 enforces arc consistency on binary CSPs by iteratively revising arcs until no more values can be removed.
function AC3(csp):
Q = queue of all directed arcs (Xi → Xj) where constraint exists
while Q not empty:
(Xi, Xj) = pop(Q)
if REVISE(Xi, Xj): // remove unsupported values from D_Xi
if D_Xi is empty: return inconsistent
for each Xk in neighbors(Xi) \ {Xj}:
push(Q, (Xk, Xi))
return consistent
function REVISE(Xi, Xj):
revised = false
for a in D_Xi:
if no b in D_Xj such that (a,b) satisfies Cij:
remove a from D_Xi
revised = true
return revisedGlobal Constraints & Stronger Propagation
-
All-different: Use matching-based propagation (Hall intervals) to remove domain values implied by pigeonhole constraints.
-
Cumulative (scheduling): Propagate resource usage over time windows to prune impossible start times.
Local Search (Min-Conflicts)
For large, nearly satisfiable CSPs, min-conflicts repeatedly picks a conflicted variable and assigns a value that minimizes conflicts with neighbors.
function MIN_CONFLICTS(max_steps):
A = random complete assignment
for step in 1..max_steps:
if A satisfies all constraints: return A
X = random variable that participates in a conflict
v = argmin_{d in D_X} conflicts(X=d, A)
A[X] = v
return failureWorks spectacularly on n-queens and many scheduling/timetabling instances.
Decomposition & Dynamic Programming
-
Trees: Choose a root, do a pass from leaves up computing compatible summaries (supports) and a down pass to recover an assignment.
-
Treewidth: Build a tree decomposition; perform join-tree propagation (bucket elimination / junction trees).
Tip
Combine AC-3 pre-processing with MRV+LCV and forward checking during search. Pre-processing shrinks domains; online pruning keeps the frontier small.
Common Misunderstandings
-
“Arc consistency finds solutions.” It only prunes; finding a solution still needs search or specialized propagation strong enough to force singletons.
-
“Bigger domains are always harder.” Sometimes adding values enables more consistent pairings, allowing stronger propagation. Hardness depends on constraint tightness and graph structure, not just domain size.
-
“Local search can’t solve CSPs with many constraints.” Min-conflicts excels when a nearby satisfying assignment exists; restarts and randomization help escape local minima.
Broader Implications
CSP techniques appear in SAT/SMT (via encodings or direct propagators), AI planning (action as variables, preconditions/effects as constraints), type inference (constraints over types), and configuration systems (feature models). The same ideas—propagation, heuristic search, decomposition—recur across discrete optimization.
Summary
CSPs model decision problems as variables, domains, and constraints. Core tools include:
-
Propagation (NC/AC/stronger global propagators) to cut domains,
-
Heuristic backtracking (MRV, degree, LCV, forward checking) to explore efficiently,
-
Local search (min-conflicts) for fast improvements on large instances,
-
Decomposition (trees/treewidth) for structural tractability.
Effective CSP solving balances inference strength against search effort, guided by problem structure.