A Constraint Satisfaction Problem (CSP) is a formalism where a solution must assign values to a set of variables such that all constraints are satisfied simultaneously. CSPs model a vast range of real-world problems: scheduling, map coloring, Sudoku, circuit layout, and resource allocation. The formal definition: a CSP is a triple (X, D, C) where X = {X₁, …, Xₙ} is a set of variables, D = {D₁, …, Dₙ} assigns a domain of possible values to each variable, and C is a set of constraints each restricting allowable combinations of variable values. GATE DA tests backtracking, constraint propagation (AC-3), and variable/value ordering heuristics.
CSP formal structure
A CSP is characterized by three components. Variables X = {X₁, X₂, …, Xₙ} are the unknowns. Domains D = {D₁, D₂, …, Dₙ} where each Dᵢ is the finite set of values Xᵢ can take. Constraints C = {C₁, C₂, …, Cₘ} where each constraint Cⱼ specifies allowable combinations of values for a subset of variables (its scope).
| CSP Component | Map Coloring Example | Sudoku Example |
|---|---|---|
| Variables | Regions: WA, NT, SA, Q, NSW, V, T | The 81 cells of the 9×9 grid |
| Domains | D = {Red, Green, Blue} for every variable | D = {1, 2, 3, 4, 5, 6, 7, 8, 9} for every blank cell |
| Constraints | Adjacent regions must have different colors | Each row, column, 3×3 box must have each digit exactly once |
| Solution | Complete assignment satisfying all constraints | Filled grid with all constraints satisfied |
Constraint types
Unary constraint: restricts a single variable (e.g., X₁ ≠ Red). Binary constraint: between two variables (e.g., X₁ ≠ X₂) — the most common type. Higher-order: involves 3+ variables. Global constraint: involves all variables (e.g., AllDifferent in Sudoku). GATE focuses on binary constraints and the constraint graph.
Backtracking Search
Naive generate-and-test (enumerate all n^d combinations) is exponential and ignores constraint structure. Backtracking search interleaves assignment and constraint checking: it assigns variables one at a time and immediately backtracks when any constraint is violated, pruning the search tree.
Backtracking CSP solver — the core algorithm
def backtracking_search(csp):
return backtrack({}, csp)
def backtrack(assignment, csp):
# If all variables assigned, return solution
if len(assignment) == len(csp.variables):
return assignment
# Select an unassigned variable (heuristic applied here)
var = select_unassigned_variable(assignment, csp)
for value in order_domain_values(var, assignment, csp):
if csp.consistent(var, value, assignment):
assignment[var] = value
# Propagate constraints (forward checking / AC-3)
inferences = inference(csp, var, value, assignment)
if inferences is not None:
assignment.update(inferences)
result = backtrack(assignment, csp)
if result is not None:
return result
# Remove inferences
for inf_var in inferences:
del assignment[inf_var]
del assignment[var]
return None # failure — triggers backtrackKey improvements over naive backtracking come from three sources: (1) Variable ordering heuristics — which variable to assign next. (2) Value ordering heuristics — which value to try first. (3) Inference / constraint propagation — detect failures early before all variables are assigned.
Variable and Value Ordering Heuristics
The order in which variables are assigned profoundly affects search efficiency. Choosing the wrong variable can lead to exponential blowup; the right choice prunes the tree dramatically.
| Heuristic | Rule | Intuition | Type |
|---|---|---|---|
| MRV (Minimum Remaining Values) | Choose the variable with the fewest legal values in its current domain | Fail-first: catch failures early, prune dead ends quickly | Variable ordering |
| Degree Heuristic | Choose the variable involved in the most constraints with unassigned variables | Tiebreaker for MRV: choose most constrained variable | Variable ordering (tiebreaker) |
| LCV (Least Constraining Value) | Choose the value that rules out the fewest values in neighboring variables' domains | Fail-last: keep maximum flexibility for remaining variables | Value ordering |
MRV + LCV combination
The standard GATE answer for "best CSP heuristic combination": use MRV for variable selection (fail-first, prune early) and LCV for value selection (fail-last, maximize remaining options). They are complementary: MRV detects the most likely failures, LCV delays failures by choosing values that preserve the most flexibility.
Constraint Propagation: Forward Checking & AC-3
Constraint propagation detects infeasibility before assignment is complete, dramatically pruning the search tree. Two key techniques are tested in GATE:
Forward Checking: After assigning variable Xᵢ = v, remove from the domains of all unassigned neighboring variables Xⱼ any values inconsistent with v. If any domain becomes empty, immediately backtrack — this partial assignment cannot lead to a solution.
Arc Consistency (AC-3): A variable Xᵢ is arc-consistent with respect to Xⱼ if for every value x in Dᵢ, there exists some value y in Dⱼ satisfying the constraint between Xᵢ and Xⱼ. AC-3 makes all arcs in the constraint graph arc-consistent by propagating domain reductions through a queue.
Arc consistency condition: every value in Dᵢ has at least one compatible value in Dⱼ. If this fails for some x ∈ Dᵢ, remove x from Dᵢ (domain reduction).
AC-3 algorithm — O(cd³) where c = number of arcs, d = domain size
from collections import deque
def ac3(csp):
"""AC-3 arc consistency algorithm.
Returns False if an inconsistency is found; True otherwise.
Modifies csp.domains in place (domain reduction)."""
queue = deque(csp.all_arcs()) # all (Xi, Xj) pairs
while queue:
Xi, Xj = queue.popleft()
if revise(csp, Xi, Xj):
if len(csp.domains[Xi]) == 0:
return False # domain wipeout → inconsistency
# Xi's domain changed; re-check all arcs pointing INTO Xi
for Xk in csp.neighbors[Xi]:
if Xk != Xj:
queue.append((Xk, Xi))
return True # all arcs consistent
def revise(csp, Xi, Xj):
"""Remove values from Di that have no support in Dj.
Returns True if Di was reduced."""
revised = False
for x in list(csp.domains[Xi]):
# x has no support in Dj → remove it
if not any(csp.consistent(Xi, x, Xj, y) for y in csp.domains[Xj]):
csp.domains[Xi].remove(x)
revised = True
return revised| Technique | What it does | Complexity | Completeness |
|---|---|---|---|
| Forward Checking | Removes inconsistent values from direct neighbors of newly assigned variable | O(d²) per assignment | Detects failures involving the current variable only |
| AC-3 | Full arc consistency — propagates reductions through the entire graph | O(cd³): c arcs, d domain size | Detects all binary arc inconsistencies; may miss higher-order issues |
| Backtracking alone | Only checks constraints when all variables in scope are assigned | O(dⁿ) worst case | Complete but inefficient |
The Constraint Graph
The constraint graph has one node per variable and an edge between two nodes for every binary constraint between them. The graph structure reveals algorithmic opportunities: if the constraint graph is a tree (no cycles), the CSP can be solved in O(nd²) time using a linear-time algorithm — exponentially faster than general backtracking.
Tree-structured CSPs are tractable: solve by topological sort then arc-consistency pass from leaves to root, then assign root-to-leaves. Any value consistent at each step is guaranteed globally consistent.
Cutset conditioning
For nearly-tree-structured graphs: find a cycle cutset — a small set S of variables whose removal leaves a tree. Try all |D|^|S| assignments to S; for each, solve the remaining tree CSP in O(nd²). Total: O(d^c · nd²) where c = cutset size. GATE tests this as the connection between graph structure and CSP tractability.
GATE PYQ Spotlight
GATE DA — AC-3 domain reduction (2-mark)
A CSP has variables X₁ ∈ {1,2,3}, X₂ ∈ {1,2,3}, X₃ ∈ {1,2,3} with constraints: X₁ < X₂ and X₂ < X₃. After running AC-3, what are the final domains? Step 1: Arc (X₁, X₂) with X₁ < X₂: value 3 has no support in D₂ (nothing > 3), so remove 3 from D₁ → D₁ = {1,2}. Value 1 in D₂ has no x in D₁ with x < 1, so remove 1 from D₂ → D₂ = {2,3}. Step 2: Arc (X₂, X₃) with X₂ < X₃: remove 3 from D₂ (nothing > 3 in D₃), D₂ = {2}. Remove 1,2 from D₃ (need something < 2 in D₂, but D₂={2}), D₃ = {3}. Final: D₁={1,2}, D₂={2}, D₃={3}. Unique solution: X₁=1, X₂=2, X₃=3.
GATE DA — MRV heuristic (1-mark)
In a CSP with 5 variables and current partial assignment, the remaining variable domains are: X₂={a,b}, X₃={c}, X₄={a,b,c,d}, X₅={a,b,c}. Which variable does MRV select next? Answer: X₃ — it has only 1 remaining value, so it is the most constrained. MRV (Minimum Remaining Values) always selects the variable with the fewest legal values in its current domain. With domain size 1, failure (if any) is detected immediately.
GATE DA — Tree-structured CSP complexity (1-mark)
A CSP with n variables each with domain size d has a tree-structured constraint graph. What is the worst-case time complexity to solve it? (A) O(dⁿ) (B) O(nd²) (C) O(n²d) (D) O(nd) Answer: (B) O(nd²). Tree-structured CSPs are solved optimally in O(nd²): topological ordering O(n), then one directed arc-consistency pass O(nd²), then one assignment pass O(nd). The key insight: tree structure eliminates all backtracking.
Practice questions
- What is the difference between forward checking and arc consistency (AC-3) in CSP solving? (Answer: Forward checking: when a variable is assigned, eliminate inconsistent values from UNASSIGNED neighbours only. Checks only one level of arcs. Arc consistency (AC-3): enforce arc consistency across all pairs — for every arc (Xi, Xj), ensure every value in Xi's domain has a consistent value in Xj's domain. Propagates further than forward checking. AC-3 catches failures earlier (before backtracking) but costs more per step. FC is a cheaper partial form of arc consistency.)
- What is the minimum remaining values (MRV) heuristic and why does it help CSP performance? (Answer: MRV (Fail-First): choose the variable with the FEWEST legal values remaining in its domain as the next to assign. Intuition: variables likely to fail (few remaining values) are tried first — failures are detected early before wasting effort on other variables. Mathematically: MRV minimises the expected search tree size by exposing contradictions early. Compared to random variable ordering, MRV can reduce search by orders of magnitude on structured CSPs (e.g., n-queens, scheduling).)
- What is the degree heuristic for tie-breaking in CSP variable selection? (Answer: When multiple variables have the same minimum remaining values (MRV tie), use the degree heuristic: choose the variable with the most constraints on other UNASSIGNED variables. High-degree variables are likely to constrain others more — removing them early reduces branching factor in subsequent choices. Degree heuristic is a tie-breaker for MRV, not a primary ordering. Together: MRV + degree heuristic significantly outperforms random variable ordering on benchmark CSPs.)
- What is the least constraining value (LCV) heuristic and how does it differ from MRV? (Answer: MRV selects which VARIABLE to assign next (fail-first). LCV selects which VALUE to assign to the chosen variable (succeed-first). LCV: choose the value that rules out the fewest choices for neighbouring unassigned variables. Intuition: LCV tries the most promising value first — if this assignment leads to a solution, we find it faster. If it fails, we still need to backtrack. MRV and LCV have opposite philosophies: MRV finds failures fast; LCV finds solutions fast.)
- What is the difference between a CSP and a constraint optimisation problem (COP)? (Answer: CSP: find ANY assignment that satisfies all constraints — a feasibility problem. Solution is valid or invalid. COP: find an assignment that satisfies all constraints AND MAXIMISES (or minimises) an objective function — an optimisation problem. Examples: CSP — solve a sudoku (any valid completion). COP — schedule nurses to shifts satisfying all constraints while minimising overtime cost. COP algorithms: branch-and-bound with constraint propagation, local search (simulated annealing, genetic algorithms), integer programming.)
On LumiChats
CSP algorithms underlie many real-world AI applications: scheduling meeting rooms, generating valid timetables, configuring product options — problems LumiChats might help automate. The constraint propagation ideas also appear in modern neural architecture search and AutoML.
Try it free