Skip to content

feat(PH): the polynomial hierarchy and the Sipser-Lautemann theorem - #28

Closed
BoltonBailey wants to merge 21 commits into
SamuelSchlesinger:devfrom
BoltonBailey:boltonbailey-dev
Closed

feat(PH): the polynomial hierarchy and the Sipser-Lautemann theorem#28
BoltonBailey wants to merge 21 commits into
SamuelSchlesinger:devfrom
BoltonBailey:boltonbailey-dev

Conversation

@BoltonBailey

Copy link
Copy Markdown
Collaborator

⚠️ Unreviewed by Bolton

Summary

Proves the Sipser–Lautemann theorem, BPP ⊆ Σ₂ᵖ ∩ Π₂ᵖ, unconditionally:

theorem Complexity.sipserLautemann : BPP ⊆ SigmaP 2 ∩ PiP 2

with BPP_subset_SigmaP_two, BPP_subset_PiP_two, and BPP_subset_PH.

This branch also carries the polynomial-hierarchy definitions (Complexitylib.Classes.PH:
SigmaP, PiP, PH, quantifier duality, level inclusions) and the bitstring-encoding
work they depend on, which were previously on separate branches.

The development

Module Content
SipserLautemann.Covering Lautemann's covering lemma, both directions, by counting shifts of an event in the seed space
SipserLautemann.TimeBound acceptance probability is frozen past the halting time, so an arbitrary time bound can be replaced by a dominating polynomial
SipserLautemann.Amplified majority amplification + covering give the ∃∀ characterization of L, and its complementary form for Lᶜ
SipserLautemann.Encode, .Matrix seed/shift codecs; L and Lᶜ as polynomially bounded ∃∀ forms over the matrix language
SipserLautemann.Verdict the matrix verdict as a member of Cobham's algebra, hence in FP

The polynomial-time obligation on the matrix is discharged with no machine construction:
CobhamFP_eq_FP turns it into a programming task in Cobham's algebra. Two supporting pieces
make that possible:

  • Models.TuringMachine.ChoiceTapeNTM.choiceTM feeds an NTM's choice bit from a work tape,
    one per step, so running one nondeterministic path is a definition, not a simulation loop;
    choiceTM_simulates proves a T-step run is tm.trace T along the bits on the tape. This is
    also the primitive NP.WitnessNTMConstruction has been waiting for.
  • Classes.P.Cobham.Internal.ChoiceSim / .BlockLoop — that run, and the amplified verdict,
    inside the algebra; plus .PolyLen / .StringOps for polynomial lengths, length tests, and
    exclusive-or.
  • Classes.P.DecisionFnmem_P_of_decisionFn_bool turns a verdict function in FP into
    membership in P.

Side effect: PH's own pairFst ∈ FP seam is closed — Cobham.fstBlock_mem_FP was already
proved — so P_subset_polyExistsClass_P, P_subset_polyForallClass_P,
SigmaP_subset_SigmaP_succ and PiP_subset_PiP_succ are now unconditional.

Test plan

All CI gates pass locally:

  • lake build --wfail (3933 jobs) and all five validation targets
  • python3 scripts/lint_style.py
  • lake exe runLinter on the public root and all five validation import graphs
  • lake env lean scripts/AxiomGuard.lean — 43463 declarations, 0 axioms; sipserLautemann
    depends only on propext, Classical.choice, Quot.sound

🤖 Generated with Claude Code

BoltonBailey and others added 21 commits July 24, 2026 12:53
Define certificate quantifiers over the pair codec (polyExistsLang /
polyForallLang and their class operators), the levels SigmaP / PiP by
the standard recursion through complement classes, and PH as their
union. Prove quantifier and class-level complement duality,
monotonicity, the recursion laws, and level inclusions, the latter
conditional on the single machine-engineering seam `pairFst ∈ FP`
(first-component pair decoding), isolated as a hypothesis following the
WitnessNTMConstruction pattern. Ported from leanprover/cslib#192,
re-proved against this library's machine model and Set-based languages.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
State `BPP ⊆ Σ₂ᵖ ∩ Π₂ᵖ` against the library's concrete `BPP` and the
certificate-quantifier levels `SigmaP`/`PiP`, packaged as the `Prop`-valued
`SipserLautemann` so downstream results can be proved against it before the
probabilistic argument lands. Adds the unconditional consequences: the split
into halves, the reduction of the statement to its `Σ₂` half given closure of
`BPP` under complement, and `BPP ⊆ PH`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reduce `BPP ⊆ Σ₂ᵖ ∩ Π₂ᵖ` to a single machine-engineering interface,
`MatrixInP`, and prove every other step unconditionally:

