Imports
import CLRSLean.Chapter_34.Section_34_4_NP_Completeness_Proofs.PolyBuilder.Syntax
import Mathlib.Computability.TuringMachine.ComputableCompiling bounded builders to TM2
The compiler in this file is intentionally separate from the independent instruction semantics. Its correctness theorem compares the concrete TM2 step with the independently defined builder semantics.
noncomputable sectionopen Computability StateTransitionnamespace CLRS.Chapter34.Turing.PolyBuilderThe compiled machine's cleared buffers and counter-test bit.
private def initialControl {Γ : Type} : ControlState Γ :=
⟨none, none, false⟩Store the symbol read by a buffer-one instruction family.
private def setBuffer₁ {Γ : Type} (state : ControlState Γ)
(symbol : Option Γ) : ControlState Γ :=
{ state with buffer₁ := symbol }Store the symbol read by a buffer-two instruction family.
private def setBuffer₂ {Γ : Type} (state : ControlState Γ)
(symbol : Option Γ) : ControlState Γ :=
{ state with buffer₂ := symbol }Record whether a unary-counter pop found a successor token.
private def setCounterTest {Γ : Type} (state : ControlState Γ)
(head : Option Unit) : ControlState Γ :=
{ state with test := head.isSome }Read buffer one after its nonempty branch, with a compiler fallback.
private def buffer₁Value {Γ : Type} (fallback : Γ)
(state : ControlState Γ) : Γ :=
state.buffer₁.getD fallbackRead buffer two after its nonempty branch, with a compiler fallback.
private def buffer₂Value {Γ : Type} (fallback : Γ)
(state : ControlState Γ) : Γ :=
state.buffer₂.getD fallbackCompile a legal stack move whose popped symbol is owned by buffer one.
private def compileMove₁ {Γ Δ Λ : Type} (fallback : Γ)
(source target : Stack) (nextEmpty : Λ) (nextMoved : Γ → Λ) :
_root_.Turing.TM2.Stmt (Alphabet Γ Δ) Λ (ControlState Γ) :=
let stmt (pop : (ControlState Γ → Option Γ → ControlState Γ) →
_root_.Turing.TM2.Stmt (Alphabet Γ Δ) Λ (ControlState Γ) →
_root_.Turing.TM2.Stmt (Alphabet Γ Δ) Λ (ControlState Γ))
(push : (ControlState Γ → Γ) →
_root_.Turing.TM2.Stmt (Alphabet Γ Δ) Λ (ControlState Γ) →
_root_.Turing.TM2.Stmt (Alphabet Γ Δ) Λ (ControlState Γ)) :=
pop setBuffer₁ <|
_root_.Turing.TM2.Stmt.branch (fun state => state.buffer₁.isSome)
(push (buffer₁Value fallback) <|
_root_.Turing.TM2.Stmt.goto
(fun state => nextMoved (buffer₁Value fallback state)))
(_root_.Turing.TM2.Stmt.goto (fun _ => nextEmpty))
match source, target with
| .input, .work₁ => stmt
(_root_.Turing.TM2.Stmt.pop (Γ := Alphabet Γ Δ) Stack.input)
(_root_.Turing.TM2.Stmt.push (Γ := Alphabet Γ Δ) Stack.work₁)
| .work₁, .input => stmt
(_root_.Turing.TM2.Stmt.pop (Γ := Alphabet Γ Δ) Stack.work₁)
(_root_.Turing.TM2.Stmt.push (Γ := Alphabet Γ Δ) Stack.input)
| .work₁, .work₂ => stmt
(_root_.Turing.TM2.Stmt.pop (Γ := Alphabet Γ Δ) Stack.work₁)
(_root_.Turing.TM2.Stmt.push (Γ := Alphabet Γ Δ) Stack.work₂)
| _, _ => .haltCompile a legal stack move whose popped symbol is owned by buffer two.
private def compileMove₂ {Γ Δ Λ : Type} (fallback : Γ)
(source target : Stack) (nextEmpty : Λ) (nextMoved : Γ → Λ) :
_root_.Turing.TM2.Stmt (Alphabet Γ Δ) Λ (ControlState Γ) :=
let stmt
(pop : (ControlState Γ → Option Γ → ControlState Γ) →
_root_.Turing.TM2.Stmt (Alphabet Γ Δ) Λ (ControlState Γ) →
_root_.Turing.TM2.Stmt (Alphabet Γ Δ) Λ (ControlState Γ))
(push : (ControlState Γ → Γ) →
_root_.Turing.TM2.Stmt (Alphabet Γ Δ) Λ (ControlState Γ) →
_root_.Turing.TM2.Stmt (Alphabet Γ Δ) Λ (ControlState Γ)) :=
pop setBuffer₂ <|
_root_.Turing.TM2.Stmt.branch (fun state => state.buffer₂.isSome)
(push (buffer₂Value fallback) <|
_root_.Turing.TM2.Stmt.goto
(fun state => nextMoved (buffer₂Value fallback state)))
(_root_.Turing.TM2.Stmt.goto (fun _ => nextEmpty))
match source, target with
| .input, .work₂ => stmt
(_root_.Turing.TM2.Stmt.pop (Γ := Alphabet Γ Δ) Stack.input)
(_root_.Turing.TM2.Stmt.push (Γ := Alphabet Γ Δ) Stack.work₂)
| .work₂, .input => stmt
(_root_.Turing.TM2.Stmt.pop (Γ := Alphabet Γ Δ) Stack.work₂)
(_root_.Turing.TM2.Stmt.push (Γ := Alphabet Γ Δ) Stack.input)
| .work₂, .work₁ => stmt
(_root_.Turing.TM2.Stmt.pop (Γ := Alphabet Γ Δ) Stack.work₂)
(_root_.Turing.TM2.Stmt.push (Γ := Alphabet Γ Δ) Stack.work₁)
| _, _ => .haltCompile a buffer-one move when the symbol alphabet is empty.
private def compileEmptyMove₁ {Γ Δ Λ : Type} (source : Stack)
(nextEmpty : Λ) :
_root_.Turing.TM2.Stmt (Alphabet Γ Δ) Λ (ControlState Γ) :=
match source with
| .input => _root_.Turing.TM2.Stmt.pop .input setBuffer₁ <|
_root_.Turing.TM2.Stmt.goto (fun _ => nextEmpty)
| .work₁ => _root_.Turing.TM2.Stmt.pop .work₁ setBuffer₁ <|
_root_.Turing.TM2.Stmt.goto (fun _ => nextEmpty)
| _ => .haltCompile a buffer-two move when the symbol alphabet is empty.
private def compileEmptyMove₂ {Γ Δ Λ : Type} (source : Stack)
(nextEmpty : Λ) :
_root_.Turing.TM2.Stmt (Alphabet Γ Δ) Λ (ControlState Γ) :=
match source with
| .input => _root_.Turing.TM2.Stmt.pop .input setBuffer₂ <|
_root_.Turing.TM2.Stmt.goto (fun _ => nextEmpty)
| .work₂ => _root_.Turing.TM2.Stmt.pop .work₂ setBuffer₂ <|
_root_.Turing.TM2.Stmt.goto (fun _ => nextEmpty)
| _ => .haltCompile an input pop copied to both work stacks through buffer one.
private def compileCopy {Γ Δ Λ : Type} (fallback : Γ)
(nextEmpty : Λ) (nextCopied : Γ → Λ) :
_root_.Turing.TM2.Stmt (Alphabet Γ Δ) Λ (ControlState Γ) :=
_root_.Turing.TM2.Stmt.pop .input setBuffer₁ <|
_root_.Turing.TM2.Stmt.branch (fun state => state.buffer₁.isSome)
(_root_.Turing.TM2.Stmt.push .work₁ (buffer₁Value fallback) <|
_root_.Turing.TM2.Stmt.push .work₂ (buffer₁Value fallback) <|
_root_.Turing.TM2.Stmt.goto
(fun state => nextCopied (buffer₁Value fallback state)))
(_root_.Turing.TM2.Stmt.goto (fun _ => nextEmpty))Compile the necessarily empty copy operation for an empty alphabet.
private def compileEmptyCopy {Γ Δ Λ : Type} (nextEmpty : Λ) :
_root_.Turing.TM2.Stmt (Alphabet Γ Δ) Λ (ControlState Γ) :=
_root_.Turing.TM2.Stmt.pop .input setBuffer₁ <|
_root_.Turing.TM2.Stmt.goto (fun _ => nextEmpty)Compile a typed pop whose result and continuation use buffer one.
private def compilePop₁ {Γ Δ Λ : Type} (source : Stack)
(nextEmpty : Λ) (nextSome : Γ → Λ) :
_root_.Turing.TM2.Stmt (Alphabet Γ Δ) Λ (ControlState Γ) :=
match source with
| .input => _root_.Turing.TM2.Stmt.pop .input setBuffer₁ <|
_root_.Turing.TM2.Stmt.goto
(fun state => state.buffer₁.elim nextEmpty nextSome)
| .work₁ => _root_.Turing.TM2.Stmt.pop .work₁ setBuffer₁ <|
_root_.Turing.TM2.Stmt.goto
(fun state => state.buffer₁.elim nextEmpty nextSome)
| _ => .haltCompile a typed pop whose result and continuation use buffer two.
private def compilePop₂ {Γ Δ Λ : Type} (source : Stack)
(nextEmpty : Λ) (nextSome : Γ → Λ) :
_root_.Turing.TM2.Stmt (Alphabet Γ Δ) Λ (ControlState Γ) :=
match source with
| .work₂ => _root_.Turing.TM2.Stmt.pop .work₂ setBuffer₂ <|
_root_.Turing.TM2.Stmt.goto
(fun state => state.buffer₂.elim nextEmpty nextSome)
| _ => .haltCompile unary decrement, storing the zero/successor test separately.
private def compileDec {Γ Δ Λ : Type} (counter : Stack)
(nextZero nextSucc : Λ) :
_root_.Turing.TM2.Stmt (Alphabet Γ Δ) Λ (ControlState Γ) :=
let stmt (pop : (ControlState Γ → Option Unit → ControlState Γ) →
_root_.Turing.TM2.Stmt (Alphabet Γ Δ) Λ (ControlState Γ) →
_root_.Turing.TM2.Stmt (Alphabet Γ Δ) Λ (ControlState Γ)) :=
pop setCounterTest <|
_root_.Turing.TM2.Stmt.branch (fun state => state.test)
(_root_.Turing.TM2.Stmt.goto (fun _ => nextSucc))
(_root_.Turing.TM2.Stmt.goto (fun _ => nextZero))
match counter with
| .counter₁ => stmt
(_root_.Turing.TM2.Stmt.pop (Γ := Alphabet Γ Δ) Stack.counter₁)
| .counter₂ => stmt
(_root_.Turing.TM2.Stmt.pop (Γ := Alphabet Γ Δ) Stack.counter₂)
| .counter₃ => stmt
(_root_.Turing.TM2.Stmt.pop (Γ := Alphabet Γ Δ) Stack.counter₃)
| _ => .haltCompile one builder instruction to a single concrete TM2 statement.
The nonempty-alphabet split is local to instructions that push a buffered symbol. When the input alphabet is empty, all input/work stacks are necessarily empty and the compiled statement takes the empty continuation without constructing a fictitious symbol.
def compileOp {Γ Δ Λ : Type} [Fintype Γ] (op : Op Γ Δ Λ) :
_root_.Turing.TM2.Stmt (Alphabet Γ Δ) Λ (ControlState Γ) :=
by
classical
exact match op with
| .pushOutput symbol next =>
_root_.Turing.TM2.Stmt.push .output (fun _ => symbol) <|
_root_.Turing.TM2.Stmt.goto (fun _ => next)
| .pushWork₁ symbol next =>
_root_.Turing.TM2.Stmt.push .work₁ (fun _ => symbol) <|
_root_.Turing.TM2.Stmt.goto (fun _ => next)
| .pushWork₂ symbol next =>
_root_.Turing.TM2.Stmt.push .work₂ (fun _ => symbol) <|
_root_.Turing.TM2.Stmt.goto (fun _ => next)
| .moveInputWork₁ nextEmpty nextMoved =>
if h : Nonempty Γ then
compileMove₁ (Classical.choice h) .input .work₁ nextEmpty nextMoved
else compileEmptyMove₁ .input nextEmpty
| .moveWork₁Input nextEmpty nextMoved =>
if h : Nonempty Γ then
compileMove₁ (Classical.choice h) .work₁ .input nextEmpty nextMoved
else compileEmptyMove₁ .work₁ nextEmpty
| .moveInputWork₂ nextEmpty nextMoved =>
if h : Nonempty Γ then
compileMove₂ (Classical.choice h) .input .work₂ nextEmpty nextMoved
else compileEmptyMove₂ .input nextEmpty
| .moveWork₂Input nextEmpty nextMoved =>
if h : Nonempty Γ then
compileMove₂ (Classical.choice h) .work₂ .input nextEmpty nextMoved
else compileEmptyMove₂ .work₂ nextEmpty
| .moveWork₁Work₂ nextEmpty nextMoved =>
if h : Nonempty Γ then
compileMove₁ (Classical.choice h) .work₁ .work₂ nextEmpty nextMoved
else compileEmptyMove₁ .work₁ nextEmpty
| .moveWork₂Work₁ nextEmpty nextMoved =>
if h : Nonempty Γ then
compileMove₂ (Classical.choice h) .work₂ .work₁ nextEmpty nextMoved
else compileEmptyMove₂ .work₂ nextEmpty
| .copyInputWorks nextEmpty nextCopied =>
if h : Nonempty Γ then
compileCopy (Classical.choice h) nextEmpty nextCopied
else compileEmptyCopy nextEmpty
| .popInput nextEmpty nextSome => compilePop₁ .input nextEmpty nextSome
| .popWork₁ nextEmpty nextSome => compilePop₁ .work₁ nextEmpty nextSome
| .popWork₂ nextEmpty nextSome => compilePop₂ .work₂ nextEmpty nextSome
| .inc₁ next =>
_root_.Turing.TM2.Stmt.push .counter₁ (fun _ => ()) <|
_root_.Turing.TM2.Stmt.goto (fun _ => next)
| .inc₂ next =>
_root_.Turing.TM2.Stmt.push .counter₂ (fun _ => ()) <|
_root_.Turing.TM2.Stmt.goto (fun _ => next)
| .inc₃ next =>
_root_.Turing.TM2.Stmt.push .counter₃ (fun _ => ()) <|
_root_.Turing.TM2.Stmt.goto (fun _ => next)
| .dec₁ nextZero nextSucc => compileDec .counter₁ nextZero nextSucc
| .dec₂ nextZero nextSucc => compileDec .counter₂ nextZero nextSucc
| .dec₃ nextZero nextSucc => compileDec .counter₃ nextZero nextSucc
| .jump next => _root_.Turing.TM2.Stmt.goto (fun _ => next)
| .halt => _root_.Turing.TM2.Stmt.load (fun _ => initialControl) .haltCompile a finite builder program to a finite TM2.
def compile {Γ Δ : Type} [Fintype Γ] (P : Program Γ Δ) :
_root_.Turing.FinTM2 where
K := Stack
k₀ := .input
k₁ := .output
Γ := Alphabet Γ Δ
Λ := P.Label
main := P.main
ΛFin := P.labelFintype
σ := ControlState Γ
initialState := initialControl
m label := compileOp (P.op label)Encode an independent builder configuration fieldwise as a TM2 configuration.
def encodeCfg {Γ Δ : Type} {P : Program Γ Δ} (c : BuilderCfg P) :
_root_.Turing.TM2.Cfg (Alphabet Γ Δ) P.Label (ControlState Γ) where
l := c.label
var := ⟨c.buffer₁, c.buffer₂, c.test⟩
stk
| .input => c.input
| .output => c.output
| .work₁ => c.work₁
| .work₂ => c.work₂
| .counter₁ => c.counter₁
| .counter₂ => c.counter₂
| .counter₃ => c.counter₃The independent initial configuration for a builder input.
def initialCfg {Γ Δ : Type} (P : Program Γ Δ) (input : List Γ) : BuilderCfg P where
label := some P.main
buffer₁ := none
buffer₂ := none
test := false
input := input
output := []
work₁ := []
work₂ := []
counter₁ := []
counter₂ := []
counter₃ := []The independent successful halt configuration for a builder output.
All non-output stacks are empty. A builder must therefore restore or clear its scratch stacks before it can establish a successful output run.
def haltCfg {Γ Δ : Type} (P : Program Γ Δ) (output : List Δ) : BuilderCfg P where
label := none
buffer₁ := none
buffer₂ := none
test := false
input := []
output := output
work₁ := []
work₂ := []
counter₁ := []
counter₂ := []
counter₃ := []Encoding a builder's initial configuration gives the compiled TM2 initial configuration exactly.
@[simp] theorem encodeCfg_initialCfg {Γ Δ : Type} [Fintype Γ]
(P : Program Γ Δ) (input : List Γ) :
encodeCfg (initialCfg P input) = _root_.Turing.initList (compile P) input := by
unfold encodeCfg initialCfg _root_.Turing.initList
congr 1
funext stack
cases stack <;> simp [compile]Encoding a builder's successful halt configuration gives the compiled TM2 halt configuration exactly.
@[simp] theorem encodeCfg_haltCfg {Γ Δ : Type} [Fintype Γ]
(P : Program Γ Δ) (output : List Δ) :
encodeCfg (haltCfg P output) = _root_.Turing.haltList (compile P) output := by
unfold encodeCfg haltCfg _root_.Turing.haltList
congr 1
funext stack
cases stack <;> simp [compile]One compiled TM2 step agrees with the independent builder semantics.
theorem compile_step {Γ Δ : Type} [Fintype Γ] (P : Program Γ Δ)
(c : BuilderCfg P) :
(compile P).step (encodeCfg c) = (step P c).map encodeCfg := by
classical
rcases c with ⟨label, buffer₁, buffer₂, test, input, output, work₁, work₂,
counter₁, counter₂, counter₃⟩
cases label with
| none => simp [compile, encodeCfg, step]
| some label =>
cases hop : P.op label <;>
simp only [compile, encodeCfg, step, hop, stepOp,
_root_.Turing.FinTM2.step, _root_.Turing.TM2.step]
case pushOutput =>
simp [compileOp, encodeCfg]
congr 3
funext stack
cases stack <;> simp [Function.update]
case pushWork₁ =>
simp [compileOp, encodeCfg]
congr 3
funext stack
cases stack <;> simp [Function.update]
case pushWork₂ =>
simp [compileOp, encodeCfg]
congr 3
funext stack
cases stack <;> simp [Function.update]
case moveInputWork₁ =>
cases input with
| nil =>
by_cases h : Nonempty Γ <;>
simp [compileOp, h, compileMove₁, compileEmptyMove₁, setBuffer₁,
encodeCfg]
| cons symbol rest =>
have h : Nonempty Γ := ⟨symbol⟩
simp [compileOp, h, compileMove₁, setBuffer₁, buffer₁Value,
encodeCfg]
congr 3
funext stack
cases stack <;> simp [Function.update]
case moveWork₁Input =>
cases work₁ with
| nil =>
by_cases h : Nonempty Γ <;>
simp [compileOp, h, compileMove₁, compileEmptyMove₁, setBuffer₁,
encodeCfg]
| cons symbol rest =>
have h : Nonempty Γ := ⟨symbol⟩
simp [compileOp, h, compileMove₁, setBuffer₁, buffer₁Value,
encodeCfg]
congr 3
funext stack
cases stack <;> simp [Function.update]
case moveInputWork₂ =>
cases input with
| nil =>
by_cases h : Nonempty Γ <;>
simp [compileOp, h, compileMove₂, compileEmptyMove₂, setBuffer₂,
encodeCfg]
| cons symbol rest =>
have h : Nonempty Γ := ⟨symbol⟩
simp [compileOp, h, compileMove₂, setBuffer₂, buffer₂Value,
encodeCfg]
congr 3
funext stack
cases stack <;> simp [Function.update]
case moveWork₂Input =>
cases work₂ with
| nil =>
by_cases h : Nonempty Γ <;>
simp [compileOp, h, compileMove₂, compileEmptyMove₂, setBuffer₂,
encodeCfg]
| cons symbol rest =>
have h : Nonempty Γ := ⟨symbol⟩
simp [compileOp, h, compileMove₂, setBuffer₂, buffer₂Value,
encodeCfg]
congr 3
funext stack
cases stack <;> simp [Function.update]
case moveWork₁Work₂ =>
cases work₁ with
| nil =>
by_cases h : Nonempty Γ <;>
simp [compileOp, h, compileMove₁, compileEmptyMove₁, setBuffer₁,
encodeCfg]
| cons symbol rest =>
have h : Nonempty Γ := ⟨symbol⟩
simp [compileOp, h, compileMove₁, setBuffer₁, buffer₁Value,
encodeCfg]
congr 3
funext stack
cases stack <;> simp [Function.update]
case moveWork₂Work₁ =>
cases work₂ with
| nil =>
by_cases h : Nonempty Γ <;>
simp [compileOp, h, compileMove₂, compileEmptyMove₂, setBuffer₂,
encodeCfg]
| cons symbol rest =>
have h : Nonempty Γ := ⟨symbol⟩
simp [compileOp, h, compileMove₂, setBuffer₂, buffer₂Value,
encodeCfg]
congr 3
funext stack
cases stack <;> simp [Function.update]
case copyInputWorks =>
cases input with
| nil =>
by_cases h : Nonempty Γ <;>
simp [compileOp, h, compileCopy, compileEmptyCopy, setBuffer₁,
encodeCfg]
| cons symbol rest =>
have h : Nonempty Γ := ⟨symbol⟩
simp [compileOp, h, compileCopy, setBuffer₁, buffer₁Value,
encodeCfg]
congr 3
funext stack
cases stack <;> simp [Function.update]
case popInput =>
cases input <;> simp [compileOp, compilePop₁, setBuffer₁, encodeCfg]
all_goals
congr 3
funext stack
cases stack <;> simp [Function.update]
case popWork₁ =>
cases work₁ <;> simp [compileOp, compilePop₁, setBuffer₁, encodeCfg]
all_goals
congr 3
funext stack
cases stack <;> simp [Function.update]
case popWork₂ =>
cases work₂ <;> simp [compileOp, compilePop₂, setBuffer₂, encodeCfg]
all_goals
congr 3
funext stack
cases stack <;> simp [Function.update]
case inc₁ =>
simp [compileOp, encodeCfg]
congr 3
funext stack
cases stack <;> simp [Function.update]
case inc₂ =>
simp [compileOp, encodeCfg]
congr 3
funext stack
cases stack <;> simp [Function.update]
case inc₃ =>
simp [compileOp, encodeCfg]
congr 3
funext stack
cases stack <;> simp [Function.update]
case dec₁ =>
cases counter₁ <;>
simp [compileOp, compileDec, setCounterTest, encodeCfg]
all_goals
congr 3
funext stack
cases stack <;> simp [Function.update]
case dec₂ =>
cases counter₂ <;>
simp [compileOp, compileDec, setCounterTest, encodeCfg]
all_goals
congr 3
funext stack
cases stack <;> simp [Function.update]
case dec₃ =>
cases counter₃ <;>
simp [compileOp, compileDec, setCounterTest, encodeCfg]
all_goals
congr 3
funext stack
cases stack <;> simp [Function.update]
case jump => simp [compileOp, encodeCfg]
case halt => simp [compileOp, initialControl, encodeCfg]Multi-step compiler agreement
Project the exact stored step count of a transitive bounded evaluation.
This wrapper insulates builder proofs from the implementation details and
field order of Mathlib's EvalsToInTime.trans constructor.
@[simp] theorem evalsToInTime_trans_steps {σ : Type} (f : σ → Option σ)
(m₁ m₂ : Nat) (a b : σ) (c : Option σ)
(first : _root_.StateTransition.EvalsToInTime f a (some b) m₁)
(second : _root_.StateTransition.EvalsToInTime f b c m₂) :
(_root_.StateTransition.EvalsToInTime.trans f m₁ m₂ a b c
first second).steps = second.steps + first.steps := rflView the fieldwise encoding at the configuration type of this compiler output.
private def compiledCfg {Γ Δ : Type} [Fintype Γ] (P : Program Γ Δ)
(c : BuilderCfg P) : (compile P).Cfg :=
encodeCfg cLift one-step compiler agreement through option-valued iteration.
private theorem compile_iterate {Γ Δ : Type} [Fintype Γ]
(P : Program Γ Δ) (iterations : Nat) (c : Option (BuilderCfg P)) :
(flip Option.bind (compile P).step)^[iterations] (c.map (compiledCfg P)) =
((flip Option.bind (step P))^[iterations] c).map (compiledCfg P) := by
induction iterations generalizing c with
| zero => rfl
| succ iterations ih =>
rw [Function.iterate_succ_apply, Function.iterate_succ_apply]
cases c with
| none => exact ih none
| some c =>
simp only [Option.map_some, flip, Option.bind_some]
rw [show (compile P).step (compiledCfg P c) =
(step P c).map (compiledCfg P) by
change (compile P).step (encodeCfg c) =
(step P c).map encodeCfg
exact compile_step P c]
exact ih (step P c)Transport a bounded multi-step builder run to the compiled TM2 without changing its exact evaluation witness or time bound.
def compile_evalsToInTime {Γ Δ : Type} [Fintype Γ]
(P : Program Γ Δ) {c c' : BuilderCfg P} {bound : Nat}
(run : _root_.StateTransition.EvalsToInTime
(step P) c (some c') bound) :
_root_.StateTransition.EvalsToInTime
(compile P).step (encodeCfg c) (some (encodeCfg c')) bound := by
refine ⟨⟨run.steps, ?_⟩, run.steps_le_m⟩
change (flip Option.bind (compile P).step)^[run.steps]
(compiledCfg P c) = some (compiledCfg P c')
rw [show compiledCfg P c = (some c).map (compiledCfg P) by rfl,
compile_iterate P run.steps (some c)]
simpa using congrArg (Option.map (compiledCfg P)) run.evals_in_stepsCompiling a bounded builder evaluation preserves its stored exact step count, not only its advertised upper bound.
@[simp] theorem compile_evalsToInTime_steps {Γ Δ : Type} [Fintype Γ]
(P : Program Γ Δ) {c c' : BuilderCfg P} {bound : Nat}
(run : _root_.StateTransition.EvalsToInTime
(step P) c (some c') bound) :
(compile_evalsToInTime P run).steps = run.steps := rflA proposition-valued bounded run of the independent builder semantics.
def BuilderOutputs {Γ Δ : Type} (P : Program Γ Δ)
(f : List Γ → List Δ) (steps : List Γ → Nat) : Prop :=
∀ input, Nonempty (_root_.StateTransition.EvalsToInTime
(step P) (initialCfg P input) (some (haltCfg P (f input))) (steps input))Exact bounded execution statement for a compiled builder.
def Outputs {Γ Δ : Type} [Fintype Γ] (P : Program Γ Δ)
(f : List Γ → List Δ) (steps : List Γ → Nat) : Prop :=
∀ input, Nonempty (_root_.Turing.TM2OutputsInTime (compile P) input
(some (f input)) (steps input))namespace OutputsCompile a bounded independent-semantics run into the corresponding exact TM2 output run.
theorem of_builder_run {Γ Δ : Type} [Fintype Γ] {P : Program Γ Δ}
{f : List Γ → List Δ} {steps : List Γ → Nat}
(run : BuilderOutputs P f steps) : Outputs P f steps := by
intro input
rcases run input with ⟨builderRun⟩
refine ⟨?_⟩
have compiledRun := compile_evalsToInTime P builderRun
simpa [_root_.Turing.TM2OutputsInTime] using compiledRunend OutputsA polynomial upper envelope for a builder's exact step function.
structure PolyBound {Γ : Type} (steps : List Γ → Nat) where
polynomial : Polynomial ℕ
bound : ∀ input, steps input ≤ polynomial.eval input.lengthPackage exact builder execution and its polynomial envelope as Mathlib's machine-level polynomial-time computability witness.
noncomputable def ComputableInPolyTime {Γ Δ : Type} [Fintype Γ]
(P : Program Γ Δ) (f : List Γ → List Δ) (steps : List Γ → Nat)
(hout : Outputs P f steps) (hpoly : PolyBound steps) :
_root_.Turing.TM2ComputableInPolyTime id id f where
tm := compile P
inputAlphabet := Equiv.refl Γ
outputAlphabet := Equiv.refl Δ
time := hpoly.polynomial
outputsFun := fun input => by
simp only [compile, id_eq, Equiv.refl]
simp only [List.map_id]
have run := Classical.choice (hout input)
exact ⟨run.toEvalsTo, le_trans run.steps_le_m (hpoly.bound input)⟩end CLRS.Chapter34.Turing.PolyBuilder