Boolean Satisfiability and DPLL: The Core Algorithm
Encode CSP constraints as SAT clauses, then trace the Davis-Putnam-Logemann-Loveland algorithm — unit propagation, pure-literal elimination, and recursive splitting — step by step on small instances.
- Estimated time
- ~16 min
- Difficulty
- intermediate
- Sources
- 6 sources
Every time your GPS finds the fastest route, a chip verifier proves a microprocessor correct, or a package manager resolves dependencies without conflict, an algorithm is deciding whether a giant system of Boolean constraints can all be true simultaneously — and if so, how. That algorithm, at its heart, is almost always DPLL.
Coming from the previous lesson?
In “Constraint Satisfaction: Variables, Domains, and Backtracking” you saw how a CSP is defined as a triple (X, D, C), traced backtracking on 4-Queens, and used arc consistency to prune domains. This lesson reframes those same constraints as Boolean clauses, then shows you the algorithm that modern solvers — including the CP-SAT solver used in the school timetabling lesson — actually use under the hood.
What Is Boolean Satisfiability?
Picture a padlock with three numbered dials: A, B, C. Each dial is binary — it can only point to 0 (False) or 1 (True). The lock opens only when a specific combination satisfies a set of rules written on the back of the box:
- Rule 1: At least one of A or B must be True.
- Rule 2: If A is True, then C must be True.
- Rule 3: B and C cannot both be True.
Can you open the lock? If so, what combination works?
This is Boolean Satisfiability (SAT): given a set of Boolean variables and a set of constraints (clauses) over them, find an assignment of True/False to each variable that satisfies every clause — or determine that none exists.
Given a set of Boolean variables and a formula in Conjunctive Normal Form (CNF) — a conjunction (AND) of clauses, each clause being a disjunction (OR) of literals — determine whether there exists a truth assignment that makes the formula True.
CNF is the universal language. Every Boolean constraint can be rewritten as a conjunction of clauses. A literal is either a variable (positive literal, e.g. A) or its negation (negative literal, e.g. ¬A). A clause is an OR of literals. The whole formula is an AND of clauses.
Our padlock rules above translate directly:
This is a 3-clause CNF over three variables. Any assignment that makes all three clauses True simultaneously is a satisfying assignment.
Checking an assignment by hand
Try A = True, B = False, C = True:
- (A ∨ B) = (T ∨ F) = T ✓
- (¬A ∨ C) = (F ∨ T) = T ✓
- (¬B ∨ ¬C) = (T ∨ F) = T ✓
All three clauses are satisfied. This assignment opens the lock.
Try A = True, B = True, C = True:
- (A ∨ B) = T ✓
- (¬A ∨ C) = (F ∨ T) = T ✓
- (¬B ∨ ¬C) = (F ∨ F) = F ✗
Rule 3 fails. This combination does not work.
Check your understanding
Which of the following is a valid CNF clause?
From CSP Constraints to SAT Clauses
In the previous lesson you encoded the 4-Queens problem as a CSP: variables Q1–Q4 (column of each queen), domain 4, and binary not-attack constraints. How does that become SAT?
The key idea: replace each (variable, value) pair with a Boolean indicator variable, then write clauses that enforce the original CSP semantics.
Analogy — CSP constraint (Q1 ≠ Q2) is like SAT clause set
For 4-Queens with columns 1–4, introduce Boolean variable meaning “queen i is in column c.” Then:
- At-least-one: each queen must be in some column.
- At-most-one: each queen can be in only one column.
- No-attack: two queens in the same column or diagonal are forbidden.
Encoding 'Q1 ≠ Q2' (same column forbidden) as clauses
Suppose only columns 1 and 2 exist (simplified). Variables: x₁₁, x₁₂ (queen 1 in col 1 or 2) and x₂₁, x₂₂ (queen 2 in col 1 or 2).
“At least one column per queen”: and
“Not both in column 1”:
“Not both in column 2”:
These four clauses, in CNF, fully capture the constraint that Q1 ≠ Q2 (same column). The pattern generalises to all pairs and all columns.
This encoding is called the direct/naive encoding. Modern SAT-based CSP solvers use more compact encodings (commander encoding, ladder encoding), but the principle is the same: every domain value becomes a Boolean, every constraint becomes a clause set. [Handbook of Satisfiability (2nd ed.)]
| CSP | SAT / CNF | |
|---|---|---|
| Variable | X with domain D | One Boolean per (variable, value) pair |
| Constraint | Relation over variable tuples | One or more clauses |
| Solution | Assignment satisfying all constraints | Assignment making CNF True |
| Backtracking | Branch on variable value | Branch on literal True/False |
| Arc consistency | Prune domain values | Unit propagation in SAT |
Why bother with SAT?
SAT solvers have been engineered for decades with heuristics (VSIDS), clause learning (CDCL), and restarts tuned for industrial instances with millions of variables. Encoding a CSP into SAT and running a modern solver often beats a hand-written CSP solver on the same problem. CP-SAT itself uses a SAT backbone for this reason.
Check your understanding
If a CSP has 10 variables each with domain size 5, how many Boolean indicator variables does the direct encoding introduce?
The DPLL Algorithm
Named after Davis, Putnam, Logemann, and Loveland (1960–62), DPLL is a systematic backtracking search over truth assignments, accelerated by two powerful inference rules that collapse large parts of the search tree before you even branch. [A Machine Program for Theorem-Proving (DPLL original paper)]
A complete, sound, backtracking-based algorithm for SAT. It selects a variable, assigns it, simplifies the formula, and recurses. Two inference rules — unit propagation and pure-literal elimination — are applied eagerly at each node to collapse forced assignments before branching.
DPLL has three interlocking rules:
Rule 1 — Unit Propagation (UP): If a clause contains exactly one unassigned literal (a unit clause), that literal must be True (otherwise the clause is immediately falsified). Assign it and simplify. This often cascades — one forced assignment creates new unit clauses.
Rule 2 — Pure Literal Elimination (PLE): If a variable appears in the formula with only one polarity (always positive, or always negative, across all remaining unsatisfied clauses), assigning it to satisfy those clauses can only help, never hurt. Assign it to make those clauses true.
Rule 3 — Splitting (Branching): If neither rule fires, pick an unassigned variable and branch: try True in one recursive call, False in the other. If the True branch fails, backtrack and try False.
flowchart TD
A([Start: CNF formula F, assignment α]) --> B{All clauses satisfied?}
B -- Yes --> SAT([Return SAT with α])
B -- No --> C{Any clause empty?}
C -- Yes --> UNSAT([Return UNSAT — backtrack])
C -- No --> D{Unit clause exists?}
D -- Yes --> E[Unit Propagation: force literal l, simplify F]
E --> A
D -- No --> F{Pure literal exists?}
F -- Yes --> G[Pure Literal Elimination: assign variable, simplify F]
G --> A
F -- No --> H[Pick unassigned variable x]
H --> I[Try x = True → recurse]
I -- SAT --> SAT
I -- UNSAT --> J[Try x = False → recurse]
J --> AShow the formal recursive pseudocode
function DPLL(F, α):
if all clauses in F are satisfied by α: return SAT(α)
if some clause in F is empty: return UNSAT
if F has a unit clause {l}:
return DPLL(simplify(F, l), α ∪ {lit(l) = True})
if F has a pure literal l:
return DPLL(simplify(F, l), α ∪ {lit(l) = True})
x ← pick_unassigned_variable(F)
if DPLL(simplify(F, x), α ∪ {x = T}) = SAT: return SAT
return DPLL(simplify(F, ¬x), α ∪ {x = F})simplify(F, l) removes every clause containing l (they are now satisfied) and removes ¬l from every remaining clause (that literal is now forced False).
Tracing DPLL by Hand
Let us trace DPLL on our padlock formula from the opening section:
Step 0 — Initial state. Three clauses, no assignments. No unit clause (each clause has 2 literals). Check for pure literals: A appears positive and negative (in clauses 1 and 2), B appears positive and negative (clauses 1 and 3), C appears positive and negative (clauses 2 and 3). No pure literal either. We must split.
Step 1 — Split on A = True. Simplify:
- (A ∨ B): contains A (True) → satisfied, remove.
- (¬A ∨ C): ¬A is False, remove it → remaining clause: (C) — a unit clause!
- (¬B ∨ ¬C): unchanged.
Remaining formula: (C) ∧ (¬B ∨ ¬C)
Step 2 — Unit propagation: C = True. (C) is a unit clause, force C = True. Simplify:
- (C): satisfied, remove.
- (¬B ∨ ¬C): ¬C is False, remove it → remaining: (¬B) — another unit clause!
Remaining formula: (¬B)
Step 3 — Unit propagation: B = False. (¬B) forces B = False. All clauses are now satisfied.
Result: SAT with A = True, C = True, B = False. ✓
Full DPLL trace on the padlock formula
| Step | Rule | Assignment added | Formula state |
|---|---|---|---|
| 0 | Split on A | A = T | (C) ∧ (¬B ∨ ¬C) |
| 1 | Unit propagation | C = T | (¬B) |
| 2 | Unit propagation | B = F | (empty — all satisfied) |
| — | SAT | A=T, B=F, C=T | ✓ |
Notice we never had to backtrack. Unit propagation cascaded through the entire problem once we committed to A = True.
Common misconception
DPLL always needs to explore both branches of every split.
What's actually true
Unit propagation frequently collapses entire sub-trees without branching. In the trace above, one split on A immediately triggered two cascaded unit propagations that resolved the entire formula — no backtracking needed. In practice, strong unit propagation means DPLL explores far fewer branches than naive enumeration.
Now step through the algorithm yourself on several different formulas using the interactive widget below.
Check your understanding
In the padlock trace above, what forced B = False without any branching on B?
Why DPLL Matters — The Foundation of Modern Solvers
DPLL from 1962 is not, by itself, the solver inside CP-SAT. But it is the architectural skeleton on which every modern SAT solver is built. Understanding DPLL gives you the vocabulary to understand everything that came after.
-
1960
Davis–Putnam procedure
Resolution-based procedure. Exponential memory; rarely used today.
-
1962
DPLL
Davis, Logemann, Loveland replace resolution with backtracking + unit propagation. Memory-linear. Still the canonical algorithm.
-
1996–99
GRASP / CDCL
Marques-Silva & Sakallah introduce Conflict-Driven Clause Learning. Each conflict generates a new clause that prevents revisiting the same bad decision. Industrial SAT is born.
-
2001–05
MiniSat, zChaff, Glucose
Modern CDCL solvers dominate SAT competitions. Millions of variables, real-time performance.
-
2011–now
CP-SAT (OR-Tools)
Google's CP-SAT solver uses a CDCL SAT core augmented with CP propagators. Routinely handles scheduling problems with tens of thousands of variables.
The jump from DPLL to industrial CDCL solvers has two key ingredients:
- Clause learning: when a branch fails, analyse why it failed and add a new clause capturing that conflict. Future searches avoid the same dead end without re-exploring it.
- Non-chronological backjumping: instead of always undoing the most recent decision, jump back to the earliest decision that actually caused the conflict.
The SAT solver inside CP-SAT is a custom CDCL implementation called Glucose-style SAT augmented with lazy clause propagation and watched literals — a data structure trick that makes unit propagation run in near-linear time on large formulas. Every time CP-SAT searches a timetabling constraint, it is running millions of unit propagations per second. [CP-SAT Solver Documentation — OR-Tools]
| DPLL (1962) | CDCL (1996+) | |
|---|---|---|
| Core search | Backtracking with UP + PLE | Backtracking with UP + PLE + clause learning |
| On conflict | Chronological backtrack | Analyse conflict, learn clause, non-chronological backjump |
| Formula size | Grows only by decisions | Grows by learned clauses (managed by deletion heuristics) |
| Practical scale | Hundreds of variables | Millions of variables |
| Completeness | Complete | Complete |
Check your understanding
Which property makes DPLL (and CDCL) complete — guaranteed to find a solution or prove none exists?
Check your understandingQ 1 / 4
A CNF formula has three clauses: (A), (¬A ∨ B), (¬B). What does DPLL conclude after applying unit propagation?