- `SipserLautemann.Covering`: Lautemann's covering lemma in both directions —
  a union bound over shifted translates of an event in the seed space gives
  covering shifts when the event is large, and a cardinality bound rules them
  out when it is small.
- `SipserLautemann.TimeBound`: the acceptance probability is frozen past the
  halting time, so a machine's arbitrary time bound may be replaced by a
  dominating polynomial. This is what makes the matrix predicate computable.
- `SipserLautemann.Amplified`: majority amplification plus the covering lemma
  give `x ∈ L ↔ ∃ shifts, ∀ seeds, some shift accepts`, and the complementary
  form for `x ∉ L`, which yields the `Π₂` half without needing closure of
  `BPP` under complement.
- `SipserLautemann.Encode`: bitstring codecs for seeds and shift tuples.
- `SipserLautemann.Matrix`: the quantifier-free matrix as a language of
  encoded triples, and the identity exhibiting `L` and `Lᶜ` as polynomially
  bounded `∃∀` forms over it.

`MatrixInP` — the matrix language is decidable in deterministic polynomial
time — is the same NTM-path-simulation construction already deferred by
`NP.WitnessNTMConstruction`, and is isolated rather than assumed silently.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`NTM.choiceTM` appends a choice tape to an NTM and reads one bit per step
from it, writing it back unchanged and advancing that head, which turns the
nondeterministic machine into a deterministic one whose run is the chosen
path. `NTM.choiceTM_simulates` proves the exact correspondence: a `T`-step
run is `tm.trace T` along the bits found on the tape, stopping early exactly
when the path halts.

This is the primitive a deterministic decider needs in order to evaluate
"does `tm` accept `x` along choice sequence `c`" — the missing ingredient
behind both `NP.WitnessNTMConstruction` and the `MatrixInP` interface of the
Sipser-Lautemann development.

Also adds `TM.stepCfg` and `TM.step_of_not_halted`, the non-halted step
unfolding used throughout the correspondence proof.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`mem_P_of_decisionFn` and `mem_P_of_decisionFn_bool` put a language in `P`
given a verdict function in `FP`, by reading the verdict through
`Language.containsOne` and closing under polynomial-time preimages. Adds the
`mem_containsZero`/`mem_containsOne` membership lemmas this needs.

This lets a development establish membership in `P` by exhibiting a function —
in particular by building one in Cobham's algebra via `CobhamFP_eq_FP` —
instead of constructing a decider machine by hand.

Uses it to sharpen the Sipser-Lautemann interface: `MatrixVerdictInFP` states
the remaining obligation as a function in `FP`, `matrixInP_of_verdictInFP`
converts it to `MatrixInP`, and `sipserLautemann_of_verdictInFP` gives the
theorem from it. `Lautemann.matrixVerdict` is the verdict function, with
`mem_matrixLang_iff_verdict` tying it to the matrix language.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The remaining obligation is now `MatrixVerdictInFP`, a function-level
statement dischargeable inside Cobham's algebra, and the path semantics it
needs are available from `NTM.choiceTM_simulates`. Records the concrete
remaining piece: an initial-configuration encoder that places the choice
string on the choice tape.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Simulating one path of an NTM inside Cobham's algebra needs a starting
configuration with the choice string already on the choice tape. This adds
that encoder and the correspondence lemmas:

- `Cobham.choiceCfg` / `Cobham.choiceTape` — the starting configuration of
  `NTM.choiceTM tm`, with the choice head parked on the first bit;
- `Cobham.initChoiceFn` with `initChoiceFn_mem` and `initChoiceFn_eq` — the
  encoder is in the algebra and computes `cfgCode` of that configuration;
- `Cobham.iterate_stepFn_choice` — iterating the algebra's step function
  tracks the deterministic run, with the start-marker and head-position
  invariants proved for choice runs;
- `Cobham.dropChoice_runCfg_choiceCfg` — that run is exactly `tm.trace` along
  the bits of the choice string, via `NTM.choiceTM_simulates`.

Next: iterate under a clock inside the algebra and read the verdict off the
final configuration, giving the path verdict in `FP`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Completes the in-algebra simulation of one nondeterministic path:

