Cook–Levin tableau core #
This file defines the Cook–Levin tableau formula and proves its semantic
correctness. The polynomial-time emitter, reductions, and final theorem
SAT.NPComplete_language are assembled in SAT/CookLevin/Assembly.lean.
Membership language ∈ NP is supplied by SAT/Headline.lean.
Completed development #
NPComplete_language (= ⟨language_mem_NP, NPHard_language⟩)
└ NPHard_language (unpack any L ∈ NP → its NTM)
└ cookLevin_reduction (multi-tape → single-tape, then ↓)
├ NTM.exists_singleTape_decidesInTime (SingleTape.lean)
└ cookLevin_reduction_singleTape
├ reductionFn (def: x ↦ (tableauCNFFlat …).encode)
├ reductionFn_mem_FP ✓ poly-time emitter TM
└ tableauCNF_correct (= encode_mem_LSAT_iff ∘ B ∘ hdec)
├ tableauCNF ✓ the tableau formula
├ tableauCNF_satisfiable_iff ✓ sat ↔ accepting computation
└ encode_mem_LSAT_iff ✓ (CNF.encode injective)
All nodes in this outline are proved. The reduction machine and final
NP-hardness/NP-completeness assembly live in SAT/CookLevin/Assembly.lean;
the emitter implementation is split across the modules under
SAT/CookLevin/.
Tableau variable encoding #
The computation-tableau formula's Boolean variables are indexed by ℕ. Each
"atom" of the tableau — a state bit, a nondeterministic choice bit, a tape-cell
bit, or a head-position bit, all indexed by a time-step — is injected into ℕ
by iterated Nat.pair, so distinct atoms receive distinct SAT variables.
Variable: at time t the machine is in the state with index q (one-hot).
Equations
- Complexity.SAT.Tableau.vState t q = Complexity.SAT.Tableau.enc 0 t q 0 0
Instances For
Variable: at time t the nondeterministic choice bit is true.
Equations
Instances For
Variable: at time t, cell pos of tape tp holds the symbol with index s.
Equations
- Complexity.SAT.Tableau.vCell t tp pos s = Complexity.SAT.Tableau.enc 2 t tp (Nat.pair pos s) 0
Instances For
Variable: at time t, the head of tape tp is at cell pos.
Equations
- Complexity.SAT.Tableau.vHead t tp pos = Complexity.SAT.Tableau.enc 3 t tp pos 0
Instances For
The symbol index of a tape symbol: 0,1,2,3 for 0,1,□,▷. Injective, so a
one-hot encoding over {0,1,2,3} faithfully names the four tape symbols.
Equations
Instances For
symIdx is injective: distinct tape symbols receive distinct indices.
"At most one of vars is true": for every ordered pair (vᵢ, vⱼ) the binary
clause ¬vᵢ ∨ ¬vⱼ.
Equations
Instances For
"Exactly one of vars is true" as a list of clauses (at-least-one and the
pairwise at-most-one constraints).
Equations
Instances For
The at-least-one clause is satisfied iff some variable in the list is true.
The at-least-one clause is satisfied iff some listed variable is true (Prop form).
The exactly-one clauses are satisfied iff exactly one listed variable is true (some variable is true, and no two are). The decoder for one-hot slots.
Under the at-most-one (pairwise) constraint, any two true listed variables coincide — the uniqueness half of one-hot decoding.
An implication clause cond ++ [conseq] whose cond literals are all false is
satisfied only if its consequent literal is true. (Each active-transition clause
is of this shape; when the read-config matches, the consequence is forced.)
A positive unit clause [v] is satisfied iff its variable is true.
CNF.eval of a map-built clause list: every mapped clause holds.
CNF.eval of a flatMap-built clause list: every sub-CNF holds.
Index of a machine state as a natural, via the canonical Fintype enumeration
of N.Q; injective, so a one-hot encoding over Fin (card Q) names the states.
Equations
- Complexity.SAT.Tableau.stateIdx N q = ↑((Fintype.equivFin N.Q) q)
Instances For
stateIdx is injective: distinct machine states receive distinct indices.
The symbol at position pos of tape tp in the start configuration on input
x: cell 0 is always ▷; tape 0 (the input) carries x at cells 1…|x|;
every other cell is blank. Mirrors Tape.init applied to each tape.
Equations
- Complexity.SAT.Tableau.initCellSym x tp pos = if pos = 0 then Complexity.Γ.start else if tp = 0 then (List.map Complexity.Γ.ofBool x)[pos - 1]?.getD Complexity.Γ.blank else Complexity.Γ.blank
Instances For
One-hot constraint that every cell (each tape 0…k+1, position 0…P, time
0…steps) holds exactly one of the four symbols.
Equations
- One or more equations did not get rendered due to their size.
Instances For
One-hot constraint that every tape head (each tape 0…k+1, time 0…steps) is
at exactly one position in 0…P.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Unit clauses fixing the start configuration at time 0: state qstart, every
head at cell 0, and every cell holding its initCellSym value.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Unit clauses fixing acceptance at time steps: the state is qhalt and cell
1 of the output tape (index k+1) holds 1.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Transition frame. A cell not under its tape's head keeps its symbol from
time t to t+1. For each tape/position/symbol, the two clauses encode
¬(head at pos) → (cellₜ = cellₜ₊₁) (a head literal disjoined with each
direction of the ↔). The complementary "active" clauses (cell under the head
updated per N.δ) are supplied separately.
Equations
- One or more equations did not get rendered due to their size.
Instances For
New head position after a move (mirrors Tape.move): left decrements
(clamped at 0 by Nat subtraction), right increments, stay keeps.
Equations
Instances For
The four tape symbols, enumerated.
Equations
Instances For
The shared "read-config" condition literals (all negated) of one transition
tuple: state q, the three heads at pi/pw/po reading si/sw/so, and
choice b. When α exhibits exactly this read-config every literal is false.
Equations
- One or more equations did not get rendered due to their size.
Instances For
The seven transition clauses for one read-config + choice tuple at time t,
encoding N.trace's step: if q = qhalt the configuration stays (the
machine has halted), otherwise the next state is out.1 (out := N.δ b q …),
the work/output cells under their heads become out's writes, and the three
heads move per out (the input cell is read-only).
Equations
- One or more equations did not get rendered due to their size.
Instances For
Active transition clauses — activeClausesAt for every time t < steps,
state q, the three head positions/read symbols, and choice bit b. Together
with the frame clauses these enforce c_{t+1} = step c_t (choice t).
Equations
- One or more equations did not get rendered due to their size.
Instances For
The acceptance clauses hold iff the final state is qhalt and output cell 1
holds 1 (the two facts witnessing an accepting halt).
The start clauses hold iff time-0 is the start configuration: state qstart,
every head (tapes 0,1,2) at cell 0, and every cell holding its
initCellSym value.
The frame clauses hold iff, whenever a tape head is not at a position, that
cell keeps its symbol from time t to t+1.
The state one-hot clauses hold iff every time-step satisfies its exactly-one state constraint.
The cell one-hot clauses hold iff every cell satisfies its exactly-one symbol constraint.
The head one-hot clauses hold iff every tape head satisfies its exactly-one position constraint.
The active transition clauses hold iff, for every read-config + choice tuple,
its activeClausesAt block holds (which says: if α exhibits that read-config
at time t, then time t+1 is the N.δ-step).
When α exhibits the read-config of a transition tuple — state q, the heads
at pi/pw/po reading si/sw/so, and choice b — every literal of
activeCond is false (so each transition clause forces its consequence).
Computation-tableau formula. tableauCNF N steps x is the CNF that is
satisfiable exactly when the (single-work-tape) machine N has an accepting
computation on input x within steps steps — variables encode the tape /
head / state contents at each time-step together with the nondeterministic
choice bits, and clauses enforce the start configuration, per-step transition
validity, and acceptance.
Equations
- One or more equations did not get rendered due to their size.
Instances For
The tableau is satisfied (by α) exactly when all seven clause families are —
the bridge from tableauCNF to the per-family characterizations.
α represents configuration c at time t (positions bounded by P): the
one-hot state/cell/head variables that hold of c are all set true in α. The
invariant carried through the backward (sat → accepts) direction.
Equations
- One or more equations did not get rendered due to their size.
Instances For
One step of N.trace from configuration c with choice bit b: stays if
halted, otherwise applies N.δ. (N.trace (t+1) is this applied to N.trace t.)
Equations
- One or more equations did not get rendered due to their size.
Instances For
Base case: a satisfying assignment for the start clauses represents the initial
configuration at time 0.
Tape move/writeAndMove cell/head behaviour, used to match traceStep's
fields against the active-transition consequence variables.
Tape.move changes the head exactly as Tableau.posMove computes.
Tape.writeAndMove changes the head exactly as Tableau.posMove computes.
The seven consequence variables of the matching active-transition tuple are all
true: when α represents c at time t, the read-config matches, so each
activeClausesAt clause forces its consequence.
Head-uniqueness: if α satisfies the head one-hot clauses and a head sits at
ha, then every other position's head variable is false (the frame's hypothesis).
Backward inductive step. If α represents c at time t and satisfies the
frame / head-one-hot / active-transition clauses, then it represents the next
configuration traceStep N c (choice t) at time t+1.
A head move lands at most one cell to the right of where it started.
Backward trace correspondence. A satisfying assignment represents the whole
computation: at every time t ≤ steps it represents N.trace t from the start.
Symbol indices are below 4, the number of tape symbols.
State indices are below the number of machine states.
The exactly-one clauses force a unique true variable: two true variables in the list must be the same.
Backward direction. If the tableau formula is satisfiable, N accepts x
within steps steps: a satisfying assignment's choice bits drive an accepting run.
Assignment backed by a function g, truncated to [0, M); reads back as g
on that range. The forward direction builds its satisfying witness this way.
Equations
- Complexity.SAT.assignOf M g = List.map g (List.range M)
Instances For
Every element of a list is bounded by the list's foldr max 0.
Indicator assignment for a finite list of "true" variables: every listed
variable reads true, every other variable reads false. The truncation
length is one past the list's maximum, so listed variables stay in range while
unlisted ones are either out of range or decided false.
Equations
- Complexity.SAT.listAssign l = Complexity.SAT.assignOf (List.foldr max 0 l + 1) fun (i : ℕ) => decide (i ∈ l)
Instances For
listAssign l reads true on every listed variable.
listAssign l reads false on every unlisted variable.
A variable that listAssign l reads as true must be listed.
The variables that hold of the run fcfg (over steps steps, positions ≤ P):
the one-hot state/cell/head variables and the true choice bits. The forward
direction's satisfying assignment marks exactly these true.
Equations
- One or more equations did not get rendered due to their size.
Instances For
A state variable is in ftraceVars iff it names the run's state at an in-range time.
A choice variable is in ftraceVars iff its time is in range and its choice bit is true.
A cell variable is in ftraceVars iff it names the run's symbol at an in-range cell.
A head variable is in ftraceVars iff it names the run's head position at an
in-range time and tape.
The forward direction's satisfying assignment: mark exactly the run's variables.
Equations
- Complexity.SAT.fassign N x g steps P = Complexity.SAT.listAssign (Complexity.SAT.ftraceVars N x g steps P)
Instances For
A one-hot constraint over (range n).map f is satisfied when exactly variable
f k is true (k < n, and f j true forces j = k).
At time 0 every cell of the run holds its initCellSym value.
fassign satisfies the acceptance clauses when the run halts at time steps with
output cell 1 holding 1.
On in-range cell variables, fassign reads true exactly when the symbol index is
the run's symbol at that cell.
fassign satisfies each activeClausesAt block: either the read-config does not match
the run (some condition literal is true), or it does and the consequence variables hold
because the run takes exactly that traceStep.
Forward direction. If N accepts x within steps steps, the tableau is
satisfiable: the assignment marking exactly the accepting run's variables.
Tableau correctness (core). The tableau formula is satisfiable iff N
accepts x within steps steps.
An encoded CNF is in language iff it is satisfiable (CNF.encode is injective,
via CNF.decode?_encode).
Flat variable re-indexing #
The enc-based variable indices are Nat.pair towers — convenient for the
injectivity bookkeeping of the correctness proof, but a Turing machine
computing them would need comparison-and-branch arithmetic. The flat
mixed-radix scheme below needs only multiplication and addition (unary
machine-trivial), and transports satisfiability along the injective
re-encoding flatToEnc (SAT.CNF.satisfiable_mapVar_iff). The reduction
emits the flat formula.
flatToEnc is injective: the mixed-radix components recompose the flat index.
Flat tableau variables: same roles as vState/vChoice/vCell/vHead,
mixed-radix indices.
Equations
- Complexity.SAT.Tableau.vStateF Qc steps P t q = Complexity.SAT.Tableau.flatVar (steps + 1) (max Qc 3) (P + 2) 4 0 t q 0 0
Instances For
Flat tableau variables: same roles as vState/vChoice/vCell/vHead,
mixed-radix indices.
Equations
- Complexity.SAT.Tableau.vChoiceF Qc steps P t = Complexity.SAT.Tableau.flatVar (steps + 1) (max Qc 3) (P + 2) 4 1 t 0 0 0
Instances For
Flat tableau variables: same roles as vState/vChoice/vCell/vHead,
mixed-radix indices.
Equations
- Complexity.SAT.Tableau.vCellF Qc steps P t tp pos s = Complexity.SAT.Tableau.flatVar (steps + 1) (max Qc 3) (P + 2) 4 2 t tp pos s
Instances For
Flat tableau variables: same roles as vState/vChoice/vCell/vHead,
mixed-radix indices.
Equations
- Complexity.SAT.Tableau.vHeadF Qc steps P t tp pos = Complexity.SAT.Tableau.flatVar (steps + 1) (max Qc 3) (P + 2) 4 3 t tp pos 0
Instances For
Variable renaming commutes with atLeastOne.
Variable renaming commutes with exactlyOne.
Flat-variable mirror of oneHotStates.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Flat-variable mirror of oneHotCells (at k = 1).
Equations
- One or more equations did not get rendered due to their size.
Instances For
Flat-variable mirror of oneHotHeads (at k = 1).
Equations
- One or more equations did not get rendered due to their size.
Instances For
Flat-variable mirror of startClauses (at k = 1).
Equations
- One or more equations did not get rendered due to their size.
Instances For
Flat-variable mirror of acceptClauses (at k = 1).
Equations
- One or more equations did not get rendered due to their size.
Instances For
The re-encoding at this tableau's moduli.
Equations
- Complexity.SAT.Tableau.encOf N steps P = Complexity.SAT.Tableau.flatToEnc (steps + 1) (max (Fintype.card N.Q) 3) (P + 2) 4
Instances For
Re-indexing by encOf carries oneHotStatesF to oneHotStates.
Re-indexing by encOf carries oneHotCellsF to oneHotCells.
Re-indexing by encOf carries oneHotHeadsF to oneHotHeads.
Re-indexing by encOf carries acceptClausesF to acceptClauses.
Flat-variable mirror of frameClauses (at k = 1).
Equations
- One or more equations did not get rendered due to their size.
Instances For
Flat-variable mirror of activeTransitionClauses.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Re-indexing by encOf carries startClausesF to startClauses.
Re-indexing by encOf carries frameClausesF to frameClauses.
Re-indexing by encOf carries activeCondF to activeCond (for in-range tuples).
Re-indexing by encOf carries activeClausesAtF to activeClausesAt (for in-range
tuples).
Re-indexing by encOf carries activeTransitionClausesF to activeTransitionClauses.
The flat computation-tableau formula: tableauCNF with mixed-radix
variable indices in place of the Nat.pair-based ones. Satisfiability is
identical (tableauCNFFlat_satisfiable_iff); this is the formula the
reduction machine emits, since its indices need only multiplication and
addition.
Equations
- One or more equations did not get rendered due to their size.
Instances For
The flat tableau is the enc tableau, re-indexed.
Flat-tableau correctness: satisfiability transports along the injective
re-indexing to the proved tableauCNF_satisfiable_iff.
The Cook–Levin reduction function: map each input to the encoding of its (flat-variable) computation-tableau formula.
Equations
- Complexity.SAT.reductionFn N T x = (Complexity.SAT.tableauCNFFlat N (T x.length) x).encode
Instances For
The reduction is correct. x ∈ L iff the reduction output is in language,
combining the tableau characterization with N deciding L.
The reduction machine, its polynomial running time, and the headline
theorems reductionFn_mem_FP, cookLevin_reduction, NPHard_language, and
NPComplete_language live in Complexitylib.SAT.CookLevin.Assembly, built on
the emitter development under Complexitylib.SAT.CookLevin/.