Cook's Turing machines; the classes P, NP, coNP; reductions; NP-completeness; CNF-SAT
DefinitionCookPvsNP_defsThis file sets up the objects of the P versus NP problem exactly as in Cook's official Clay description.
-
Turing machines (Appendix). A machine with non-blank tape symbols (a type) has tape alphabet , a finite state set with decidable equality, an initial state , halting states , and a transition function . A configuration is stored as the state, the left part (reversed), the scanned symbol and the rest of . One step follows Cook's four cases (moving right off the end of scans a fresh blank; moving left off the start of creates a fresh blank). Halting configurations do not change. The computation on starts from (or if is empty). halts within steps if the configuration after steps is halting, and accepts if some iterate is in state .
-
The class P (§1). For an alphabet (a finite type), iff there are a finite , an injection , a machine and such that halts within steps on every , and .
-
Checking relations and NP (§1). For ,
over the alphabet (disjointly tagged). iff there are a finite nonempty , a relation with , and such that for all ,
-
coNP (§2): iff .
-
Polynomial-time computable functions and reductions (Definition 3). is polynomial-time computable if some machine whose finite tape alphabet contains copies of and halts within steps on every input , leaving as the tape content from the head rightwards (trailing blanks removed). iff there is such an with .
-
NP-completeness (Definition 4). is NP-complete iff and for every language over every finite nonempty alphabet .
-
CNF-Satisfiability (§2). A CNF formula is a list of clauses, each a list of literals or (). It is satisfiable if some assignment makes some literal in every clause true. Formulas are written over the four-letter alphabet : a literal is its sign followed by copies of , and every clause is followed by . is the set of codes of satisfiable formulas.
These definitions are shared by every statement of the mission.
Formalization Note Strings are Lists and languages are Set (List Sym). The blank is none : Option Γ. Cook's is modelled as Option (Sym ⊕ Sym₁), and Satisfiability is restricted to CNF formulas.
import Mathlib
/-!
# The P versus NP problem, following S. Cook's Clay Mathematics Institute statement
Definitions for the mission draft. Everything follows S. Cook, *The P versus NP problem*
(official Clay problem description): the one-tape Turing machine of the Appendix, the class `P`
(§1), checking relations and the class `NP` (§1), `coNP` (§2), polynomial-time many-one
reducibility (Definition 3), NP-completeness (Definition 4), and the language Satisfiability (§2),
here in its conjunctive-normal-form version.
Conventions:
* A string over an alphabet `Sym` is a `List Sym`; a language is a `Set (List Sym)`.
* The blank symbol is represented by `none : Option Γ`; the full tape alphabet of a machine whose
non-blank symbols are `Γ` is `Option Γ`, so the blank is automatically distinct from every input
symbol.
-/
namespace CookPvsNP
/-- A language over the alphabet `Sym`: a set of finite strings over `Sym`. -/
abbrev Lang (Sym : Type) : Type := Set (List Sym)
/-- Head movements of a Turing machine: one square to the left (`-1`) or right (`+1`). -/
inductive Move where
| left
| right
deriving DecidableEq
/-- A (deterministic, one-tape) Turing machine in the sense of Cook's Appendix, with non-blank tape
symbols `Γ` (the full tape alphabet is `Option Γ`, where `none` is the blank symbol `b`).
It has a finite state set `Q` with three special states `q₀`, `qaccept`, `qreject`
(the latter two distinct) and a transition function `δ`. The value of `δ` on the two halting
states is never used. -/
structure TM (Γ : Type) where
/-- The finite set of states. -/
Q : Type
[finQ : Fintype Q]
[decQ : DecidableEq Q]
/-- The initial state. -/
q₀ : Q
/-- The accepting state. -/
qaccept : Q
/-- The rejecting state. -/
qreject : Q
accept_ne_reject : qaccept ≠ qreject
/-- The transition function: `δ q s = (q', s', h)` means: in state `q` scanning `s`, print `s'`,
move the head in direction `h`, and enter state `q'`. -/
δ : Q → Option Γ → Q × Option Γ × Move
attribute [instance] TM.finQ TM.decQ
/-- A configuration `x q y` of a machine (Cook's Appendix): the state `q`, the tape contents `x`
to the left of the head (stored in reverse order, nearest square first), the scanned symbol
(the first symbol of `y`), and the rest of `y` to the right of the head. -/
structure Cfg (Γ Q : Type) where
state : Q
left : List (Option Γ)
head : Option Γ
right : List (Option Γ)
namespace TM
variable {Γ : Type} (M : TM Γ)
/-- A configuration is halting if its state is `qaccept` or `qreject`. -/
def IsHalting (c : Cfg Γ M.Q) : Prop := c.state = M.qaccept ∨ c.state = M.qreject
instance (c : Cfg Γ M.Q) : Decidable (M.IsHalting c) := by
unfold IsHalting; infer_instance
/-- One computation step `C → C'` (the four cases of Cook's Appendix); halting configurations are
left unchanged. -/
def step (c : Cfg Γ M.Q) : Cfg Γ M.Q :=
if M.IsHalting c then c else
match M.δ c.state c.head with
| (q', s', Move.right) =>
{ state := q', left := s' :: c.left, head := c.right.headD none, right := c.right.tail }
| (q', s', Move.left) =>
match c.left with
| [] => { state := q', left := [], head := none, right := s' :: c.right }
| a :: x' => { state := q', left := x', head := a, right := s' :: c.right }
/-- The configuration reached after `n` steps (halting configurations are fixed points). -/
def run (n : ℕ) (c : Cfg Γ M.Q) : Cfg Γ M.Q := (M.step)^[n] c
/-- The initial configuration `q₀ w` on input `w` (or `q₀ b` if `w` is empty). -/
def init (w : List Γ) : Cfg Γ M.Q :=
{ state := M.q₀, left := [], head := (w.map some).headD none, right := (w.map some).tail }
/-- `M` halts on input `w` within `n` steps, i.e. `t_M(w) ≤ n`. -/
def HaltsWithin (n : ℕ) (w : List Γ) : Prop := M.IsHalting (M.run n (M.init w))
/-- `M` accepts `w`: the computation on `w` is finite and ends in the state `qaccept`. -/
def Accepts (w : List Γ) : Prop := ∃ n, (M.run n (M.init w)).state = M.qaccept
/-- The output of a configuration: the tape contents from the scanned square rightwards, with the
trailing blanks removed. -/
def output (c : Cfg Γ M.Q) : List (Option Γ) :=
((c.head :: c.right).reverse.dropWhile Option.isNone).reverse
end TM
/-- The class `P` of languages over `Sym` (Cook, §1): `L ∈ P Sym` iff `L = L(M)` for a Turing
machine `M` with input alphabet `Sym` (embedded into the machine's finite tape alphabet by `ι`)
that runs in polynomial time, i.e. there is `k` with `t_M(w) ≤ |w|^k + k` for every input `w`. -/
def P (Sym : Type) [Fintype Sym] : Set (Lang Sym) :=
{ L | ∃ (Γ : Type) (_ : Fintype Γ) (ι : Sym ↪ Γ) (M : TM Γ) (k : ℕ),
(∀ w : List Sym, M.HaltsWithin (w.length ^ k + k) (w.map ι)) ∧
∀ w : List Sym, w ∈ L ↔ M.Accepts (w.map ι) }
/-- The language `L_R = {w#y | R(w, y)}` of a checking relation `R ⊆ Sym* × Sym₁*` (Cook, §1).
It is a language over the alphabet `Option (Sym ⊕ Sym₁)`, where `none` plays the role of the
separator `#` and `inl`/`inr` tag the symbols of `Sym` and `Sym₁`. -/
def checkLang {Sym Sym₁ : Type} (R : List Sym → List Sym₁ → Prop) :
Lang (Option (Sym ⊕ Sym₁)) :=
{ s | ∃ w y, R w y ∧ s = w.map (some ∘ Sum.inl) ++ none :: y.map (some ∘ Sum.inr) }
/-- The class `NP` of languages over `Sym` (Cook, §1): `L ∈ NP Sym` iff there are a finite
nonempty alphabet `Sym₁`, a checking relation `R ⊆ Sym* × Sym₁*` which is polynomial-time
(`L_R ∈ P`), and `k ∈ ℕ` such that for all `w`, `w ∈ L ↔ ∃ y, |y| ≤ |w|^k ∧ R(w, y)`. -/
def NP (Sym : Type) [Fintype Sym] : Set (Lang Sym) :=
{ L | ∃ (Sym₁ : Type) (_ : Fintype Sym₁) (_ : Nonempty Sym₁)
(R : List Sym → List Sym₁ → Prop) (k : ℕ),
checkLang R ∈ P (Option (Sym ⊕ Sym₁)) ∧
∀ w : List Sym, w ∈ L ↔ ∃ y : List Sym₁, y.length ≤ w.length ^ k ∧ R w y }
/-- The class `coNP` of complements of `NP` languages (Cook, §2). -/
def coNP (Sym : Type) [Fintype Sym] : Set (Lang Sym) :=
{ L | Lᶜ ∈ NP Sym }
/-- `f : Sym₁* → Sym₂*` is polynomial-time computable: some Turing machine whose finite tape
alphabet contains both `Sym₁` (via `ι₁`) and `Sym₂` (via `ι₂`) halts on every input `x` within
`|x|^k + k` steps, and its output (tape contents from the head rightwards, trailing blanks
removed) is `f x`. -/
def PolyTimeComputable {Sym₁ Sym₂ : Type} (f : List Sym₁ → List Sym₂) : Prop :=
∃ (Γ : Type) (_ : Fintype Γ) (ι₁ : Sym₁ ↪ Γ) (ι₂ : Sym₂ ↪ Γ) (M : TM Γ) (k : ℕ),
∀ x : List Sym₁,
M.HaltsWithin (x.length ^ k + k) (x.map ι₁) ∧
M.output (M.run (x.length ^ k + k) (M.init (x.map ι₁))) = (f x).map (some ∘ ι₂)
/-- Polynomial-time many-one reducibility `L₁ ≤ₚ L₂` (Cook, Definition 3). -/
def PolyReducible {Sym₁ Sym₂ : Type} (L₁ : Lang Sym₁) (L₂ : Lang Sym₂) : Prop :=
∃ f : List Sym₁ → List Sym₂, PolyTimeComputable f ∧ ∀ x, x ∈ L₁ ↔ f x ∈ L₂
/-- NP-completeness (Cook, Definition 4): `L ∈ NP` and `L' ≤ₚ L` for every language `L'` in `NP`
over any finite nonempty alphabet. -/
def NPComplete {Sym : Type} [Fintype Sym] (L : Lang Sym) : Prop :=
L ∈ NP Sym ∧
∀ (Sym' : Type) [Fintype Sym'] [Nonempty Sym'] (L' : Lang Sym'),
L' ∈ NP Sym' → PolyReducible L' L
/-! ### Satisfiability (conjunctive normal form) -/
/-- A literal: a propositional variable `x_i` (`i : ℕ`) with a polarity (`true` for `x_i`,
`false` for its negation). -/
abbrev Literal : Type := Bool × ℕ
/-- A propositional formula in conjunctive normal form: a list of clauses, each a list of literals
(the disjunction of its literals); the formula is the conjunction of its clauses. -/
abbrev CNF : Type := List (List Literal)
/-- A CNF formula is satisfiable if some truth assignment makes every clause contain a true
literal. -/
def CNF.Satisfiable (F : CNF) : Prop :=
∃ τ : ℕ → Bool, ∀ C ∈ F, ∃ ℓ ∈ C, τ ℓ.2 = ℓ.1
/-- The four-letter alphabet used to write CNF formulas. -/
inductive SatSym where
| pos
| neg
| one
| endClause
deriving DecidableEq
instance : Fintype SatSym where
elems := {SatSym.pos, SatSym.neg, SatSym.one, SatSym.endClause}
complete := by intro x; cases x <;> simp
/-- Encoding of a literal: its sign (`pos`/`neg`) followed by the variable index in unary. -/
def encodeLiteral (ℓ : Literal) : List SatSym :=
(if ℓ.1 then SatSym.pos else SatSym.neg) :: List.replicate ℓ.2 SatSym.one
/-- Encoding of a CNF formula: each clause is the concatenation of its literals' codes followed by
`endClause`. -/
def encodeCNF (F : CNF) : List SatSym :=
F.flatMap fun C => C.flatMap encodeLiteral ++ [SatSym.endClause]
/-- The language CNF-Satisfiability: codes of satisfiable CNF formulas. -/
def SAT : Lang SatSym :=
{ s | ∃ F : CNF, F.Satisfiable ∧ s = encodeCNF F }
end CookPvsNP
Read-back
What the Lean code literally says, in plain math · Aristotle (Harmonic) — same agent as the drafter; non-blind
Non-blind read-back — not independent testimony. This read-back was written by the same agent that drafted these Lean statements, at the explicit request of the proposal owner. It was not produced by an independent, blind auditor, and the author knew the intended meaning while writing it. Do not treat it as independent evidence of faithfulness; compare the Lean code against the source yourself.
The code fixes the following objects, in order.
Languages. For a type , a language over is an arbitrary set of finite lists of elements of .
Machines. A machine over a type of non-blank symbols consists of: a type with a finiteness structure and decidable equality; three elements with (nothing prevents or ); and a function , defined on every state, including the halting ones. The type itself is not required to be finite by the machine structure.
A configuration is a state, a list (the squares left of the head, nearest first), a scanned symbol , and a list (the squares right of the head). A configuration is halting iff its state is or . One step leaves halting configurations unchanged. Otherwise, with :
- if = right, the new configuration has state , left part , scanned symbol the first element of (blank if is empty) and right part without its first element;
- if = left and is empty, it has state , empty left part, blank scanned symbol and right part ;
- if = left and , it has state , left part , scanned symbol and right part .
is the -fold iterate of the step. The initial configuration on has state , empty left part, scanned symbol the first letter of (blank if ) and right part the rest of . "Halts within steps on " means is halting. "Accepts " means some has in state . The output of a configuration is the list with all trailing blanks removed; the left part is ignored.
P. For a type with a finiteness structure, is the set of languages over for which there exist a type with a finiteness structure, an injective map , a machine over and with
- for every , halts on within steps (with ), and
- for every , iff accepts .
Check language. For , the check language is the language over consisting exactly of the strings with , where are the two disjoint tags.
NP. is the set of for which there exist a type (in the lowest universe) that is finite and nonempty, a relation and such that the check language of lies in and, for every , iff there is with and . For and this forces ; for it allows .
coNP. .
Polynomial-time computable functions. is polynomial-time computable iff there exist a finite type , injective maps and (their images may overlap), a machine over and such that for every : halts on within steps, and the output of equals . No finiteness is assumed of beyond what the injections into finite force.
Reducibility. iff some polynomial-time computable satisfies for all .
NP-complete. For finite , is NP-complete iff and, for every finite nonempty type (lowest universe) and every , .
CNF-SAT. A literal is a pair (Boolean sign, natural-number index). A formula is a list of lists of literals. It is satisfiable iff there is such that every clause contains a literal with . So the empty formula is satisfiable and a formula containing an empty clause is not. The alphabet has four letters: pos, neg, one, end. A literal is written as pos (if is true) or neg, followed by copies of one; so variable is the bare sign. A formula is written by concatenating, for each clause, the codes of its literals followed by end. is the set of strings equal to the code of some satisfiable formula; in particular it contains the empty string.