- `Cobham.runChoiceFn` iterates the encoded step function once per bit of the
  choice string, with `runChoiceFn_mem` placing it in the algebra via the
  bounded-iteration combinator;
- `Cobham.outPairChoiceFn` rewinds the output tape and `Cobham.acceptChoiceFn`
  reads the verdict off the final configuration — the state block against the
  halt state's code, and the first output cell against the code for `1`;
- `Cobham.acceptChoiceFn_eq_true_iff` proves that verdict is exactly
  `Cobham.PathAccepts`: after `|c|` steps along the choice bits of `c`, the
  machine has halted with `1` on the first output cell.

With `acceptChoiceFn_mem` this gives the path verdict as a member of Cobham's
algebra, hence in `FP` by `CobhamFP_eq_FP`, with no machine construction.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tional

`Cobham.fstBlock` is the payload scanner of Cobham's algebra and
`Cobham.fstBlock_mem_FP` already proves it polynomial-time. Defining `pairFst`
as that scanner discharges the `pairFst ∈ FP` hypothesis the polynomial
hierarchy's inclusions were stated against, so `P_subset_polyExistsClass_P`,
`P_subset_polyForallClass_P`, `SigmaP_subset_SigmaP_succ` and
`PiP_subset_PiP_succ` are now unconditional.

Only `pairFst`'s behaviour on canonical pairs is used, and `pairFst_pair` is
unchanged; the scanner differs from the old definition only on malformed
input, where it returns the bits decoded so far rather than the empty string.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…he algebra

Three utilities the matrix computation needs, all machine-free:

- `Cobham.polyLen` — for any `Polynomial ℕ`, a member of the algebra whose
  output has exactly the polynomial's value as its length, by Horner's scheme
  through `smash` (which multiplies lengths) and concatenation (which adds
  them);
- `Cobham.lenLeFlag` / `Cobham.lenEqFlag` — length comparison flags, a drop
  followed by an emptiness test;
- `Cobham.xorSuffix` — bitwise exclusive-or of a string with the matching
  suffix of a second string, as one limited recursion whose step reads the
  paired bit of the second argument through a ruler cut to the right width;
  `xorSuffix_eq_zipWith_of_length` identifies it with the pointwise operation
  on equal-length strings.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both loops of the amplified acceptance test, as limited recursions over a
ruler whose length is the iteration count — the block index at each step is
the length of the remaining tail, so no state is threaded through:

- `Cobham.acceptCountAux` counts, in unary, the blocks of a seed on which the
  path accepts, with `acceptCountAux_length` identifying the count and
  `acceptCountAux_mem` placing it in the algebra;
- `Cobham.majorityFlag` and `Cobham.verdictFlag` turn that count into the
  amplified majority verdict (or its negation), with
  `majorityFlag_eq_true_iff`;
- `Cobham.anyShiftAux` takes the disjunction of the verdict over the shift
  blocks of the witness, with `anyShiftAux_eq_true_iff` identifying it with an
  existential over shift indices.

Also adds `orBit_flag` and `orBit_length` to the flag API.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`BPP ⊆ Σ₂ᵖ ∩ Π₂ᵖ` is now unconditional: `Complexity.sipserLautemann`, with
`BPP_subset_SigmaP_two`, `BPP_subset_PiP_two` and `BPP_subset_PH`.

The last interface, `MatrixVerdictInFP`, is discharged by computing the matrix
verdict inside Cobham's algebra — no machine construction:

- `SipserLautemann.Verdict` builds the rulers (per-trial step count, trial
  count, seed length and shift count as `smash` lengths from the polynomial
  time bound), decodes the triple with the payload scanners, and takes the
  disjunction over shift blocks of the majority vote over trial blocks;
- `matrixFn_eq` proves that computation equals `matrixVerdict`, bridging the
  string view and the `Finset` counting view: `blockEventCount_seedOfList`
  matches the counts, `seedOfList_xorSuffix` and `seedOfList_padTo_block`
  match the exclusive-or with the shift action, and `pathAccepts_iff` matches
  a block's acceptance with membership in the single-trial event;
- `matrixVerdict_mem_FP` concludes via `CobhamFP_subset_FP`.

`Matrix` now decodes with `Cobham.fstBlock`/`sndBlock` rather than `unpair?`,
so the matrix language and its verdict agree on every input, including
malformed ones.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@BoltonBailey

Copy link
Copy Markdown
Collaborator Author

Superseded: splitting the Sipser–Lautemann work onto its own branch; boltonbailey-dev stays as a personal integration branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant