diff --git a/Complexity.lean b/Complexity.lean index f59b96f22..1e851ba01 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2038,6 +2038,7 @@ public import Complexity.Computability.WordRAM.Randomized.Expected public import Complexity.Computability.WordRAM.Randomized.LoopSegment public import Complexity.Computability.WordRAM.Randomized.Structured public import Complexity.Computability.WordRAM.RegisterFrame +public import Complexity.Computability.WordRAM.RelSubroutine public import Complexity.Computability.WordRAM.Sequence public import Complexity.Computability.WordRAM.Signed.Arithmetic public import Complexity.Computability.WordRAM.Signed.Comparison @@ -2662,6 +2663,7 @@ public import Complexity.Foundations.Semantics.Stateful.CostHoare public import Complexity.Foundations.Semantics.Stateful.Hoare public import Complexity.Foundations.Semantics.Stateful.Module public import Complexity.Foundations.Semantics.Stateful.Realization +public import Complexity.Foundations.Semantics.Stateful.Relational public import Complexity.Foundations.Semantics.Stateful.Resource public import Complexity.Init public import Complexity.Languages.Probabilistic.Basic diff --git a/Complexity/Algorithms/WordRAM/PriorityQueue/BinaryHeap.lean b/Complexity/Algorithms/WordRAM/PriorityQueue/BinaryHeap.lean index f0605c71b..b9de086eb 100644 --- a/Complexity/Algorithms/WordRAM/PriorityQueue/BinaryHeap.lean +++ b/Complexity/Algorithms/WordRAM/PriorityQueue/BinaryHeap.lean @@ -691,7 +691,7 @@ def dynamicProgram (n K_max : ℕ) : DynamicProgram (IndexedMinPQ.spec n K_max) output := pqCode.output input := pqCode.input encode := pqCode.encode - decode := pqCode.decode + decode := pqCode.decode (K_max := K_max) /-- Standalone `.standard`-basis `DynamicCertificate` for the binary min-heap. -/ def certificate {n K_max w base : ℕ} (hS : Fits w (base + space n)) (hK : Fits w K_max) : diff --git a/Complexity/Computability/WordRAM/Dynamic.lean b/Complexity/Computability/WordRAM/Dynamic.lean index ab80464d6..ace6fd08a 100644 --- a/Complexity/Computability/WordRAM/Dynamic.lean +++ b/Complexity/Computability/WordRAM/Dynamic.lean @@ -14,6 +14,10 @@ public import Complexity.Computability.WordRAM.Trace /-! # Fixed-program implementations of dynamic data structures +`DynamicProgram` depends only on the existing `PFunctor` operation signature and its instruction +basis, which defaults to `.standard`. Deterministic and relational specifications supply their +signatures by coercion; correctness certificates retain the actual specification separately. + An implementation has one structured program, one output register, and a register-only calling convention. Each call replaces designated input registers with encoded words, preserving all other registers and the entire memory. Argument encoding, external delivery, and result decoding @@ -74,39 +78,41 @@ universe u v z variable {r : ℕ} {D : Stateful.Spec.{u, v, z}} -/-- A single program serving all operations through a fixed register calling convention. -/ -structure DynamicProgram (D : Stateful.Spec.{u, v, z}) (r : ℕ) where +/-- A single program serving an operation signature through a fixed register calling convention. +Deterministic and relational stateful specifications supply their signatures by coercion; +the program itself makes no assumption about abstract states or permitted transitions. -/ +structure DynamicProgram (S : PFunctor.{v, z}) (r : ℕ) (B : Basis := .standard) where /-- The same code is executed for every operation. -/ - code : Code .standard r + code : Code B r /-- Register returned by the final halt instruction. -/ output : Fin r /-- Registers overwritten by externally supplied operation arguments. -/ input : Fin r → Bool /-- Encoded arguments, independent of the maintained machine state. -/ - encode : D.Op → Fin r → ℕ + encode : S.A → Fin r → ℕ /-- Interpretation of the final register tuple as the operation's typed observation. -/ - decode : (o : D.Op) → (Fin r → ℕ) → Option (D.Result o) + decode : (o : S.A) → (Fin r → ℕ) → Option (S.B o) namespace DynamicProgram -variable (P : DynamicProgram D r) +variable {S : PFunctor.{v, z}} {B : Basis} (P : DynamicProgram S r B) /-- Deliver operation arguments without changing maintained memory or non-input registers. -/ -def prepare (o : D.Op) (x : Data r) : Data r where +def prepare (o : S.A) (x : Data r) : Data r where regs i := if P.input i then P.encode o i else x.regs i mem := x.mem /-- Delivering operation arguments preserves the machine memory unchanged. -/ @[simp] -theorem prepare_mem (o : D.Op) (x : Data r) : (P.prepare o x).mem = x.mem := rfl +theorem prepare_mem (o : S.A) (x : Data r) : (P.prepare o x).mem = x.mem := rfl /-- Registers outside the calling convention retain their contents. -/ -theorem prepare_regs_of_not_input {i : Fin r} (hi : P.input i = false) (o : D.Op) (x : Data r) : +theorem prepare_regs_of_not_input {i : Fin r} (hi : P.input i = false) (o : S.A) (x : Data r) : (P.prepare o x).regs i = x.regs i := by simp [prepare, hi] /-- Argument delivery preserves bounded words when the arguments fit. -/ -theorem prepare_bounded {w : ℕ} {o : D.Op} {x : Data r} (hx : x.Bounded w) +theorem prepare_bounded {w : ℕ} {o : S.A} {x : Data r} (hx : x.Bounded w) (ho : ∀ i, P.input i = true → Fits w (P.encode o i)) : (P.prepare o x).Bounded w := by refine ⟨fun i => ?_, hx.2⟩ change Fits w (if P.input i then P.encode o i else x.regs i) @@ -120,7 +126,7 @@ instructions; exhaustion, a machine fault, or decoder failure returns `none`. This interpreter does not check the initial state's global word bound or operation admissibility. Use `DynamicCertificate.execOp_of_eval` to connect a successful call to certified execution. -/ -def eval (w fuel : ℕ) (o : D.Op) (x : Data r) : Option (Data r × D.Result o × ℕ) := +def eval (w fuel : ℕ) (o : S.A) (x : Data r) : Option (Data r × S.B o × ℕ) := match P.code.eval w fuel (P.prepare o x) with | none => none | some (y, n) => (P.decode o y.regs).map fun a => (y, a, n + 1) @@ -133,13 +139,13 @@ structure OperationTrace where probes : List Probe /-- A bounded machine call with native execution evidence independent of any resource bound. -/ -def ExecOp (w : ℕ) : Stateful.Execution D (Data r) OperationTrace := fun x o a y trace => +def ExecOp (w : ℕ) : Stateful.Execution S (Data r) OperationTrace := fun x o a y trace => (P.prepare o x).Bounded w ∧ ∃ n, TraceExec w P.code (P.prepare o x) y n trace.probes ∧ P.decode o y.regs = some a ∧ trace.instructions = n + 1 /-- Successful typed evaluation supplies an exact operation trace when the delivered state is bounded. Evaluator success alone does not imply a bound on untouched memory. -/ -theorem ExecOp.of_eval {w fuel k : ℕ} {o : D.Op} {x y : Data r} {a : D.Result o} +theorem ExecOp.of_eval {w fuel k : ℕ} {o : S.A} {x y : Data r} {a : S.B o} (hb : (P.prepare o x).Bounded w) (h : P.eval w fuel o x = some (y, a, k)) : ∃ probes, P.ExecOp w x o a y ⟨k, probes⟩ := by cases he : P.code.eval w fuel (P.prepare o x) with @@ -156,7 +162,7 @@ theorem ExecOp.of_eval {w fuel k : ℕ} {o : D.Op} {x y : Data r} {a : D.Result /-- Every call is an execution of one fixed flat machine program. The returned register tuple is observed at the final state; the designated output register is also the halt value. -/ -theorem ExecOp.executes {w : ℕ} {x y : Data r} {o : D.Op} {a : D.Result o} +theorem ExecOp.executes {w : ℕ} {x y : Data r} {o : S.A} {a : S.B o} {trace : OperationTrace} (h : P.ExecOp w x o a y trace) : Executes w (P.code.program P.output) ⟨0, P.prepare o x⟩ (y.regs P.output) trace.instructions := by @@ -165,13 +171,13 @@ theorem ExecOp.executes {w : ℕ} {x y : Data r} {o : D.Op} {a : D.Result o} exact he.exec.executes P.output /-- A call preserves bounded words, including every word in the observed register tuple. -/ -theorem ExecOp.bounded {w : ℕ} {x y : Data r} {o : D.Op} {a : D.Result o} +theorem ExecOp.bounded {w : ℕ} {x y : Data r} {o : S.A} {a : S.B o} {trace : OperationTrace} (h : P.ExecOp w x o a y trace) : y.Bounded w := by obtain ⟨hb, n, he, _, _⟩ := h exact he.exec.bounded hb /-- Calls are deterministic in their final representation, observation, and instruction count. -/ -theorem ExecOp.unique {w : ℕ} {x y y' : Data r} {o : D.Op} {a a' : D.Result o} +theorem ExecOp.unique {w : ℕ} {x y y' : Data r} {o : S.A} {a a' : S.B o} {trace trace' : OperationTrace} (h : P.ExecOp w x o a y trace) (h' : P.ExecOp w x o a' y' trace') : y = y' ∧ a = a' ∧ trace.instructions = trace'.instructions := by @@ -181,8 +187,8 @@ theorem ExecOp.unique {w : ℕ} {x y y' : Data r} {o : D.Op} {a a' : D.Result o} exact ⟨rfl, Option.some.inj (ha.symm.trans ha'), ht.trans ht'.symm⟩ /-- A call's final state and instruction count agree with any structured execution of the code. -/ -theorem ExecOp.eq_of_exec {P : DynamicProgram D r} {w : ℕ} {x y z : Data r} {o : D.Op} - {a : D.Result o} {trace : OperationTrace} {m : ℕ} (he : P.ExecOp w x o a y trace) +theorem ExecOp.eq_of_exec {P : DynamicProgram S r B} {w : ℕ} {x y z : Data r} {o : S.A} + {a : S.B o} {trace : OperationTrace} {m : ℕ} (he : P.ExecOp w x o a y trace) (h : Exec w P.code (P.prepare o x) z m) : y = z ∧ trace.instructions = m + 1 := by obtain ⟨_, n, ht, _, hn⟩ := he @@ -190,7 +196,7 @@ theorem ExecOp.eq_of_exec {P : DynamicProgram D r} {w : ℕ} {x y z : Data r} {o exact ⟨rfl, hn⟩ /-- The write transcript supplies a frame rule for a call, including register-only delivery. -/ -theorem ExecOp.frame {w : ℕ} {x y : Data r} {o : D.Op} {a : D.Result o} +theorem ExecOp.frame {w : ℕ} {x y : Data r} {o : S.A} {a : S.B o} {trace : OperationTrace} (h : P.ExecOp w x o a y trace) {Q : HeapAssertion} (hQ : Q.holds x.mem) (hd : Disjoint Q.footprint (Probe.writes trace.probes)) : Q.holds y.mem := by diff --git a/Complexity/Computability/WordRAM/RelSubroutine.lean b/Complexity/Computability/WordRAM/RelSubroutine.lean new file mode 100644 index 000000000..ba187f24c --- /dev/null +++ b/Complexity/Computability/WordRAM/RelSubroutine.lean @@ -0,0 +1,613 @@ +/- +Copyright (c) 2026 Renfei Zhou. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Renfei Zhou +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Complexity.Computability.WordRAM.Subroutine +public import Complexity.Foundations.Semantics.Stateful.Relational + +/-! +# Relational Word-RAM dynamic certificates and composable data-structure subroutines + +This file extends `Complexity.Computability.WordRAM.Dynamic` and +`Complexity.Computability.WordRAM.Subroutine` from deterministic specifications (`Stateful.Spec`) +to relational specifications (`Stateful.RelSpec`): + +1. **Standalone relational fixed-program certificates (`RelDynamicCertificate`):** Generalizes + `DynamicCertificate` over `DynamicProgram D r` for `D : Stateful.RelSpec`, where `execute` + witnesses an angelic outcome `(s', a)` satisfying `D.step s o s' a`. Because the Word-RAM + structured semantics `Exec` is deterministic, `RelDynamicCertificate.realization` establishes + `Stateful.RelRealization` directly via `Exec.unique`. +2. **Unified standalone and inline relational subroutine certificates (`RelSubroutineDS`):** + Extends `SubroutineBase P D.Result D.valid w base params s₀` over `D : Stateful.RelSpec`, + inheriting `toDynamicProgram`, `initFresh_exec`, `initFresh_rename`, + `initClear_exec`, `initClear_rename`, `frame_caller`, and `prepareRegs` without duplication, + omitting `rep_unique` (since concrete potential `potential : Data r_ds → ℕ` depends only on + machine data), and existentially quantifying over `(s', a)` with `D.step s o s' a` in + `step_exec`. +3. **Zero-cost embedding of deterministic subroutines (`SubroutineDS.toRelSubroutineDS`):** + Every `SubroutineDS P D w base params s₀` embeds into + `RelSubroutineDS P D.toRelSpec w base params s₀`. +4. **Basis lifting, standalone `Stateful` projections, inline subroutine calls, and flat-program + operation sequences:** Provides `mapSimulates`, `toStandard`, `toRelCertificate`, + `toRelAmortized`, `toRelBound`, `run_client_amortized`, `run_client_bound`, `code_rename`, + `call_rename`, and flat-program sequence execution (`RunsRelOps`, `seq_runsRelOps`, + `run_ops_exact_fresh`, `run_ops_exact_reusable`). + +## Main definitions + +* `Cslib.WordRAM.RelDynamicCertificate`: standalone fixed-program certificate for + `DynamicProgram D r` realizing a relational specification `D : Stateful.RelSpec`. +* `Cslib.WordRAM.RelSubroutineDS`: unified standalone and inline subroutine specification and + amortized resource certificate for `P : SubroutineCode B r_ds k_init` realizing + `D : Stateful.RelSpec` (extending `SubroutineBase` without `rep_unique`). +* `Cslib.WordRAM.SubroutineDS.toRelSubroutineDS`: embedding of a deterministic `SubroutineDS` into + `RelSubroutineDS`. +* `Cslib.WordRAM.RelSubroutineDS.RunsRelOps`: sequential flat-program execution of an operation list + recording the resulting abstract transcript `D.Transcript`. + +## Main results + +* `Cslib.WordRAM.RelDynamicCertificate.realization`, + `Cslib.WordRAM.RelDynamicCertificate.instruction_bound`: standalone `Stateful.RelRealization` + and `Stateful.RelBound` certificates. +* `Cslib.WordRAM.RelSubroutineDS.toRelAmortized`, + `Cslib.WordRAM.RelSubroutineDS.run_client_amortized`: standalone amortized potential certificate + and adaptive-client bound over `Basis.standard`. +* `Cslib.WordRAM.RelSubroutineDS.code_rename`, + `Cslib.WordRAM.RelSubroutineDS.call_rename`: relocated inline operation calls with angelic + relational step outcomes. +* `Cslib.WordRAM.RelSubroutineDS.run_ops_exact_fresh`, + `Cslib.WordRAM.RelSubroutineDS.run_ops_exact_reusable`: end-to-end flat-program execution of any + demonically admissible operation sequence `D.ValidOpSeq s₀ ops`. + +## References + +* [T. H. Cormen, C. E. Leiserson, R. L. Rivest and C. Stein, *Introduction to Algorithms*, + Chapter 17][CLRS2009] +* [J. C. Reynolds, *Separation Logic: A Logic for Shared Mutable Data Structures*][Reynolds2002] +-/ + +@[expose] public section + +namespace Cslib.WordRAM + +universe u v z + +/-! ## Standalone relational dynamic certificates (`RelDynamicCertificate`) -/ + +variable {r : ℕ} {D : Stateful.RelSpec.{u, v, z}} + +/-- Per-operation angelic machine proof obligations for a `DynamicProgram D r` over a relational +specification `D : Stateful.RelSpec`. -/ +structure RelDynamicCertificate (P : DynamicProgram D r) (w : ℕ) where + /-- Abstract state represented by the persistent machine data. -/ + rep : D.State → Data r → Prop + /-- Represented configurations contain only machine words. -/ + bounded : ∀ {s x}, rep s x → x.Bounded w + /-- Admissible arguments fit the machine's word size. -/ + arguments_fit : ∀ {s} (o), D.valid s o → ∀ i, P.input i = true → Fits w (P.encode o i) + /-- A machine execution implements an admissible operation by realizing some permitted + `(s', a)` with `D.step s o s' a`. -/ + execute : ∀ {s x} (o), rep s x → D.valid s o → + ∃ y s' a n, Exec w P.code (P.prepare o x) y n ∧ + D.step s o s' a ∧ P.decode o y.regs = some a ∧ rep s' y + +/-- Embed a deterministic `DynamicCertificate` as a `RelDynamicCertificate`. -/ +def DynamicCertificate.toRelDynamicCertificate {D : Stateful.Spec.{u, v, z}} {r w : ℕ} + {P : DynamicProgram D r} (C : DynamicCertificate P w) : + RelDynamicCertificate (D := D.toRelSpec) + ⟨P.code, P.output, P.input, P.encode, P.decode⟩ w where + rep := C.rep + bounded := C.bounded + arguments_fit := C.arguments_fit + execute o hr hv := by + obtain ⟨y, n, he, hdec, hrep⟩ := C.execute o hr hv + exact ⟨y, (D.step _ o).1, (D.step _ o).2, n, he, rfl, hdec, hrep⟩ + +namespace RelDynamicCertificate + +variable {P : DynamicProgram D r} {w : ℕ} (C : RelDynamicCertificate P w) + +/-- Turn a successful typed evaluation on a represented state and admissible operation into +certified `ExecOp` evidence. -/ +theorem execOp_of_eval {s : D.State} {x y : Data r} {o : D.Op} {a : D.Result o} {fuel k : ℕ} + (hr : C.rep s x) (hv : D.valid s o) (h : P.eval w fuel o x = some (y, a, k)) : + ∃ probes, P.ExecOp w x o a y ⟨k, probes⟩ := + DynamicProgram.ExecOp.of_eval P + (P.prepare_bounded (C.bounded hr) (C.arguments_fit o hv)) h + +/-- Fixed-program relational certificates realize `D` via `Stateful.RelRealization`. -/ +def realization : Stateful.RelRealization D (P.ExecOp w) where + rep := C.rep + total o hr hv := by + obtain ⟨y, _, a, n, he, _, ha, _⟩ := C.execute o hr hv + obtain ⟨ts, hts⟩ := he.exists_trace + exact ⟨a, y, ⟨n + 1, ts⟩, + P.prepare_bounded (C.bounded hr) (C.arguments_fit o hv), n, hts, ha, rfl⟩ + correct hr hv he := by + obtain ⟨y, s', a, n, he', hstep, ha, hr'⟩ := C.execute _ hr hv + obtain ⟨_, _, ht, ha', _⟩ := he + obtain ⟨rfl, rfl⟩ := ht.exec.unique he' + have h_eq : _ = a := Option.some.inj (ha'.symm.trans ha) + subst h_eq + exact ⟨s', hstep, hr'⟩ + +/-- A separate per-operation instruction estimate supplies a reusable `Stateful.RelBound`. -/ +theorem instruction_bound (charge : D.State → D.Op → ℕ) + (hbound : ∀ {s x} (o) {y n}, C.rep s x → D.valid s o → + Exec w P.code (P.prepare o x) y n → n + 1 ≤ charge s o) : + Stateful.RelBound C.realization DynamicProgram.OperationTrace.instructions charge := by + intro s x o a y trace hr hv he + obtain ⟨_, n, ht, _, hn⟩ := he + rw [hn] + exact hbound o hr hv ht.exec + +/-- A uniform constant instruction bound follows from an existential per-operation execution. -/ +theorem instruction_bound_const (K : ℕ) + (hbound : ∀ {s x} (o), C.rep s x → D.valid s o → + ∃ y n, Exec w P.code (P.prepare o x) y n ∧ n + 1 ≤ K) : + Stateful.RelBound C.realization DynamicProgram.OperationTrace.instructions + (fun _ _ => K) := by + apply C.instruction_bound + intro s x o y n hr hv he + obtain ⟨_, _, he', hn⟩ := hbound o hr hv + obtain ⟨_, rfl⟩ := he.unique he' + exact hn + +/-- Adaptive client execution, final representation, and linear total instruction bound for a +uniform constant per-operation instruction certificate. -/ +theorem run_client_const {K : ℕ} + (B : Stateful.RelBound C.realization DynamicProgram.OperationTrace.instructions + (fun _ _ => K)) + {α : Type*} (p : D.Client α) {s : D.State} {x : Data r} + (hr : C.rep s x) (hv : D.DemonicValid s p) : + ∃ a y traces s' tr, + Stateful.Executes (P.ExecOp w) p x a y traces ∧ + D.ClientRun s p a s' tr ∧ + C.rep s' y ∧ + traces.length = tr.length ∧ + Stateful.total DynamicProgram.OperationTrace.instructions traces ≤ tr.length * K := by + obtain ⟨a, y, traces, s', tr, he, hrun, hr', hlen, hcost⟩ := B.exists_executes p hr hv + rw [D.totalCost_const] at hcost + exact ⟨a, y, traces, s', tr, he, hrun, hr', hlen, hcost⟩ + +/-- Lift a per-operation pointwise memory frame from `Exec` to `ExecOp`. -/ +theorem frame_of_execute {j : ℕ} + (hframe : ∀ {s x} (o), C.rep s x → D.valid s o → + ∃ z n, Exec w P.code (P.prepare o x) z n ∧ z.mem j = x.mem j) + {s : D.State} {x y : Data r} {o : D.Op} {a : D.Result o} + {trace : DynamicProgram.OperationTrace} + (hr : C.rep s x) (hv : D.valid s o) (he : P.ExecOp w x o a y trace) : + y.mem j = x.mem j := by + obtain ⟨z, n, hz, hf⟩ := hframe o hr hv + exact (he.eq_of_exec hz).1 ▸ hf + +/-- Lift a per-operation `Modifies` bound from `Exec` to `ExecOp`. -/ +theorem modifies_of_execute {F : Finset ℕ} + (hmod : ∀ {s x} (o), C.rep s x → D.valid s o → + ∃ z n, Exec w P.code (P.prepare o x) z n ∧ Modifies F x.mem z.mem) + {s : D.State} {x y : Data r} {o : D.Op} {a : D.Result o} + {trace : DynamicProgram.OperationTrace} + (hr : C.rep s x) (hv : D.valid s o) (he : P.ExecOp w x o a y trace) : + Modifies F x.mem y.mem := + fun j hj => C.frame_of_execute + (fun o hr' hv' => let ⟨z, n, hz, hf⟩ := hmod o hr' hv'; ⟨z, n, hz, hf j hj⟩) + hr hv he + +/-- Lift a per-operation pointwise memory frame across a demonically admissible adaptive client +execution. -/ +theorem executes_client_frame {j : ℕ} + (hframe : ∀ {s x y o a trace}, C.rep s x → D.valid s o → + P.ExecOp w x o a y trace → y.mem j = x.mem j) + {α : Type*} {p : D.Client α} {s : D.State} {x y : Data r} {val : α} + {traces : List DynamicProgram.OperationTrace} + (hr : C.rep s x) (hv : D.DemonicValid s p) + (he : Stateful.Executes (P.ExecOp w) p x val y traces) : + y.mem j = x.mem j := by + induction he generalizing s with + | pure _ _ => rfl + | @call o k c a c' trace _ _ _ hstep _ ih => + obtain ⟨hv_o, hv_k⟩ := (D.demonicValid_liftBind_iff s o k).mp hv + obtain ⟨s', hstep_d, hr'⟩ := C.realization.correct hr hv_o hstep + exact (ih hr' (hv_k s' a hstep_d)).trans (hframe hr hv_o hstep) + +/-- Lift a per-operation `Modifies` bound across a demonically admissible adaptive client +execution. -/ +theorem executes_client_modifies {F : Finset ℕ} + (hmod : ∀ {s x y o a trace}, C.rep s x → D.valid s o → + P.ExecOp w x o a y trace → Modifies F x.mem y.mem) + {α : Type*} {p : D.Client α} {s : D.State} {x y : Data r} {val : α} + {traces : List DynamicProgram.OperationTrace} + (hr : C.rep s x) (hv : D.DemonicValid s p) + (he : Stateful.Executes (P.ExecOp w) p x val y traces) : + Modifies F x.mem y.mem := + fun j hj => C.executes_client_frame + (fun hr' hv' he' => hmod hr' hv' he' j hj) hr hv he + +/-- Memory probes are bounded by the instruction charge. -/ +theorem probe_bound (charge : D.State → D.Op → ℕ) + (B : Stateful.RelBound C.realization DynamicProgram.OperationTrace.instructions charge) : + Stateful.RelBound C.realization (fun t => t.probes.length) charge := by + intro s x o a y trace hr hv he + have hb := B hr hv he + obtain ⟨_, n, ht, _, hn⟩ := he + exact ht.length_le.trans ((Nat.le_add_right n 1).trans (hn ▸ hb)) + +end RelDynamicCertificate + +/-! ## `RelSubroutineDS` and `SubroutineDS.toRelSubroutineDS` -/ + +namespace SubroutineCode + +variable {B : Basis} {r_ds k_init : ℕ} + +/-- Compiled flat `Program B r_ds` for `O(1)` fresh zeroed-memory initialization. -/ +def initFreshProg (P : SubroutineCode B r_ds k_init) : Program B r_ds := + P.initFresh.program P.output + +/-- Compiled flat `Program B r_ds` for `O(space)` dirty-memory reusable initialization. -/ +def initClearProg (P : SubroutineCode B r_ds k_init) : Program B r_ds := + P.initClear.program P.output + +/-- Compiled flat `Program B r_ds` serving all operations of the data structure. -/ +def opProg (P : SubroutineCode B r_ds k_init) : Program B r_ds := + P.code.program P.output + +end SubroutineCode + +/-- Unified standalone and subroutine specification/resource certificate for +`P : SubroutineCode B r_ds k_init` realizing a relational specification `D : Stateful.RelSpec` +at word width `w`, base address `base`, runtime parameters `params`, and initial state `s₀`. + +Extends `SubroutineBase P D.Result D.valid w base params s₀` (inheriting `toDynamicProgram`, +`initFresh_exec`, `initFresh_rename`, `initClear_exec`, `initClear_rename`, +`frame_caller`, and `prepareRegs`), omits `rep_unique` (because concrete potential +`potential : Data r_ds → ℕ` depends only on machine data), and existentially quantifies over a +permitted outcome `(s', a)` satisfying `D.step s o s' a` in `step_exec`. -/ +structure RelSubroutineDS {B : Basis} {r_ds k_init : ℕ} (P : SubroutineCode B r_ds k_init) + (D : Stateful.RelSpec.{u, v, z}) (w base : ℕ) (params : Fin k_init → ℕ) (s₀ : D.State) + extends SubroutineBase P D.Result D.valid w base params s₀ where + /-- Angelic execution of `P.code` for any admissible operation `o` whose arguments match `x.regs`, + witnessing some permitted outcome `(s', a)` with `D.step s o s' a`. -/ + step_exec : ∀ {s : D.State} {x : Data r_ds} (o : D.Op), + rep s x → D.valid s o → argMatch o x.regs → + ∃ y s' a n, Exec w P.code x y n ∧ + D.step s o s' a ∧ + decode o y.regs = some a ∧ + rep s' y ∧ + Modifies (Block.mk base space).footprint x.mem y.mem ∧ + n + potential y ≤ charge s o + potential x + +/-- Embed any deterministic `SubroutineDS P D w base params s₀` as a relational +`RelSubroutineDS P D.toRelSpec w base params s₀`. -/ +def SubroutineDS.toRelSubroutineDS {B : Basis} {r_ds k_init w base : ℕ} + {P : SubroutineCode B r_ds k_init} {D : Stateful.Spec.{u, v, z}} + {params : Fin k_init → ℕ} {s₀ : D.State} + (C : SubroutineDS P D w base params s₀) : + RelSubroutineDS P D.toRelSpec w base params s₀ where + toSubroutineBase := C.toSubroutineBase + step_exec o hr hv harg := by + obtain ⟨y, n, he, hdec, hrep, hmod, hpot⟩ := C.step_exec o hr hv harg + exact ⟨y, (D.step _ o).1, (D.step _ o).2, n, he, rfl, hdec, hrep, hmod, hpot⟩ + +namespace RelSubroutineDS + +variable {B B' : Basis} {r_ds r_caller k_init w base : ℕ} + {P : SubroutineCode B r_ds k_init} {D : Stateful.RelSpec.{u, v, z}} + {params : Fin k_init → ℕ} {s₀ : D.State} + +/-! ### Projection 1: Basis lifting and standalone `DynamicProgram` / `Stateful` theorems -/ + +/-- Lift a `RelSubroutineDS` certificate along any defined-evaluation basis simulation. -/ +def mapSimulates (φ : B.Simulates B') (C : RelSubroutineDS P D w base params s₀) : + RelSubroutineDS (P.mapSimulates φ) D w base params s₀ where + toSubroutineBase := C.toSubroutineBase.mapSimulates φ + step_exec o hr hv harg := + let ⟨y, s', a, n, he, hstep, hdec, hrep, hmod, hpot⟩ := C.step_exec o hr hv harg + ⟨y, s', a, n, he.mapSimulates φ, hstep, hdec, hrep, hmod, hpot⟩ + +/-- Lift an exact-basis `RelSubroutineDS` certificate to `Basis.standard`. -/ +abbrev toStandard {P : SubroutineCode .exact r_ds k_init} + (C : RelSubroutineDS P D w base params s₀) : + RelSubroutineDS P.toStandard D w base params s₀ := + C.mapSimulates .exactStandard + +section Standard + +variable {P_std : SubroutineCode .standard r_ds k_init} + (C : RelSubroutineDS P_std D w base params s₀) + +/-- Project a `.standard`-basis `RelSubroutineDS` to a standalone `RelDynamicCertificate`. -/ +def toRelCertificate : RelDynamicCertificate (D := D) C.toDynamicProgram w where + rep := C.rep + bounded := C.bounded + arguments_fit := C.arguments_fit + execute {s x} o hr hv := by + have hb' : (C.toDynamicProgram.prepare o x).Bounded w := + C.toDynamicProgram.prepare_bounded (C.bounded hr) (C.arguments_fit o hv) + have hr' : C.rep s (C.toDynamicProgram.prepare o x) := + C.rep_supported + (fun i hi => C.toDynamicProgram.prepare_regs_of_not_input hi o x) + (fun _ _ => rfl) hb' hr + have harg : C.argMatch o (C.toDynamicProgram.prepare o x).regs := + C.encode_matches o x.regs + obtain ⟨y, s', a, n, he, hstep, hdec, hrep, _, _⟩ := C.step_exec o hr' hv harg + exact ⟨y, s', a, n, he, hstep, hdec, hrep⟩ + +/-- Project a `.standard`-basis `RelSubroutineDS` to a standalone `Stateful.RelAmortized` +certificate, charging `+ 1` per operation for `DynamicProgram`'s trailing `halt` instruction. -/ +theorem toRelAmortized : + Stateful.RelAmortized C.toRelCertificate.realization + DynamicProgram.OperationTrace.instructions + (fun _ x => C.potential x) + (fun s o => C.charge s o + 1) where + step {s x o a y trace} hr hv he := by + have hb' : (C.toDynamicProgram.prepare o x).Bounded w := + C.toDynamicProgram.prepare_bounded (C.bounded hr) (C.arguments_fit o hv) + have hr' : C.rep s (C.toDynamicProgram.prepare o x) := + C.rep_supported + (fun i hi => C.toDynamicProgram.prepare_regs_of_not_input hi o x) + (fun _ _ => rfl) hb' hr + have hp_eq : C.potential (C.toDynamicProgram.prepare o x) = C.potential x := + C.potential_supported + (fun i hi => C.toDynamicProgram.prepare_regs_of_not_input hi o x) + (fun _ _ => rfl) hr + have harg : C.argMatch o (C.toDynamicProgram.prepare o x).regs := + C.encode_matches o x.regs + obtain ⟨z, s', a', m, hz, hstep, hdec, hrep, _, hpot⟩ := C.step_exec o hr' hv harg + obtain ⟨rfl, hinst⟩ := he.eq_of_exec hz + obtain ⟨_, _, _, ha_trace, _⟩ := he + have ha_eq : a = a' := Option.some.inj (ha_trace.symm.trans hdec) + subst ha_eq + refine ⟨s', hstep, hrep, ?_⟩ + rw [hinst] + rw [hp_eq] at hpot + omega + +/-- When `C.potential` is identically zero, `RelSubroutineDS` projects directly to a worst-case +`Stateful.RelBound` certificate. -/ +theorem toRelBound (hpot : ∀ x, C.potential x = 0) : + Stateful.RelBound C.toRelCertificate.realization + DynamicProgram.OperationTrace.instructions + (fun s o => C.charge s o + 1) := by + intro s x o a y trace hr hv he + obtain ⟨_, _, _, h⟩ := C.toRelAmortized.step hr hv he + simp only [hpot] at h + omega + +/-- Standalone adaptive-client amortized bound for any demonically admissible client `p`. -/ +theorem run_client_amortized {α : Type*} (p : D.Client α) {s : D.State} {x : Data r_ds} + (hr : C.rep s x) (hv : D.DemonicValid s p) : + ∃ a y traces s' tr, + Stateful.Executes (C.toDynamicProgram.ExecOp w) p x a y traces ∧ + D.ClientRun s p a s' tr ∧ + C.rep s' y ∧ + traces.length = tr.length ∧ + Stateful.total DynamicProgram.OperationTrace.instructions traces + C.potential y ≤ + D.totalCost (fun s o => C.charge s o + 1) s tr + C.potential x := + C.toRelAmortized.run_client_amortized p hr hv + +/-- Standalone adaptive-client worst-case bound when `C.potential` is identically zero. -/ +theorem run_client_bound (hpot : ∀ x, C.potential x = 0) {α : Type*} (p : D.Client α) + {s : D.State} {x : Data r_ds} (hr : C.rep s x) (hv : D.DemonicValid s p) : + ∃ a y traces s' tr, + Stateful.Executes (C.toDynamicProgram.ExecOp w) p x a y traces ∧ + D.ClientRun s p a s' tr ∧ + C.rep s' y ∧ + traces.length = tr.length ∧ + Stateful.total DynamicProgram.OperationTrace.instructions traces ≤ + D.totalCost (fun s o => C.charge s o + 1) s tr := + Stateful.RelBound.exists_executes (C.toRelBound hpot) p hr hv + +end Standard + +/-! ### Projection 2: Relocated inline subroutine calls (`code_rename`, `call_rename`) -/ + +/-- Execute `P.code.rename f` inline when arguments already match `(x.comap f).regs`, +witnessing a permitted outcome `(s', a)` with `D.step s o s' a`. -/ +theorem code_rename (C : RelSubroutineDS P D w base params s₀) + {f : Fin r_ds → Fin r_caller} (hf : f.Injective) + {s : D.State} {x : Data r_caller} (o : D.Op) + (hr : C.rep s (x.comap f)) (hv : D.valid s o) (harg : C.argMatch o (x.comap f).regs) : + ∃ z s' a t, ExecWithin w (P.code.rename f) x z t ∧ + D.step s o s' a ∧ + C.decode o (z.comap f).regs = some a ∧ + C.rep s' (z.comap f) ∧ + Modifies (Block.mk base C.space).footprint x.mem z.mem ∧ + (∀ j ∉ Set.range f, z.regs j = x.regs j) ∧ + t + C.potential (z.comap f) ≤ C.charge s o + C.potential (x.comap f) := by + obtain ⟨y, s', a, n, he, hstep, hdec, hrep, hmod, hpot⟩ := C.step_exec o hr hv harg + refine ⟨y.extend f x, s', a, n, (he.rename hf).within, hstep, ?_, ?_, hmod, + fun j hj => Data.extend_regs_of_notMem y x hj, ?_⟩ + · rw [Data.comap_extend hf] + exact hdec + · rw [Data.comap_extend hf] + exact hrep + · rw [Data.comap_extend hf] + exact hpot + +/-- Execute `.seq prep (P.code.rename f)` inline after a caller argument-preparation block `prep` +that preserves persistent DS registers and memory in `[base, base + C.space)`. -/ +theorem call_rename (C : RelSubroutineDS P D w base params s₀) + {f : Fin r_ds → Fin r_caller} (hf : f.Injective) + {s : D.State} {x x' : Data r_caller} {prep : Code B r_caller} {t_prep : ℕ} (o : D.Op) + (hr : C.rep s (x.comap f)) (hv : D.valid s o) + (hprep : ExecWithin w prep x x' t_prep) + (hb' : (x'.comap f).Bounded w) + (hreg : ∀ i, P.input i = false → x'.regs (f i) = x.regs (f i)) + (hmem : ∀ a ∈ (Block.mk base C.space).footprint, x'.mem a = x.mem a) + (harg : C.argMatch o (x'.comap f).regs) : + ∃ z s' a t, ExecWithin w (.seq prep (P.code.rename f)) x z t ∧ + D.step s o s' a ∧ + C.decode o (z.comap f).regs = some a ∧ + C.rep s' (z.comap f) ∧ + Modifies (Block.mk base C.space).footprint x'.mem z.mem ∧ + (∀ j ∉ Set.range f, z.regs j = x'.regs j) ∧ + t + C.potential (z.comap f) ≤ t_prep + C.charge s o + C.potential (x.comap f) := by + have hr' : C.rep s (x'.comap f) := + C.rep_supported (x := x.comap f) (x' := x'.comap f) hreg hmem hb' hr + have hp_eq : C.potential (x'.comap f) = C.potential (x.comap f) := + C.potential_supported (x := x.comap f) (x' := x'.comap f) hreg hmem hr + obtain ⟨z, s', a, t_op, he_op, hstep, hdec, hrep_z, hmod_z, hregs_z, hpot_z⟩ := + C.code_rename hf o hr' hv harg + exact ⟨z, s', a, t_prep + t_op, hprep.seq he_op, hstep, hdec, hrep_z, hmod_z, hregs_z, + by rw [hp_eq] at hpot_z; omega⟩ + +/-! ### Projection 3: Flat-program operation-sequence execution (`RunsRelOps`, `run_ops_exact_*`) -/ + +/-- Sequential flat-program execution of `ops : List D.Op` by repeated calls to `P.opProg`: +each call prepares input registers via `prepRegs`, runs `P.opProg` from `pc = 0` to `halt`, +decodes a permitted outcome `(s₁, a)` with `D.step s o s₁ a`, modifies memory only within +`[base, base + C.space)`, records the resulting transcript `tr : D.Transcript`, and accumulates +total instructions executed (including each trailing `halt`). -/ +inductive RunsRelOps (C : RelSubroutineDS P D w base params s₀) + (prepRegs : D.Op → (Fin r_ds → ℕ) → (Fin r_ds → ℕ) := C.prepareRegs) : + D.State → Data r_ds → List D.Op → D.Transcript → D.State → Data r_ds → ℕ → Prop + | nil {s x} : RunsRelOps C prepRegs s x [] [] s x 0 + | cons {s s₁ s₂ x o a ops tr pc y z steps rest} : + Runs w P.opProg ⟨0, ⟨prepRegs o x.regs, x.mem⟩⟩ ⟨pc, y⟩ steps → + (∃ out, step w P.opProg ⟨pc, y⟩ = .halt out) → + D.step s o s₁ a → + C.decode o y.regs = some a → + Modifies (Block.mk base C.space).footprint x.mem y.mem → + RunsRelOps C prepRegs s₁ y ops tr s₂ z rest → + RunsRelOps C prepRegs s x (o :: ops) (⟨o, a, s₁⟩ :: tr) s₂ z ((steps + 1) + rest) + +private theorem step_program_halt (c : Code B r_ds) (out : Fin r_ds) (y : Data r_ds) : + step w (c.program out) ⟨0 + c.size, y⟩ = .halt (y.regs out) := + step_halt (by + simp only [Code.program, Nat.zero_add, + List.getElem?_append_right (Code.length_assemble c 0).le, + Code.length_assemble, Nat.sub_self, List.getElem?_cons_zero]) + +/-- Flat-program sequence execution under a custom register-preparation function `prepRegs`. -/ +theorem seq_runsRelOps_of_prep (C : RelSubroutineDS P D w base params s₀) + (prepRegs : D.Op → (Fin r_ds → ℕ) → (Fin r_ds → ℕ)) + (hfit : ∀ {s} (o) (regs : Fin r_ds → ℕ), D.valid s o → (∀ i, Fits w (regs i)) → + ∀ i, Fits w (prepRegs o regs i)) + (hnot_in : ∀ (o : D.Op) (regs : Fin r_ds → ℕ) (i : Fin r_ds), + P.input i = false → prepRegs o regs i = regs i) + (harg : ∀ (o : D.Op) (regs : Fin r_ds → ℕ), C.argMatch o (prepRegs o regs)) : + ∀ (ops : List D.Op) {s : D.State} {x : Data r_ds}, + C.rep s x → D.ValidOpSeq s ops → + ∃ tr s_fin z t_ops, + C.RunsRelOps prepRegs s x ops tr s_fin z t_ops ∧ + tr.ops = ops ∧ + D.ValidTranscript s tr s_fin ∧ + C.rep s_fin z ∧ + t_ops + C.potential z ≤ + D.totalCost (fun st o => C.charge st o + 1) s tr + C.potential x + | [], s, x, hr, _ => + ⟨[], s, x, 0, .nil, rfl, .nil s, hr, Nat.zero_add (C.potential x) ▸ le_rfl⟩ + | o :: ops, s, x, hr, ⟨hv_o, hv_rest⟩ => by + let x' : Data r_ds := ⟨prepRegs o x.regs, x.mem⟩ + have hbx := C.bounded hr + have hb' : x'.Bounded w := ⟨hfit o x.regs hv_o hbx.1, hbx.2⟩ + have hreg' : ∀ i, P.input i = false → x'.regs i = x.regs i := + fun i hi => hnot_in o x.regs i hi + have hr' : C.rep s x' := C.rep_supported hreg' (fun _ _ => rfl) hb' hr + have hpot' : C.potential x' = C.potential x := + C.potential_supported hreg' (fun _ _ => rfl) hr + obtain ⟨y, s₁, a, m, he_op, hstep, hdec, hr_y, hmod, hpot_m⟩ := + C.step_exec o hr' hv_o (harg o x.regs) + rw [hpot'] at hpot_m + have hruns : Runs w P.opProg ⟨0, x'⟩ ⟨0 + P.code.size, y⟩ m := + he_op.runs (pc := 0) (p := P.opProg) (Program.Contains.self _).append_left + have hhalt : step w P.opProg ⟨0 + P.code.size, y⟩ = .halt (y.regs P.output) := + step_program_halt P.code P.output y + obtain ⟨tr, s_fin, z, rest, hrest, hops, htr, hr_z, hpot_rest⟩ := + seq_runsRelOps_of_prep C prepRegs hfit hnot_in harg ops hr_y (hv_rest s₁ a hstep) + refine ⟨⟨o, a, s₁⟩ :: tr, s_fin, z, (m + 1) + rest, + .cons hruns ⟨_, hhalt⟩ hstep hdec hmod hrest, + congrArg (o :: ·) hops, .cons _ hv_o hstep htr, hr_z, ?_⟩ + simp only [Stateful.RelSpec.totalCost_cons] + omega + +/-- Flat-program sequence execution using `C.prepareRegs`. -/ +theorem seq_runsRelOps (C : RelSubroutineDS P D w base params s₀) + (ops : List D.Op) {s : D.State} {x : Data r_ds} + (hr : C.rep s x) (hv : D.ValidOpSeq s ops) : + ∃ tr s_fin z t_ops, + C.RunsRelOps C.prepareRegs s x ops tr s_fin z t_ops ∧ + tr.ops = ops ∧ + D.ValidTranscript s tr s_fin ∧ + C.rep s_fin z ∧ + t_ops + C.potential z ≤ + D.totalCost (fun st o => C.charge st o + 1) s tr + C.potential x := by + refine C.seq_runsRelOps_of_prep C.prepareRegs ?_ ?_ C.encode_matches ops hr hv + · intro s' o regs hv_o hregs i + simp only [SubroutineBase.prepareRegs] + split_ifs with hi + · exact C.arguments_fit o hv_o i hi + · exact hregs i + · intro o regs i hi + simp [SubroutineBase.prepareRegs, hi] + +/-- Standalone flat-program operation-sequence theorem with `O(1)` fresh initialization on zeroed +`[base, base + C.space)`. -/ +theorem run_ops_exact_fresh (C : RelSubroutineDS P D w base params s₀) + {d₀ : Data r_ds} (hd₀ : d₀.Bounded w) + (hfr : d₀.regs P.frontierReg = base) + (hpar : ∀ j, d₀.regs (P.initParams j) = params j) + (hzero : ∀ a ∈ (Block.mk base C.space).footprint, d₀.mem a = 0) + (ops : List D.Op) (hv : D.ValidOpSeq s₀ ops) : + ∃ pc₀ x₀ t_init tr s_fin z t_ops, + Runs w P.initFreshProg ⟨0, d₀⟩ ⟨pc₀, x₀⟩ t_init ∧ + (∃ out, step w P.initFreshProg ⟨pc₀, x₀⟩ = .halt out) ∧ + Modifies (Block.mk base C.space).footprint d₀.mem x₀.mem ∧ + C.RunsRelOps C.prepareRegs s₀ x₀ ops tr s_fin z t_ops ∧ + tr.ops = ops ∧ + D.ValidTranscript s₀ tr s_fin ∧ + C.rep s_fin z ∧ + t_init ≤ C.initTime ∧ + t_ops + C.potential z ≤ + D.totalCost (fun st o => C.charge st o + 1) s₀ tr + C.initPotential := by + obtain ⟨x₀, ⟨t_init, ht_init, he_init⟩, _, hr₀, hpot₀, hmod₀⟩ := + C.initFresh_exec hd₀ hfr hpar hzero + have hruns₀ : Runs w P.initFreshProg ⟨0, d₀⟩ ⟨0 + P.initFresh.size, x₀⟩ t_init := + he_init.runs (pc := 0) (p := P.initFreshProg) (Program.Contains.self _).append_left + have hhalt₀ : step w P.initFreshProg ⟨0 + P.initFresh.size, x₀⟩ = .halt (x₀.regs P.output) := + step_program_halt P.initFresh P.output x₀ + obtain ⟨tr, s_fin, z, t_ops, hruns_ops, hops, htr, hr_z, hbound⟩ := + C.seq_runsRelOps ops hr₀ hv + exact ⟨_, x₀, t_init, tr, s_fin, z, t_ops, hruns₀, ⟨_, hhalt₀⟩, hmod₀, hruns_ops, + hops, htr, hr_z, ht_init, by omega⟩ + +/-- Standalone flat-program operation-sequence theorem with `O(space)` reusable initialization on +arbitrary `w`-bounded memory. -/ +theorem run_ops_exact_reusable (C : RelSubroutineDS P D w base params s₀) + {d₀ : Data r_ds} (hd₀ : d₀.Bounded w) + (hfr : d₀.regs P.frontierReg = base) + (hpar : ∀ j, d₀.regs (P.initParams j) = params j) + (ops : List D.Op) (hv : D.ValidOpSeq s₀ ops) : + ∃ pc₀ x₀ t_init tr s_fin z t_ops, + Runs w P.initClearProg ⟨0, d₀⟩ ⟨pc₀, x₀⟩ t_init ∧ + (∃ out, step w P.initClearProg ⟨pc₀, x₀⟩ = .halt out) ∧ + Modifies (Block.mk base C.space).footprint d₀.mem x₀.mem ∧ + C.RunsRelOps C.prepareRegs s₀ x₀ ops tr s_fin z t_ops ∧ + tr.ops = ops ∧ + D.ValidTranscript s₀ tr s_fin ∧ + C.rep s_fin z ∧ + t_init ≤ C.initTime + 4 * C.space + 6 ∧ + t_ops + C.potential z ≤ + D.totalCost (fun st o => C.charge st o + 1) s₀ tr + C.initPotential := by + obtain ⟨x₀, ⟨t_init, ht_init, he_init⟩, _, hr₀, hpot₀, hmod₀⟩ := + C.initClear_exec hd₀ hfr hpar + have hruns₀ : Runs w P.initClearProg ⟨0, d₀⟩ ⟨0 + P.initClear.size, x₀⟩ t_init := + he_init.runs (pc := 0) (p := P.initClearProg) (Program.Contains.self _).append_left + have hhalt₀ : step w P.initClearProg ⟨0 + P.initClear.size, x₀⟩ = .halt (x₀.regs P.output) := + step_program_halt P.initClear P.output x₀ + obtain ⟨tr, s_fin, z, t_ops, hruns_ops, hops, htr, hr_z, hbound⟩ := + C.seq_runsRelOps ops hr₀ hv + exact ⟨_, x₀, t_init, tr, s_fin, z, t_ops, hruns₀, ⟨_, hhalt₀⟩, hmod₀, hruns_ops, + hops, htr, hr_z, ht_init, by omega⟩ + +end RelSubroutineDS + +end Cslib.WordRAM diff --git a/Complexity/Computability/WordRAM/Subroutine.lean b/Complexity/Computability/WordRAM/Subroutine.lean index 2dae6c8e7..b6cd0bd1b 100644 --- a/Complexity/Computability/WordRAM/Subroutine.lean +++ b/Complexity/Computability/WordRAM/Subroutine.lean @@ -40,11 +40,12 @@ A data structure is packaged once as a `SubroutineCode B r_ds k_init` and certif ## Main definitions -* `Cslib.WordRAM.SubroutineCode`, `Cslib.WordRAM.SubroutineDS`: uniform relocatable machine code and - unified standalone / inline specification and resource certificate for a Word-RAM data structure. +* `Cslib.WordRAM.SubroutineCode`, `Cslib.WordRAM.SubroutineBase`, `Cslib.WordRAM.SubroutineDS`: + uniform relocatable machine code, step-independent initialization/framing bundle, and unified + standalone / inline specification and resource certificate for a Word-RAM data structure. * `Cslib.WordRAM.SubroutineCode.initFresh`, `Cslib.WordRAM.SubroutineCode.initClear`: `O(1)` fresh initialization on zeroed memory and generic `O(space)` dirty-memory re-initialization. -* `Cslib.WordRAM.SubroutineDS.toDynamicProgram`, `Cslib.WordRAM.SubroutineDS.toCertificate`: +* `Cslib.WordRAM.SubroutineCode.toDynamicProgram`, `Cslib.WordRAM.SubroutineDS.toCertificate`: standalone `.standard`-basis `DynamicProgram` and `DynamicCertificate` projections. ## Main results @@ -53,8 +54,8 @@ A data structure is packaged once as a `SubroutineCode B r_ds k_init` and certif * `Cslib.WordRAM.SubroutineDS.toAmortized`, `Cslib.WordRAM.SubroutineDS.toBound`, `Cslib.WordRAM.SubroutineDS.run_client_amortized`, `Cslib.WordRAM.SubroutineDS.run_client_bound`: standalone amortized and worst-case instruction bounds over `Basis.standard`. -* `Cslib.WordRAM.SubroutineDS.initFresh_rename`, `Cslib.WordRAM.SubroutineDS.initClear_rename`, - `Cslib.WordRAM.SubroutineDS.call_rename`, `Cslib.WordRAM.SubroutineDS.frame_caller`: relocated +* `Cslib.WordRAM.SubroutineBase.initFresh_rename`, `Cslib.WordRAM.SubroutineBase.initClear_rename`, + `Cslib.WordRAM.SubroutineDS.call_rename`, `Cslib.WordRAM.SubroutineBase.frame_caller`: relocated inline initialization, inline operation calls, and bidirectional register/memory framing. ## References @@ -205,14 +206,185 @@ def mapSimulates (φ : B.Simulates B') (P : SubroutineCode B r_ds k_init) : abbrev toStandard (P : SubroutineCode .exact r_ds k_init) : SubroutineCode .standard r_ds k_init := P.mapSimulates .exactStandard +/-- Project subroutine code and its calling convention to the same operation signature. +The basis is retained, so this projection does not translate instructions or resource claims. -/ +def toDynamicProgram (P : SubroutineCode B r_ds k_init) {O : Type*} {Result : O → Type*} + (encode : O → Fin r_ds → ℕ) (decode : (o : O) → (Fin r_ds → ℕ) → Option (Result o)) : + DynamicProgram ⟨O, Result⟩ r_ds B where + code := P.code + output := P.output + input := P.input + encode := encode + decode := decode + +/-- Generic execution of `P.zeroFillCode` clearing `[base, base + space)` in `4 * space + 6` +instructions over any basis `B`. -/ +theorem zeroFillCode_exec (P : SubroutineCode B r_ds k_init) + {w base space : ℕ} {params : Fin k_init → ℕ} + (hclear : [P.curReg, P.zeroReg, P.oneReg, P.baseReg, P.frontierReg].Nodup) + (hparams : ∀ j, P.initParams j ∉ [P.curReg, P.zeroReg, P.oneReg]) + (hinc : ∀ {x : Data r_ds}, x.regs P.oneReg = 1 → Fits w (x.regs P.curReg + 1) → + P.incCur.eval w x = some (x.set P.curReg (x.regs P.curReg + 1))) + {x₁ : Data r_ds} + (hx₁ : x₁.Bounded w) (hb₁ : x₁.regs P.baseReg = base) + (hfr₁ : x₁.regs P.frontierReg = base + space) + (hpar₁ : ∀ j, x₁.regs (P.initParams j) = params j) : + ∃ x₂, ExecWithin w P.zeroFillCode x₁ x₂ (4 * space + 6) ∧ + x₂.Bounded w ∧ + x₂.regs P.baseReg = base ∧ + x₂.regs P.frontierReg = base + space ∧ + (∀ j, x₂.regs (P.initParams j) = params j) ∧ + (∀ a ∈ (Block.mk base space).footprint, x₂.mem a = 0) ∧ + Modifies (Block.mk base space).footprint x₁.mem x₂.mem := by + have hfit0 : Fits w 0 := (hx₁.1 P.baseReg).of_le (Nat.zero_le _) + have hfit_stop : Fits w (base + space) := hfr₁ ▸ hx₁.1 P.frontierReg + obtain ⟨hcur_not, hzero_not, hone_not, hbase_ne_fr, _⟩ := + (List.nodup_cons.mp hclear).imp_right fun h => + (List.nodup_cons.mp h).imp_right fun h => + (List.nodup_cons.mp h).imp_right List.nodup_cons.mp + have hcur_ne_zero : P.curReg ≠ P.zeroReg := fun h => hcur_not (by simp [h]) + have hcur_ne_one : P.curReg ≠ P.oneReg := fun h => hcur_not (by simp [h]) + have hcur_ne_base : P.curReg ≠ P.baseReg := fun h => hcur_not (by simp [h]) + have hcur_ne_fr : P.curReg ≠ P.frontierReg := fun h => hcur_not (by simp [h]) + have hzero_ne_one : P.zeroReg ≠ P.oneReg := fun h => hzero_not (by simp [h]) + have hzero_ne_base : P.zeroReg ≠ P.baseReg := fun h => hzero_not (by simp [h]) + have hzero_ne_fr : P.zeroReg ≠ P.frontierReg := fun h => hzero_not (by simp [h]) + have hone_ne_base : P.oneReg ≠ P.baseReg := fun h => hone_not (by simp [h]) + have hone_ne_fr : P.oneReg ≠ P.frontierReg := fun h => hone_not (by simp [h]) + by_cases hsp : 0 < space + · have hc_pos : (Cond.lt P.baseReg P.frontierReg).Holds x₁ := by + simp only [Cond.Holds, hb₁, hfr₁] + omega + have hfit1 : Fits w 1 := hfit_stop.of_le (by omega) + let x_z := x₁.set P.zeroReg 0 + let x_o := x_z.set P.oneReg 1 + let x_c := x_o.set P.curReg (x_o.regs P.baseReg) + have hxc_base : x_c.regs P.baseReg = base := by + simp [x_c, x_o, x_z, Data.set, Function.update_of_ne hcur_ne_base.symm, + Function.update_of_ne hone_ne_base.symm, Function.update_of_ne hzero_ne_base.symm, hb₁] + have hxc_fr : x_c.regs P.frontierReg = base + space := by + simp [x_c, x_o, x_z, Data.set, Function.update_of_ne hcur_ne_fr.symm, + Function.update_of_ne hone_ne_fr.symm, Function.update_of_ne hzero_ne_fr.symm, hfr₁] + have hxc_zero : x_c.regs P.zeroReg = 0 := by + simp [x_c, x_o, x_z, Data.set, Function.update_of_ne hcur_ne_zero.symm, + Function.update_of_ne hzero_ne_one] + have hxc_one : x_c.regs P.oneReg = 1 := by + simp [x_c, x_o, x_z, Data.set, Function.update_of_ne hcur_ne_one.symm] + have hxc_cur : x_c.regs P.curReg = base := by + simp [x_c, x_o, x_z, Data.set, Function.update_of_ne hone_ne_base.symm, + Function.update_of_ne hzero_ne_base.symm, hb₁] + let xs : ℕ → Data r_ds := fun i => + ⟨Function.update x_c.regs P.curReg (base + i), clear x₁.mem base i⟩ + have hxs0 : xs 0 = x_c := by + ext r + · by_cases hr : r = P.curReg + · subst hr + simp [xs, hxc_cur] + · simp [xs, Function.update_of_ne hr, x_c, x_o, x_z, Data.set] + · simp [xs, x_c, x_o, x_z, Data.set] + have hloop_cond : ∀ i < space, (Cond.lt P.curReg P.frontierReg).Holds (xs i) := by + intro i hi + simp only [Cond.Holds, xs, Function.update_self, + Function.update_of_ne hcur_ne_fr.symm, hxc_fr] + omega + have hloop_exit : ¬(Cond.lt P.curReg P.frontierReg).Holds (xs space) := by + simp only [Cond.Holds, xs, Function.update_self, + Function.update_of_ne hcur_ne_fr.symm, hxc_fr] + omega + have hloop_body : ∀ i < space, + ExecWithin w (.seq (.op (.store P.curReg P.zeroReg)) (.op P.incCur)) + (xs i) (xs (i + 1)) 2 := by + intro i hi + have hsi_zero : (xs i).regs P.zeroReg = 0 := by + simp [xs, Function.update_of_ne hcur_ne_zero.symm, hxc_zero] + have hsi_one : (xs i).regs P.oneReg = 1 := by + simp [xs, Function.update_of_ne hcur_ne_one.symm, hxc_one] + have hsi_cur : (xs i).regs P.curReg = base + i := by simp [xs] + let x_st := (xs i).write ((xs i).regs P.curReg) ((xs i).regs P.zeroReg) + have hst_one : x_st.regs P.oneReg = 1 := hsi_one + have hst_fit : Fits w (x_st.regs P.curReg + 1) := by + change Fits w ((xs i).regs P.curReg + 1) + rw [hsi_cur] + exact hfit_stop.of_le (by omega) + have hinc : ExecWithin w (.op P.incCur) x_st (x_st.set P.curReg (x_st.regs P.curReg + 1)) 1 := + (Exec.op (hinc hst_one hst_fit)).within + have htarget : x_st.set P.curReg (x_st.regs P.curReg + 1) = xs (i + 1) := by + ext r + · simp only [x_st, Data.set, Data.write, xs, hsi_cur] + by_cases hr : r = P.curReg + · subst hr + simp [Nat.add_assoc] + · simp [Function.update_of_ne hr] + · simp only [x_st, Data.set, Data.write, hsi_cur, hsi_zero] + simp [xs, clear_succ] + rw [htarget] at hinc + exact ExecWithin.store.seq hinc + have hloop : ExecWithin w + (.loop (.lt P.curReg P.frontierReg) (.seq (.op (.store P.curReg P.zeroReg)) (.op P.incCur))) + x_c (xs space) (space * (2 + 2) + 2) := by + simpa [hxs0] using ExecWithin.loop xs space 2 hloop_cond hloop_exit hloop_body + have hbranch : ExecWithin w P.zeroFillCode x₁ (xs space) (4 * space + 6) := by + have hseq : ExecWithin w + (.seq (.op (.const P.zeroReg 0)) <| + .seq (.op (.const P.oneReg 1)) <| + .seq (.op (.copy P.curReg P.baseReg)) <| + .loop (.lt P.curReg P.frontierReg) + (.seq (.op (.store P.curReg P.zeroReg)) (.op P.incCur))) + x₁ (xs space) (3 + (space * 4 + 2)) := by + obtain ⟨n₁, hn₁, he₁⟩ := (ExecWithin.const (x := x₁) (d := P.zeroReg) hfit0) + obtain ⟨n₂, hn₂, he₂⟩ := (ExecWithin.const (x := x_z) (d := P.oneReg) hfit1) + obtain ⟨n₃, hn₃, he₃⟩ := (ExecWithin.copy (B := B) (w := w) (x := x_o) + (d := P.curReg) (s := P.baseReg)) + obtain ⟨n₄, hn₄, he₄⟩ := hloop + exact ⟨n₁ + (n₂ + (n₃ + n₄)), by omega, he₁.seq (he₂.seq (he₃.seq he₄))⟩ + exact (ExecWithin.branch_pos hc_pos hseq).mono (by omega) + refine ⟨xs space, hbranch, hbranch.bounded hx₁, ?_, ?_, ?_, ?_, ?_⟩ + · simp [xs, Function.update_of_ne hcur_ne_base.symm, hxc_base] + · simp [xs, Function.update_of_ne hcur_ne_fr.symm, hxc_fr] + · intro j + have hne := hparams j + have h1 : P.initParams j ≠ P.curReg := fun h => hne (by simp [h]) + have h2 : P.initParams j ≠ P.zeroReg := fun h => hne (by simp [h]) + have h3 : P.initParams j ≠ P.oneReg := fun h => hne (by simp [h]) + simp [xs, x_c, x_o, x_z, Data.set, Function.update_of_ne h1, Function.update_of_ne h2, + Function.update_of_ne h3, hpar₁ j] + · intro a ha + exact clear_of_owns (Block.mem_footprint.mp ha) + · intro a ha + exact clear_of_not_owns (mt Block.mem_footprint.mpr ha) + · have hsp0 : space = 0 := by omega + have hc_neg : ¬(Cond.lt P.baseReg P.frontierReg).Holds x₁ := by + simp only [Cond.Holds, hb₁, hfr₁, hsp0] + omega + have hbranch : ExecWithin w P.zeroFillCode x₁ (x₁.set P.zeroReg 0) (4 * space + 6) := + (ExecWithin.branch_neg hc_neg (ExecWithin.const hfit0)).mono (by omega) + refine ⟨x₁.set P.zeroReg 0, hbranch, hbranch.bounded hx₁, ?_, ?_, ?_, ?_, ?_⟩ + · simp [Data.set, Function.update_of_ne hzero_ne_base.symm, hb₁] + · simp [Data.set, Function.update_of_ne hzero_ne_fr.symm, hfr₁] + · intro j + have hne := hparams j + have h2 : P.initParams j ≠ P.zeroReg := fun h => hne (by simp [h]) + simp [Data.set, Function.update_of_ne h2, hpar₁ j] + · intro a ha + simp [hsp0, Block.footprint] at ha + · exact Modifies.refl _ _ + end SubroutineCode universe u v z -/-- Unified standalone and subroutine specification/resource certificate for `P` realizing `D` at -word width `w`, base address `base`, runtime parameters `params`, and initial state `s₀`. -/ -structure SubroutineDS {B : Basis} {r_ds k_init : ℕ} (P : SubroutineCode B r_ds k_init) - (D : Stateful.Spec.{u, v, z}) (w base : ℕ) (params : Fin k_init → ℕ) (s₀ : D.State) where +/-- Step-independent specification, representation, calling-convention, and initialization bundle +for `P : SubroutineCode B r_ds k_init` over a state type `State`, operation type `Op`, observation +family `Result`, and admissibility predicate `valid` at word width `w`, base address `base`, +runtime parameters `params`, and initial state `s₀`. + +Extended by `SubroutineDS` (for deterministic `Stateful.Spec`) and `RelSubroutineDS` (for +relational `Stateful.RelSpec`) so that initialization (`initFresh`, `zeroFillCode`, `initClear`), +register relocation, standalone `toDynamicProgram`, and bidirectional framing (`frame_caller`) are +stated and proved once. -/ +structure SubroutineBase {B : Basis} {r_ds k_init : ℕ} (P : SubroutineCode B r_ds k_init) + {State : Type u} {O : Type v} (Result : O → Type z) (valid : State → O → Prop) + (w base : ℕ) (params : Fin k_init → ℕ) (s₀ : State) where /-- Contiguous memory footprint length `[base, base + space)`. -/ space : ℕ /-- Instruction bound for `P.allocSpan`. -/ @@ -222,25 +394,23 @@ structure SubroutineDS {B : Basis} {r_ds k_init : ℕ} (P : SubroutineCode B r_d /-- Upper bound on initial concrete potential after initialization. -/ initPotential : ℕ /-- Amortized instruction charge per operation (excluding trailing standalone `halt`). -/ - charge : D.State → D.Op → ℕ + charge : State → O → ℕ /-- Representation invariant linking mathematical state `s` to concrete `Data r_ds`. -/ - rep : D.State → Data r_ds → Prop + rep : State → Data r_ds → Prop /-- Concrete integer potential depending only on `Data r_ds`. -/ potential : Data r_ds → ℕ /-- Standalone register encoding for `DynamicProgram.prepare`. -/ - encode : D.Op → Fin r_ds → ℕ + encode : O → Fin r_ds → ℕ /-- Partial argument-matching predicate for inline callers (refined by `encode`). -/ - argMatch : D.Op → (Fin r_ds → ℕ) → Prop + argMatch : O → (Fin r_ds → ℕ) → Prop /-- Output decoder reading typed operation observations from final registers. -/ - decode : (o : D.Op) → (Fin r_ds → ℕ) → Option (D.Result o) + decode : (o : O) → (Fin r_ds → ℕ) → Option (Result o) /-- Represented configurations are word-bounded. -/ bounded : ∀ {s x}, rep s x → x.Bounded w - /-- Concrete machine data determines at most one represented abstract state. -/ - rep_unique : ∀ {s₁ s₂ x}, rep s₁ x → rep s₂ x → s₁ = s₂ /-- Standalone encoded arguments fit in `w`-bit words on admissible calls. -/ - arguments_fit : ∀ {s} (o), D.valid s o → ∀ i, P.input i = true → Fits w (encode o i) + arguments_fit : ∀ {s} (o), valid s o → ∀ i, P.input i = true → Fits w (encode o i) /-- Full standalone `prepare` delivery satisfies `argMatch`. -/ - encode_matches : ∀ (o : D.Op) (regs : Fin r_ds → ℕ), + encode_matches : ∀ (o : O) (regs : Fin r_ds → ℕ), argMatch o (fun i => if P.input i then encode o i else regs i) /-- `rep` depends only on persistent registers (`P.input i = false`) and `[base, base + space)`. -/ @@ -274,30 +444,25 @@ structure SubroutineDS {B : Basis} {r_ds k_init : ℕ} (P : SubroutineCode B r_d ∃ y, ExecWithin w P.initSetup x₁ y setupTime ∧ y.regs P.frontierReg = base + space ∧ rep s₀ y ∧ potential y ≤ initPotential ∧ Modifies (Block.mk base space).footprint x₁.mem y.mem - /-- Execution of `P.code` for any admissible operation `o` whose arguments match `x.regs`. -/ - step_exec : ∀ {s : D.State} {x : Data r_ds} (o : D.Op), - rep s x → D.valid s o → argMatch o x.regs → - ∃ y n, Exec w P.code x y n ∧ - decode o y.regs = some (D.step s o).2 ∧ - rep (D.step s o).1 y ∧ - Modifies (Block.mk base space).footprint x.mem y.mem ∧ - n + potential y ≤ charge s o + potential x -namespace SubroutineDS +namespace SubroutineBase variable {B B' : Basis} {r_ds r_caller k_init w base : ℕ} - {P : SubroutineCode B r_ds k_init} {D : Stateful.Spec.{u, v, z}} - {params : Fin k_init → ℕ} {s₀ : D.State} + {P : SubroutineCode B r_ds k_init} {State : Type u} {O : Type v} + {Result : O → Type z} {valid : State → O → Prop} {params : Fin k_init → ℕ} {s₀ : State} /-- Total instruction bound for `P.initFresh`. -/ -def initTime (C : SubroutineDS P D w base params s₀) : ℕ := +def initTime (C : SubroutineBase P Result valid w base params s₀) : ℕ := C.spanTime + C.setupTime -/-! ### Projection 1: Basis lifting and standalone `DynamicProgram` / `Stateful` theorems -/ +/-- Default register-preparation function overwriting `P.input` registers with `C.encode o`. -/ +def prepareRegs (C : SubroutineBase P Result valid w base params s₀) (o : O) + (regs : Fin r_ds → ℕ) : Fin r_ds → ℕ := + fun i => if P.input i then C.encode o i else regs i -/-- Lift a `SubroutineDS` certificate along any defined-evaluation basis simulation. -/ -def mapSimulates (φ : B.Simulates B') (C : SubroutineDS P D w base params s₀) : - SubroutineDS (P.mapSimulates φ) D w base params s₀ where +/-- Lift a `SubroutineBase` bundle along any defined-evaluation basis simulation. -/ +def mapSimulates (φ : B.Simulates B') (C : SubroutineBase P Result valid w base params s₀) : + SubroutineBase (P.mapSimulates φ) Result valid w base params s₀ where space := C.space spanTime := C.spanTime setupTime := C.setupTime @@ -309,7 +474,6 @@ def mapSimulates (φ : B.Simulates B') (C : SubroutineDS P D w base params s₀) argMatch := C.argMatch decode := C.decode bounded := C.bounded - rep_unique := C.rep_unique arguments_fit := C.arguments_fit encode_matches := C.encode_matches rep_supported := C.rep_supported @@ -323,6 +487,144 @@ def mapSimulates (φ : B.Simulates B') (C : SubroutineDS P D w base params s₀) setup_exec hx₁ hb hfr hpar hz := let ⟨y, he, hfr', hrep, hpot, hmod⟩ := C.setup_exec hx₁ hb hfr hpar hz ⟨y, he.mapSimulates φ, hfr', hrep, hpot, hmod⟩ + +/-- Project a subroutine bundle to its signature-level program, retaining the instruction basis. -/ +def toDynamicProgram (C : SubroutineBase P Result valid w base params s₀) : + DynamicProgram ⟨O, Result⟩ r_ds B := + P.toDynamicProgram C.encode C.decode + +/-- Execute `P.initFresh` on zeroed `[base, base + C.space)` in `C.initTime` instructions. -/ +theorem initFresh_exec (C : SubroutineBase P Result valid w base params s₀) {x : Data r_ds} + (hx : x.Bounded w) (hfr : x.regs P.frontierReg = base) + (hpar : ∀ j, x.regs (P.initParams j) = params j) + (hz : ∀ a ∈ (Block.mk base C.space).footprint, x.mem a = 0) : + ∃ y, ExecWithin w P.initFresh x y C.initTime ∧ + y.regs P.frontierReg = base + C.space ∧ + C.rep s₀ y ∧ + C.potential y ≤ C.initPotential ∧ + Modifies (Block.mk base C.space).footprint x.mem y.mem := by + obtain ⟨x₁, he₁, hm₁, hb₁, hfr₁, hpar₁⟩ := C.span_exec hx hfr hpar + have hx₁ : x₁.Bounded w := he₁.bounded hx + have hz₁ : ∀ a ∈ (Block.mk base C.space).footprint, x₁.mem a = 0 := by + intro a ha + rw [hm₁] + exact hz a ha + obtain ⟨y, he₂, hfr₂, hrep, hpot, hmod⟩ := C.setup_exec hx₁ hb₁ hfr₁ hpar₁ hz₁ + rw [hm₁] at hmod + exact ⟨y, he₁.seq he₂, hfr₂, hrep, hpot, hmod⟩ + +/-- Relocated `O(1)` fresh initialization along any injective register embedding `f`. -/ +theorem initFresh_rename (C : SubroutineBase P Result valid w base params s₀) + {f : Fin r_ds → Fin r_caller} (hf : f.Injective) {x : Data r_caller} + (hx : (x.comap f).Bounded w) (hfr : x.regs (f P.frontierReg) = base) + (hpar : ∀ j, x.regs (f (P.initParams j)) = params j) + (hz : ∀ a ∈ (Block.mk base C.space).footprint, x.mem a = 0) : + ∃ z, ExecWithin w (P.initFresh.rename f) x z C.initTime ∧ + z.regs (f P.frontierReg) = base + C.space ∧ + C.rep s₀ (z.comap f) ∧ + C.potential (z.comap f) ≤ C.initPotential ∧ + Modifies (Block.mk base C.space).footprint x.mem z.mem ∧ + (∀ j ∉ Set.range f, z.regs j = x.regs j) := by + obtain ⟨y, he, hfr', hrep, hpot, hmod⟩ := C.initFresh_exec hx hfr hpar hz + refine ⟨y.extend f x, he.rename hf, ?_, ?_, ?_, hmod, + fun j hj => Data.extend_regs_of_notMem y x hj⟩ + · change ((y.extend f x).comap f).regs P.frontierReg = base + C.space + rw [Data.comap_extend hf] + exact hfr' + · rw [Data.comap_extend hf] + exact hrep + · rw [Data.comap_extend hf] + exact hpot + +/-- Execute `P.initClear` on **arbitrary dirty memory** in `C.initTime + 4 * C.space + 6` +instructions. -/ +theorem initClear_exec (C : SubroutineBase P Result valid w base params s₀) {x : Data r_ds} + (hx : x.Bounded w) (hfr : x.regs P.frontierReg = base) + (hpar : ∀ j, x.regs (P.initParams j) = params j) : + ∃ y, ExecWithin w P.initClear x y (C.initTime + 4 * C.space + 6) ∧ + y.regs P.frontierReg = base + C.space ∧ + C.rep s₀ y ∧ + C.potential y ≤ C.initPotential ∧ + Modifies (Block.mk base C.space).footprint x.mem y.mem := by + obtain ⟨x₁, he₁, hm₁, hb₁, hfr₁, hpar₁⟩ := C.span_exec hx hfr hpar + obtain ⟨x₂, he₂, hx₂, hb₂, hfr₂, hpar₂, hz₂, hmod₂⟩ := + P.zeroFillCode_exec C.clear_nodup C.params_not_clear C.incCur_eval + (he₁.bounded hx) hb₁ hfr₁ hpar₁ + obtain ⟨y, he₃, hfr₃, hrep, hpot, hmod₃⟩ := C.setup_exec hx₂ hb₂ hfr₂ hpar₂ hz₂ + rw [hm₁] at hmod₂ + have hmod : Modifies (Block.mk base C.space).footprint x.mem y.mem := by + simpa only [Finset.union_idempotent] using hmod₂.trans hmod₃ + exact ⟨y, (he₁.seq (he₂.seq he₃)).mono (by unfold initTime; omega), hfr₃, hrep, hpot, hmod⟩ + +/-- Relocated `O(space)` dirty-memory initialization along any injective register embedding `f`. -/ +theorem initClear_rename (C : SubroutineBase P Result valid w base params s₀) + {f : Fin r_ds → Fin r_caller} (hf : f.Injective) {x : Data r_caller} + (hx : (x.comap f).Bounded w) (hfr : x.regs (f P.frontierReg) = base) + (hpar : ∀ j, x.regs (f (P.initParams j)) = params j) : + ∃ z, ExecWithin w (P.initClear.rename f) x z (C.initTime + 4 * C.space + 6) ∧ + z.regs (f P.frontierReg) = base + C.space ∧ + C.rep s₀ (z.comap f) ∧ + C.potential (z.comap f) ≤ C.initPotential ∧ + Modifies (Block.mk base C.space).footprint x.mem z.mem ∧ + (∀ j ∉ Set.range f, z.regs j = x.regs j) := by + obtain ⟨y, he, hfr', hrep, hpot, hmod⟩ := C.initClear_exec hx hfr hpar + refine ⟨y.extend f x, he.rename hf, ?_, ?_, ?_, hmod, + fun j hj => Data.extend_regs_of_notMem y x hj⟩ + · change ((y.extend f x).comap f).regs P.frontierReg = base + C.space + rw [Data.comap_extend hf] + exact hfr' + · rw [Data.comap_extend hf] + exact hrep + · rw [Data.comap_extend hf] + exact hpot + +/-- Any caller state transition that preserves `(x'.comap f).Bounded w`, leaves persistent DS +registers (`P.input i = false`) unchanged, and writes only to a set `F` disjoint from +`[base, base + C.space)` preserves both `C.rep s (x'.comap f)` and `C.potential (x'.comap f)`. -/ +theorem frame_caller (C : SubroutineBase P Result valid w base params s₀) + {f : Fin r_ds → Fin r_caller} {s : State} {x x' : Data r_caller} {F : Finset ℕ} + (hr : C.rep s (x.comap f)) + (hb' : (x'.comap f).Bounded w) + (hreg : ∀ i, P.input i = false → x'.regs (f i) = x.regs (f i)) + (hdisj : Disjoint (Block.mk base C.space).footprint F) + (hmem : Modifies F x.mem x'.mem) : + C.rep s (x'.comap f) ∧ C.potential (x'.comap f) = C.potential (x.comap f) := by + have hag : ∀ a ∈ (Block.mk base C.space).footprint, x'.mem a = x.mem a := fun a ha => + hmem a (fun hf => Finset.disjoint_left.mp hdisj ha hf) + exact ⟨C.rep_supported (x := x.comap f) (x' := x'.comap f) hreg hag hb' hr, + C.potential_supported (x := x.comap f) (x' := x'.comap f) hreg hag hr⟩ + +end SubroutineBase + +/-- Unified standalone and subroutine specification/resource certificate for `P` realizing `D` at +word width `w`, base address `base`, runtime parameters `params`, and initial state `s₀`. -/ +structure SubroutineDS {B : Basis} {r_ds k_init : ℕ} (P : SubroutineCode B r_ds k_init) + (D : Stateful.Spec.{u, v, z}) (w base : ℕ) (params : Fin k_init → ℕ) (s₀ : D.State) + extends SubroutineBase P D.Result D.valid w base params s₀ where + /-- Concrete machine data determines at most one represented abstract state. -/ + rep_unique : ∀ {s₁ s₂ x}, rep s₁ x → rep s₂ x → s₁ = s₂ + /-- Execution of `P.code` for any admissible operation `o` whose arguments match `x.regs`. -/ + step_exec : ∀ {s : D.State} {x : Data r_ds} (o : D.Op), + rep s x → D.valid s o → argMatch o x.regs → + ∃ y n, Exec w P.code x y n ∧ + decode o y.regs = some (D.step s o).2 ∧ + rep (D.step s o).1 y ∧ + Modifies (Block.mk base space).footprint x.mem y.mem ∧ + n + potential y ≤ charge s o + potential x + +namespace SubroutineDS + +variable {B B' : Basis} {r_ds r_caller k_init w base : ℕ} + {P : SubroutineCode B r_ds k_init} {D : Stateful.Spec.{u, v, z}} + {params : Fin k_init → ℕ} {s₀ : D.State} + +/-! ### Projection 1: Basis lifting and standalone `DynamicProgram` / `Stateful` theorems -/ + +/-- Lift a `SubroutineDS` certificate along any defined-evaluation basis simulation. -/ +def mapSimulates (φ : B.Simulates B') (C : SubroutineDS P D w base params s₀) : + SubroutineDS (P.mapSimulates φ) D w base params s₀ where + toSubroutineBase := C.toSubroutineBase.mapSimulates φ + rep_unique := C.rep_unique step_exec o hr hv harg := let ⟨y, n, he, hdec, hrep, hmod, hpot⟩ := C.step_exec o hr hv harg ⟨y, n, he.mapSimulates φ, hdec, hrep, hmod, hpot⟩ @@ -338,14 +640,6 @@ section Standard variable {P_std : SubroutineCode .standard r_ds k_init} (C : SubroutineDS P_std D w base params s₀) -/-- Project a `.standard`-basis `SubroutineDS` to a standalone `DynamicProgram`. -/ -def toDynamicProgram : DynamicProgram D r_ds where - code := P_std.code - output := P_std.output - input := P_std.input - encode := C.encode - decode := C.decode - /-- Project a `.standard`-basis `SubroutineDS` to a standalone `DynamicCertificate`. -/ def toCertificate : DynamicCertificate C.toDynamicProgram w where rep := C.rep @@ -423,241 +717,7 @@ theorem run_client_bound (hpot : ∀ x, C.potential x = 0) {α : Type*} (p : D.C end Standard -/-! ### Projection 2: Fresh `O(1)` initialization and register relocation -/ - -/-- Execute `P.initFresh` on zeroed `[base, base + C.space)` in `C.initTime` instructions. -/ -theorem initFresh_exec (C : SubroutineDS P D w base params s₀) {x : Data r_ds} - (hx : x.Bounded w) (hfr : x.regs P.frontierReg = base) - (hpar : ∀ j, x.regs (P.initParams j) = params j) - (hz : ∀ a ∈ (Block.mk base C.space).footprint, x.mem a = 0) : - ∃ y, ExecWithin w P.initFresh x y C.initTime ∧ - y.regs P.frontierReg = base + C.space ∧ - C.rep s₀ y ∧ - C.potential y ≤ C.initPotential ∧ - Modifies (Block.mk base C.space).footprint x.mem y.mem := by - obtain ⟨x₁, he₁, hm₁, hb₁, hfr₁, hpar₁⟩ := C.span_exec hx hfr hpar - have hx₁ : x₁.Bounded w := he₁.bounded hx - have hz₁ : ∀ a ∈ (Block.mk base C.space).footprint, x₁.mem a = 0 := by - intro a ha - rw [hm₁] - exact hz a ha - obtain ⟨y, he₂, hfr₂, hrep, hpot, hmod⟩ := C.setup_exec hx₁ hb₁ hfr₁ hpar₁ hz₁ - rw [hm₁] at hmod - exact ⟨y, he₁.seq he₂, hfr₂, hrep, hpot, hmod⟩ - -/-- Relocated `O(1)` fresh initialization along any injective register embedding `f`. -/ -theorem initFresh_rename (C : SubroutineDS P D w base params s₀) - {f : Fin r_ds → Fin r_caller} (hf : f.Injective) {x : Data r_caller} - (hx : (x.comap f).Bounded w) (hfr : x.regs (f P.frontierReg) = base) - (hpar : ∀ j, x.regs (f (P.initParams j)) = params j) - (hz : ∀ a ∈ (Block.mk base C.space).footprint, x.mem a = 0) : - ∃ z, ExecWithin w (P.initFresh.rename f) x z C.initTime ∧ - z.regs (f P.frontierReg) = base + C.space ∧ - C.rep s₀ (z.comap f) ∧ - C.potential (z.comap f) ≤ C.initPotential ∧ - Modifies (Block.mk base C.space).footprint x.mem z.mem ∧ - (∀ j ∉ Set.range f, z.regs j = x.regs j) := by - obtain ⟨y, he, hfr', hrep, hpot, hmod⟩ := C.initFresh_exec hx hfr hpar hz - refine ⟨y.extend f x, he.rename hf, ?_, ?_, ?_, hmod, - fun j hj => Data.extend_regs_of_notMem y x hj⟩ - · change ((y.extend f x).comap f).regs P.frontierReg = base + C.space - rw [Data.comap_extend hf] - exact hfr' - · rw [Data.comap_extend hf] - exact hrep - · rw [Data.comap_extend hf] - exact hpot - -/-! ### Projection 3: Generic `O(space)` dirty-memory initialization (`initClear`) -/ - -/-- Generic execution of `P.zeroFillCode` clearing `[base, base + C.space)` in `4 * C.space + 6` -instructions over any basis `B`. -/ -theorem zeroFillCode_exec (C : SubroutineDS P D w base params s₀) {x₁ : Data r_ds} - (hx₁ : x₁.Bounded w) (hb₁ : x₁.regs P.baseReg = base) - (hfr₁ : x₁.regs P.frontierReg = base + C.space) - (hpar₁ : ∀ j, x₁.regs (P.initParams j) = params j) : - ∃ x₂, ExecWithin w P.zeroFillCode x₁ x₂ (4 * C.space + 6) ∧ - x₂.Bounded w ∧ - x₂.regs P.baseReg = base ∧ - x₂.regs P.frontierReg = base + C.space ∧ - (∀ j, x₂.regs (P.initParams j) = params j) ∧ - (∀ a ∈ (Block.mk base C.space).footprint, x₂.mem a = 0) ∧ - Modifies (Block.mk base C.space).footprint x₁.mem x₂.mem := by - have hfit0 : Fits w 0 := (hx₁.1 P.baseReg).of_le (Nat.zero_le _) - have hfit_stop : Fits w (base + C.space) := hfr₁ ▸ hx₁.1 P.frontierReg - obtain ⟨hcur_not, hzero_not, hone_not, hbase_ne_fr, _⟩ := - (List.nodup_cons.mp C.clear_nodup).imp_right fun h => - (List.nodup_cons.mp h).imp_right fun h => - (List.nodup_cons.mp h).imp_right List.nodup_cons.mp - have hcur_ne_zero : P.curReg ≠ P.zeroReg := fun h => hcur_not (by simp [h]) - have hcur_ne_one : P.curReg ≠ P.oneReg := fun h => hcur_not (by simp [h]) - have hcur_ne_base : P.curReg ≠ P.baseReg := fun h => hcur_not (by simp [h]) - have hcur_ne_fr : P.curReg ≠ P.frontierReg := fun h => hcur_not (by simp [h]) - have hzero_ne_one : P.zeroReg ≠ P.oneReg := fun h => hzero_not (by simp [h]) - have hzero_ne_base : P.zeroReg ≠ P.baseReg := fun h => hzero_not (by simp [h]) - have hzero_ne_fr : P.zeroReg ≠ P.frontierReg := fun h => hzero_not (by simp [h]) - have hone_ne_base : P.oneReg ≠ P.baseReg := fun h => hone_not (by simp [h]) - have hone_ne_fr : P.oneReg ≠ P.frontierReg := fun h => hone_not (by simp [h]) - by_cases hsp : 0 < C.space - · have hc_pos : (Cond.lt P.baseReg P.frontierReg).Holds x₁ := by - simp only [Cond.Holds, hb₁, hfr₁] - omega - have hfit1 : Fits w 1 := hfit_stop.of_le (by omega) - let x_z := x₁.set P.zeroReg 0 - let x_o := x_z.set P.oneReg 1 - let x_c := x_o.set P.curReg (x_o.regs P.baseReg) - have hxc_base : x_c.regs P.baseReg = base := by - simp [x_c, x_o, x_z, Data.set, Function.update_of_ne hcur_ne_base.symm, - Function.update_of_ne hone_ne_base.symm, Function.update_of_ne hzero_ne_base.symm, hb₁] - have hxc_fr : x_c.regs P.frontierReg = base + C.space := by - simp [x_c, x_o, x_z, Data.set, Function.update_of_ne hcur_ne_fr.symm, - Function.update_of_ne hone_ne_fr.symm, Function.update_of_ne hzero_ne_fr.symm, hfr₁] - have hxc_zero : x_c.regs P.zeroReg = 0 := by - simp [x_c, x_o, x_z, Data.set, Function.update_of_ne hcur_ne_zero.symm, - Function.update_of_ne hzero_ne_one] - have hxc_one : x_c.regs P.oneReg = 1 := by - simp [x_c, x_o, x_z, Data.set, Function.update_of_ne hcur_ne_one.symm] - have hxc_cur : x_c.regs P.curReg = base := by - simp [x_c, x_o, x_z, Data.set, Function.update_of_ne hone_ne_base.symm, - Function.update_of_ne hzero_ne_base.symm, hb₁] - let xs : ℕ → Data r_ds := fun i => - ⟨Function.update x_c.regs P.curReg (base + i), clear x₁.mem base i⟩ - have hxs0 : xs 0 = x_c := by - ext r - · by_cases hr : r = P.curReg - · subst hr - simp [xs, hxc_cur] - · simp [xs, Function.update_of_ne hr, x_c, x_o, x_z, Data.set] - · simp [xs, x_c, x_o, x_z, Data.set] - have hloop_cond : ∀ i < C.space, (Cond.lt P.curReg P.frontierReg).Holds (xs i) := by - intro i hi - simp only [Cond.Holds, xs, Function.update_self, - Function.update_of_ne hcur_ne_fr.symm, hxc_fr] - omega - have hloop_exit : ¬(Cond.lt P.curReg P.frontierReg).Holds (xs C.space) := by - simp only [Cond.Holds, xs, Function.update_self, - Function.update_of_ne hcur_ne_fr.symm, hxc_fr] - omega - have hloop_body : ∀ i < C.space, - ExecWithin w (.seq (.op (.store P.curReg P.zeroReg)) (.op P.incCur)) - (xs i) (xs (i + 1)) 2 := by - intro i hi - have hsi_zero : (xs i).regs P.zeroReg = 0 := by - simp [xs, Function.update_of_ne hcur_ne_zero.symm, hxc_zero] - have hsi_one : (xs i).regs P.oneReg = 1 := by - simp [xs, Function.update_of_ne hcur_ne_one.symm, hxc_one] - have hsi_cur : (xs i).regs P.curReg = base + i := by simp [xs] - let x_st := (xs i).write ((xs i).regs P.curReg) ((xs i).regs P.zeroReg) - have hst_one : x_st.regs P.oneReg = 1 := hsi_one - have hst_fit : Fits w (x_st.regs P.curReg + 1) := by - change Fits w ((xs i).regs P.curReg + 1) - rw [hsi_cur] - exact hfit_stop.of_le (by omega) - have hinc : ExecWithin w (.op P.incCur) x_st (x_st.set P.curReg (x_st.regs P.curReg + 1)) 1 := - (Exec.op (C.incCur_eval hst_one hst_fit)).within - have htarget : x_st.set P.curReg (x_st.regs P.curReg + 1) = xs (i + 1) := by - ext r - · simp only [x_st, Data.set, Data.write, xs, hsi_cur] - by_cases hr : r = P.curReg - · subst hr - simp [Nat.add_assoc] - · simp [Function.update_of_ne hr] - · simp only [x_st, Data.set, Data.write, hsi_cur, hsi_zero] - simp [xs, clear_succ] - rw [htarget] at hinc - exact ExecWithin.store.seq hinc - have hloop : ExecWithin w - (.loop (.lt P.curReg P.frontierReg) (.seq (.op (.store P.curReg P.zeroReg)) (.op P.incCur))) - x_c (xs C.space) (C.space * (2 + 2) + 2) := by - simpa [hxs0] using ExecWithin.loop xs C.space 2 hloop_cond hloop_exit hloop_body - have hbranch : ExecWithin w P.zeroFillCode x₁ (xs C.space) (4 * C.space + 6) := by - have hseq : ExecWithin w - (.seq (.op (.const P.zeroReg 0)) <| - .seq (.op (.const P.oneReg 1)) <| - .seq (.op (.copy P.curReg P.baseReg)) <| - .loop (.lt P.curReg P.frontierReg) - (.seq (.op (.store P.curReg P.zeroReg)) (.op P.incCur))) - x₁ (xs C.space) (3 + (C.space * 4 + 2)) := by - obtain ⟨n₁, hn₁, he₁⟩ := (ExecWithin.const (x := x₁) (d := P.zeroReg) hfit0) - obtain ⟨n₂, hn₂, he₂⟩ := (ExecWithin.const (x := x_z) (d := P.oneReg) hfit1) - obtain ⟨n₃, hn₃, he₃⟩ := (ExecWithin.copy (B := B) (w := w) (x := x_o) - (d := P.curReg) (s := P.baseReg)) - obtain ⟨n₄, hn₄, he₄⟩ := hloop - exact ⟨n₁ + (n₂ + (n₃ + n₄)), by omega, he₁.seq (he₂.seq (he₃.seq he₄))⟩ - exact (ExecWithin.branch_pos hc_pos hseq).mono (by omega) - refine ⟨xs C.space, hbranch, hbranch.bounded hx₁, ?_, ?_, ?_, ?_, ?_⟩ - · simp [xs, Function.update_of_ne hcur_ne_base.symm, hxc_base] - · simp [xs, Function.update_of_ne hcur_ne_fr.symm, hxc_fr] - · intro j - have hne := C.params_not_clear j - have h1 : P.initParams j ≠ P.curReg := fun h => hne (by simp [h]) - have h2 : P.initParams j ≠ P.zeroReg := fun h => hne (by simp [h]) - have h3 : P.initParams j ≠ P.oneReg := fun h => hne (by simp [h]) - simp [xs, x_c, x_o, x_z, Data.set, Function.update_of_ne h1, Function.update_of_ne h2, - Function.update_of_ne h3, hpar₁ j] - · intro a ha - exact clear_of_owns (Block.mem_footprint.mp ha) - · intro a ha - exact clear_of_not_owns (mt Block.mem_footprint.mpr ha) - · have hsp0 : C.space = 0 := by omega - have hc_neg : ¬(Cond.lt P.baseReg P.frontierReg).Holds x₁ := by - simp only [Cond.Holds, hb₁, hfr₁, hsp0] - omega - have hbranch : ExecWithin w P.zeroFillCode x₁ (x₁.set P.zeroReg 0) (4 * C.space + 6) := - (ExecWithin.branch_neg hc_neg (ExecWithin.const hfit0)).mono (by omega) - refine ⟨x₁.set P.zeroReg 0, hbranch, hbranch.bounded hx₁, ?_, ?_, ?_, ?_, ?_⟩ - · simp [Data.set, Function.update_of_ne hzero_ne_base.symm, hb₁] - · simp [Data.set, Function.update_of_ne hzero_ne_fr.symm, hfr₁] - · intro j - have hne := C.params_not_clear j - have h2 : P.initParams j ≠ P.zeroReg := fun h => hne (by simp [h]) - simp [Data.set, Function.update_of_ne h2, hpar₁ j] - · intro a ha - simp [hsp0, Block.footprint] at ha - · exact Modifies.refl _ _ - -/-- Execute `P.initClear` on **arbitrary dirty memory** in `C.initTime + 4 * C.space + 6` -instructions. -/ -theorem initClear_exec (C : SubroutineDS P D w base params s₀) {x : Data r_ds} - (hx : x.Bounded w) (hfr : x.regs P.frontierReg = base) - (hpar : ∀ j, x.regs (P.initParams j) = params j) : - ∃ y, ExecWithin w P.initClear x y (C.initTime + 4 * C.space + 6) ∧ - y.regs P.frontierReg = base + C.space ∧ - C.rep s₀ y ∧ - C.potential y ≤ C.initPotential ∧ - Modifies (Block.mk base C.space).footprint x.mem y.mem := by - obtain ⟨x₁, he₁, hm₁, hb₁, hfr₁, hpar₁⟩ := C.span_exec hx hfr hpar - obtain ⟨x₂, he₂, hx₂, hb₂, hfr₂, hpar₂, hz₂, hmod₂⟩ := - C.zeroFillCode_exec (he₁.bounded hx) hb₁ hfr₁ hpar₁ - obtain ⟨y, he₃, hfr₃, hrep, hpot, hmod₃⟩ := C.setup_exec hx₂ hb₂ hfr₂ hpar₂ hz₂ - rw [hm₁] at hmod₂ - have hmod : Modifies (Block.mk base C.space).footprint x.mem y.mem := by - simpa only [Finset.union_idempotent] using hmod₂.trans hmod₃ - exact ⟨y, (he₁.seq (he₂.seq he₃)).mono (by unfold initTime; omega), hfr₃, hrep, hpot, hmod⟩ - -/-- Relocated `O(space)` dirty-memory initialization along any injective register embedding `f`. -/ -theorem initClear_rename (C : SubroutineDS P D w base params s₀) - {f : Fin r_ds → Fin r_caller} (hf : f.Injective) {x : Data r_caller} - (hx : (x.comap f).Bounded w) (hfr : x.regs (f P.frontierReg) = base) - (hpar : ∀ j, x.regs (f (P.initParams j)) = params j) : - ∃ z, ExecWithin w (P.initClear.rename f) x z (C.initTime + 4 * C.space + 6) ∧ - z.regs (f P.frontierReg) = base + C.space ∧ - C.rep s₀ (z.comap f) ∧ - C.potential (z.comap f) ≤ C.initPotential ∧ - Modifies (Block.mk base C.space).footprint x.mem z.mem ∧ - (∀ j ∉ Set.range f, z.regs j = x.regs j) := by - obtain ⟨y, he, hfr', hrep, hpot, hmod⟩ := C.initClear_exec hx hfr hpar - refine ⟨y.extend f x, he.rename hf, ?_, ?_, ?_, hmod, - fun j hj => Data.extend_regs_of_notMem y x hj⟩ - · change ((y.extend f x).comap f).regs P.frontierReg = base + C.space - rw [Data.comap_extend hf] - exact hfr' - · rw [Data.comap_extend hf] - exact hrep - · rw [Data.comap_extend hf] - exact hpot - -/-! ### Projection 4: Relocated inline subroutine calls (`code_rename`, `call_rename`) -/ +/-! ### Projection 2: Relocated inline subroutine calls (`code_rename`, `call_rename`) -/ /-- Execute `P.code.rename f` inline when arguments already match `(x.comap f).regs`. -/ theorem code_rename (C : SubroutineDS P D w base params s₀) @@ -706,24 +766,6 @@ theorem call_rename (C : SubroutineDS P D w base params s₀) exact ⟨z, t_prep + t_op, hprep.seq he_op, hdec, hrep_z, hmod_z, hregs_z, by rw [hp_eq] at hpot_z; omega⟩ -/-! ### Projection 5: Caller-to-DS bidirectional framing (`frame_caller`) -/ - -/-- Any caller state transition that preserves `(x'.comap f).Bounded w`, leaves persistent DS -registers (`P.input i = false`) unchanged, and writes only to a set `F` disjoint from -`[base, base + C.space)` preserves both `C.rep s (x'.comap f)` and `C.potential (x'.comap f)`. -/ -theorem frame_caller (C : SubroutineDS P D w base params s₀) - {f : Fin r_ds → Fin r_caller} {s : D.State} {x x' : Data r_caller} {F : Finset ℕ} - (hr : C.rep s (x.comap f)) - (hb' : (x'.comap f).Bounded w) - (hreg : ∀ i, P.input i = false → x'.regs (f i) = x.regs (f i)) - (hdisj : Disjoint (Block.mk base C.space).footprint F) - (hmem : Modifies F x.mem x'.mem) : - C.rep s (x'.comap f) ∧ C.potential (x'.comap f) = C.potential (x.comap f) := by - have hag : ∀ a ∈ (Block.mk base C.space).footprint, x'.mem a = x.mem a := fun a ha => - hmem a (fun hf => Finset.disjoint_left.mp hdisj ha hf) - exact ⟨C.rep_supported (x := x.comap f) (x' := x'.comap f) hreg hag hb' hr, - C.potential_supported (x := x.comap f) (x' := x'.comap f) hreg hag hr⟩ - end SubroutineDS end Cslib.WordRAM diff --git a/Complexity/Foundations/Semantics/Stateful/Realization.lean b/Complexity/Foundations/Semantics/Stateful/Realization.lean index 43afd738d..5761f7fb1 100644 --- a/Complexity/Foundations/Semantics/Stateful/Realization.lean +++ b/Complexity/Foundations/Semantics/Stateful/Realization.lean @@ -39,14 +39,16 @@ namespace Cslib.Stateful universe u v w x y -variable {D E : Spec.{u, v, w}} {C τ : Type x} {α β : Type y} +variable {S : PFunctor.{v, w}} {D E : Spec.{u, v, w}} {C τ : Type x} {α β : Type y} + +instance : Coe Spec.{u, v, w} PFunctor.{v, w} := ⟨Spec.signature⟩ /-- A backend chooses its own execution evidence, independently of a resource measure. -/ -abbrev Execution (D : Spec.{u, v, w}) (C Trace : Type x) := - C → (o : D.Op) → D.Result o → C → Trace → Prop +abbrev Execution (S : PFunctor.{v, w}) (C Trace : Type x) := + C → (o : S.A) → S.B o → C → Trace → Prop /-- Clients collect native execution evidence in call order; no costs occur in this semantics. -/ -inductive Executes (exec : Execution D C τ) : D.Client α → C → α → C → List τ → Prop +inductive Executes (exec : Execution S C τ) : S.FreeM α → C → α → C → List τ → Prop | pure (a c) : Executes exec (.pure a) c a c [] | call {o k c a c' trace b c'' traces} : exec c o a c' trace → Executes exec (k a) c' b c'' traces → @@ -97,12 +99,12 @@ end Realization /-- Execution of a sequential composition splits its native trace at the call boundary. -/ -theorem executes_bind_iff (exec : Execution D C τ) (p : D.Client α) - (k : α → D.Client β) {c c' : C} {b : β} {ts : List τ} : +theorem executes_bind_iff (exec : Execution S C τ) (p : S.FreeM α) + (k : α → S.FreeM β) {c c' : C} {b : β} {ts : List τ} : Executes exec (p.bind k) c b c' ts ↔ ∃ a mid front back, Executes exec p c a mid front ∧ Executes exec (k a) mid b c' back ∧ ts = front ++ back := by - induction p generalizing c ts with + induction p using PFunctor.FreeM.recOn generalizing c ts with | pure a => constructor · intro h; exact ⟨a, c, [], ts, .pure a c, h, rfl⟩ diff --git a/Complexity/Foundations/Semantics/Stateful/Relational.lean b/Complexity/Foundations/Semantics/Stateful/Relational.lean new file mode 100644 index 000000000..6a709a6d1 --- /dev/null +++ b/Complexity/Foundations/Semantics/Stateful/Relational.lean @@ -0,0 +1,1106 @@ +/- +Copyright (c) 2026 Renfei Zhou. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Renfei Zhou +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Complexity.Foundations.Semantics.Stateful.Amortized +public import Complexity.Foundations.Semantics.Stateful.CostHoare +public import Cslib.Foundations.Semantics.LTS.Basic + +/-! +# Relational stateful specifications, demonic adaptive clients, and angelic realizations + +A relational stateful specification (`Cslib.Stateful.RelSpec`) generalizes `Cslib.Stateful.Spec` +from a deterministic transition function `step : State → (o : Op) → State × Result o` to a +transition relation `step : State → (o : Op) → State → Result o → Prop`. This allows data-structure +specifications to leave implementation choices (such as which free slot or handle is allocated, or +how equal-key ties are broken) underspecified in the abstract interface while keeping internal +allocator state hidden inside concrete representation invariants. + +The semantics is **demonic** for callers and **angelic** for implementations: +* An adaptive client `p : D.Client α` is demonically admissible (`D.DemonicValid s p`) when each + issued operation is admissible (`D.valid`) and the continuation remains admissible under *every* + outcome `(s', a)` permitted by `D.step`. Caller postconditions (`D.Hoare`) and abstract cost + budgets (`D.CostHoare`) hold across *all* permitted client runs (`D.ClientRun`). +* A concrete backend realization (`RelRealization`, `RelAmortized`) is angelic in the abstract + post-state: each concrete operation execution witnesses *some* permitted abstract transition + `D.step s o s' a` maintaining the representation invariant and potential inequality. + +Every deterministic `D : Spec` embeds into `RelSpec` with zero overhead via `Spec.toRelSpec` +(preserving `State`, `Op`, `Result`, `valid`, `signature`, and `Client α` definitionally), and +`DemonicValid`, `ClientRun`, `Hoare`, and `CostHoare` on `D.toRelSpec` are equivalent to their +deterministic counterparts on `D`. + +## Main definitions + +* `Cslib.Stateful.RelSpec`: relational stateful specification with `State`, `Op`, `Result`, + transition relation `step`, and input admissibility `valid`. +* `Cslib.Stateful.RelSpec.Total`: typeclass asserting that every admissible `(s, o)` has at least + one permitted outcome `(s', a)`. +* `Cslib.Stateful.Spec.toRelSpec`: zero-cost embedding of a deterministic `Spec` into `RelSpec`. +* `Cslib.Stateful.RelSpec.toLTS`: projection of a `RelSpec` to a `Cslib.LTS` labelled by + input-output pairs `Σ o : D.Op, D.Result o`. +* `Cslib.Stateful.RelSpec.Step`, `Cslib.Stateful.RelSpec.Transcript`, + `Cslib.Stateful.RelSpec.ValidTranscript`, `Cslib.Stateful.RelSpec.ValidOpSeq`, + `Cslib.Stateful.RelSpec.totalCost`, `Cslib.Stateful.RelSpec.totalCostOutcome`: execution + transcripts, non-adaptive operation-sequence admissibility, and abstract cost accounting. +* `Cslib.Stateful.RelSpec.ClientRun`, `Cslib.Stateful.RelSpec.DemonicValid`, + `Cslib.Stateful.RelSpec.Hoare`, `Cslib.Stateful.RelSpec.CostHoare`: adaptive client runs, + demonic admissibility, and compositional Hoare / CostHoare triples. +* `Cslib.Stateful.RelRealization`, `Cslib.Stateful.RelBound`, `Cslib.Stateful.RelAmortized`: + angelic representation simulation and potential-method certificates over `Execution` / `Executes`. + +## Main results + +* `Cslib.Stateful.Spec.toRelSpec_demonicValid_iff`, + `Cslib.Stateful.Spec.toRelSpec_clientRun_iff`, + `Cslib.Stateful.Spec.toRelSpec_hoare_iff`, + `Cslib.Stateful.Spec.toRelSpec_costHoare_iff`: exact collapse of relational client semantics to + deterministic `Spec` semantics on `D.toRelSpec`. +* `Cslib.Stateful.RelRealization.correct_client`, + `Cslib.Stateful.RelRealization.exists_executes`: soundness and existence of native client + executions under `DemonicValid`. +* `Cslib.Stateful.RelAmortized.executes_le`, `Cslib.Stateful.RelAmortized.run_client_amortized`: + amortized potential telescoping along adaptive client executions. + +## References + +* [T. H. Cormen, C. E. Leiserson, R. L. Rivest and C. Stein, *Introduction to Algorithms*, + Chapter 17][CLRS2009] +* [D. Sangiorgi, *Introduction to Bisimulation and Coinduction*][Sangiorgi2011] +-/ + +@[expose] public section + +namespace Cslib.Stateful + +universe u v w x y + +/-! ## Core relational specification and totality -/ + +/-- A relational stateful specification with state type `State`, operation type `Op`, +operation-dependent observation family `Result`, transition relation `step`, and input +admissibility predicate `valid`. -/ +structure RelSpec where + /-- Mathematical states, independent of a concrete storage representation. -/ + State : Type u + /-- Operations available to clients. -/ + Op : Type v + /-- The observation type returned by each operation. -/ + Result : Op → Type w + /-- Relational step specification: `step s o s' a` holds when transitioning from `s` to `s'` + and returning `a : Result o` is a permitted outcome of `o`. -/ + step : State → (o : Op) → State → Result o → Prop + /-- Preconditions under which an operation's implementation must satisfy its contract. -/ + valid : State → Op → Prop := fun _ _ => True + +namespace RelSpec + +variable (D : RelSpec.{u, v, w}) + +/-- The set of permitted `(nextState, result)` outcomes of operation `o` from state `s`. -/ +def outcomes (s : D.State) (o : D.Op) : Set (D.State × D.Result o) := + {p | D.step s o p.1 p.2} + +/-- Membership in `D.outcomes s o` unfolds to `D.step s o`. -/ +@[simp] +theorem mem_outcomes_iff (s : D.State) (o : D.Op) (p : D.State × D.Result o) : + p ∈ D.outcomes s o ↔ D.step s o p.1 p.2 := Iff.rfl + +/-- A relational specification is total on admissible inputs if every valid `(s, o)` admits at +least one permitted outcome `(s', a)`. -/ +class Total : Prop where + /-- Every admissible operation call has at least one permitted post-state and result. -/ + step_total : ∀ s o, D.valid s o → ∃ s' a, D.step s o s' a + +/-- Obtain a permitted outcome `(s', a)` for an admissible operation under `[D.Total]`. -/ +theorem exists_step [D.Total] {s : D.State} {o : D.Op} (hv : D.valid s o) : + ∃ s' a, D.step s o s' a := + Total.step_total s o hv + +/-- The polynomial operation signature `⟨D.Op, D.Result⟩`. -/ +abbrev signature : PFunctor.{v, w} := ⟨D.Op, D.Result⟩ + +instance : Coe RelSpec.{u, v, w} PFunctor.{v, w} := ⟨RelSpec.signature⟩ + +/-- An adaptive client over `D`: each continuation may branch on the observed operation result. -/ +abbrev Client (α : Type y) : Type (max v w y) := D.signature.FreeM α + +/-- Invoke a single operation `o : D.Op` in an adaptive `D.Client`. -/ +abbrev call (o : D.Op) : D.Client (D.Result o) := .liftBind o .pure + +/-- An input-output transition label pairing an operation `o : D.Op` with its result `a`. -/ +abbrev Label : Type (max v w) := Σ o : D.Op, D.Result o + +/-- View a relational stateful specification as a labelled transition system (`Cslib.LTS`) +labelled by input-output pairs `⟨o, a⟩ : Σ o : D.Op, D.Result o`. -/ +def toLTS : LTS D.State D.Label where + Tr s lbl s' := D.step s lbl.1 s' lbl.2 + +/-- A transition in `D.toLTS` is a step of `D`. -/ +@[simp] +theorem toLTS_tr (s s' : D.State) (lbl : D.Label) : + D.toLTS.Tr s lbl s' ↔ D.step s lbl.1 s' lbl.2 := Iff.rfl + +end RelSpec + +/-! ## Zero-cost embedding of deterministic specifications (`Spec.toRelSpec`) -/ + +/-- Embed a deterministic `Stateful.Spec` as a relational `RelSpec` whose transition relation +holds if and only if `(s', a) = D.step s o`. -/ +abbrev Spec.toRelSpec (D : Spec.{u, v, w}) : RelSpec.{u, v, w} where + State := D.State + Op := D.Op + Result := D.Result + step s o s' a := (s', a) = D.step s o + valid := D.valid + +namespace Spec + +variable (D : Spec.{u, v, w}) + +/-- `toRelSpec` preserves the state type definitionally. -/ +@[simp] +theorem toRelSpec_state : D.toRelSpec.State = D.State := rfl + +/-- `toRelSpec` preserves the operation type definitionally. -/ +@[simp] +theorem toRelSpec_op : D.toRelSpec.Op = D.Op := rfl + +/-- `toRelSpec` preserves the result family definitionally. -/ +@[simp] +theorem toRelSpec_result : D.toRelSpec.Result = D.Result := rfl + +/-- A step in `D.toRelSpec` holds iff `(s', a) = D.step s o`. -/ +@[simp] +theorem toRelSpec_step (s : D.State) (o : D.Op) (s' : D.State) (a : D.Result o) : + D.toRelSpec.step s o s' a ↔ (s', a) = D.step s o := Iff.rfl + +/-- `toRelSpec` preserves the admissibility predicate definitionally. -/ +@[simp] +theorem toRelSpec_valid (s : D.State) (o : D.Op) : + D.toRelSpec.valid s o ↔ D.valid s o := Iff.rfl + +/-- `toRelSpec` preserves the polynomial signature definitionally. -/ +@[simp] +theorem toRelSpec_signature : D.toRelSpec.signature = D.signature := rfl + +/-- `toRelSpec` preserves the adaptive client type definitionally. -/ +@[simp] +theorem toRelSpec_client (α : Type y) : D.toRelSpec.Client α = D.Client α := rfl + +/-- Every embedded deterministic specification is automatically total. -/ +instance : D.toRelSpec.Total where + step_total s o _ := ⟨(D.step s o).1, (D.step s o).2, Prod.mk.eta⟩ + +/-- Transitions in `D.toRelSpec.toLTS` agree with `D.toFLTS` on states and `(D.step s o).2` on +observations. -/ +theorem toRelSpec_toLTS_tr (s s' : D.State) (o : D.Op) (a : D.Result o) : + D.toRelSpec.toLTS.Tr s ⟨o, a⟩ s' ↔ s' = D.toFLTS.tr s o ∧ a = (D.step s o).2 := by + simp [RelSpec.toLTS, Spec.toRelSpec, Spec.toFLTS, Prod.ext_iff] + +/-- The labelled transition system of an embedded deterministic specification is deterministic. -/ +instance : D.toRelSpec.toLTS.Deterministic where + deterministic s lbl s₁ s₂ h₁ h₂ := by + have h₁' := congrArg Prod.fst h₁ + have h₂' := congrArg Prod.fst h₂ + exact h₁'.trans h₂'.symm + +/-- Multi-step execution `D.run` produces a valid multi-step trace in `D.toRelSpec.toLTS` ending +at `D.toFLTS.mtr s os`. -/ +theorem toRelSpec_toLTS_mtr_run (s : D.State) (os : List D.Op) : + D.toRelSpec.toLTS.MTr s (D.run s os).2 (D.toFLTS.mtr s os) := by + induction os generalizing s with + | nil => exact .refl + | cons o os ih => + exact .stepL Prod.mk.eta (ih (D.step s o).1) + +end Spec + +/-! ## Transcripts, non-adaptive operation sequences, and cost accounting -/ + +namespace RelSpec + +variable (D : RelSpec.{u, v, w}) + +/-- One recorded step of a relational specification: the issued operation `op`, its returned +observation `result`, and the resulting post-state `nextState`. -/ +structure Step : Type (max u v w) where + /-- The operation invoked at this step. -/ + op : D.Op + /-- The observation returned by `op`. -/ + result : D.Result op + /-- The abstract post-state reached after `op`. -/ + nextState : D.State + +/-- A finite execution transcript is a list of recorded steps. -/ +abbrev Transcript : Type (max u v w) := List D.Step + +/-- Extract the underlying sequence of operations from a transcript. -/ +def Transcript.ops {D : RelSpec.{u, v, w}} (tr : D.Transcript) : List D.Op := + tr.map Step.op + +/-- Extract the `Cslib.LTS` input-output labels from a transcript. -/ +def Transcript.labels {D : RelSpec.{u, v, w}} (tr : D.Transcript) : List D.Label := + tr.map fun st => ⟨st.op, st.result⟩ + +@[simp] +theorem Transcript.ops_nil {D : RelSpec.{u, v, w}} : + Transcript.ops ([] : D.Transcript) = [] := rfl + +@[simp] +theorem Transcript.ops_cons {D : RelSpec.{u, v, w}} (st : D.Step) (tr : D.Transcript) : + Transcript.ops (st :: tr) = st.op :: Transcript.ops tr := rfl + +@[simp] +theorem Transcript.ops_append {D : RelSpec.{u, v, w}} (tr₁ tr₂ : D.Transcript) : + Transcript.ops (tr₁ ++ tr₂) = Transcript.ops tr₁ ++ Transcript.ops tr₂ := + List.map_append + +@[simp] +theorem Transcript.length_ops {D : RelSpec.{u, v, w}} (tr : D.Transcript) : + (Transcript.ops tr).length = tr.length := + List.length_map _ + +@[simp] +theorem Transcript.labels_nil {D : RelSpec.{u, v, w}} : + Transcript.labels ([] : D.Transcript) = [] := rfl + +@[simp] +theorem Transcript.labels_cons {D : RelSpec.{u, v, w}} (st : D.Step) (tr : D.Transcript) : + Transcript.labels (st :: tr) = ⟨st.op, st.result⟩ :: Transcript.labels tr := rfl + +@[simp] +theorem Transcript.labels_append {D : RelSpec.{u, v, w}} (tr₁ tr₂ : D.Transcript) : + Transcript.labels (tr₁ ++ tr₂) = Transcript.labels tr₁ ++ Transcript.labels tr₂ := + List.map_append + +@[simp] +theorem Transcript.length_labels {D : RelSpec.{u, v, w}} (tr : D.Transcript) : + (Transcript.labels tr).length = tr.length := + List.length_map _ + +/-- `D.ValidTranscript s tr s'` holds when `tr` is a valid step-by-step execution starting at `s` +and ending at `s'`, where each step is admissible (`D.valid`) and permitted by `D.step`. -/ +inductive ValidTranscript : D.State → D.Transcript → D.State → Prop + | nil (s : D.State) : ValidTranscript s [] s + | cons {s : D.State} (st : D.Step) {tr : D.Transcript} {s_fin : D.State} : + D.valid s st.op → + D.step s st.op st.nextState st.result → + ValidTranscript st.nextState tr s_fin → + ValidTranscript s (st :: tr) s_fin + +/-- The empty transcript is valid iff the initial and final states coincide. -/ +@[simp] +theorem validTranscript_nil_iff {D : RelSpec.{u, v, w}} (s s' : D.State) : + D.ValidTranscript s [] s' ↔ s = s' := by + constructor + · intro h; cases h; rfl + · rintro rfl; exact .nil s + +/-- A nonempty transcript is valid iff its head step is admissible and permitted and its tail is +valid from `st.nextState`. -/ +@[simp] +theorem validTranscript_cons_iff {D : RelSpec.{u, v, w}} (s s' : D.State) (st : D.Step) + (tr : D.Transcript) : + D.ValidTranscript s (st :: tr) s' ↔ + D.valid s st.op ∧ D.step s st.op st.nextState st.result ∧ + D.ValidTranscript st.nextState tr s' := by + constructor + · intro h + cases h with + | cons _ hv hstep htr => exact ⟨hv, hstep, htr⟩ + · rintro ⟨hv, hstep, htr⟩ + exact .cons st hv hstep htr + +/-- Valid transcripts compose across concatenation. -/ +theorem ValidTranscript.append {D : RelSpec.{u, v, w}} {s s₁ s₂ : D.State} + {tr₁ tr₂ : D.Transcript} (h₁ : D.ValidTranscript s tr₁ s₁) + (h₂ : D.ValidTranscript s₁ tr₂ s₂) : + D.ValidTranscript s (tr₁ ++ tr₂) s₂ := by + induction h₁ with + | nil _ => exact h₂ + | cons st hv hstep _ ih => exact .cons st hv hstep (ih h₂) + +/-- Every valid transcript projects to a multi-step transition in `D.toLTS`. -/ +theorem ValidTranscript.toLTS_mtr {D : RelSpec.{u, v, w}} {s s' : D.State} + {tr : D.Transcript} (h : D.ValidTranscript s tr s') : + D.toLTS.MTr s tr.labels s' := by + induction h with + | nil _ => exact .refl + | cons _ _ hstep _ ih => exact .stepL hstep ih + +/-- Demonic admissibility of a non-adaptive operation sequence `ops : List D.Op` from `s`: +the head operation is admissible at `s`, and the tail sequence remains admissible from every +permitted post-state `s'` of the head operation. -/ +def ValidOpSeq (s : D.State) : List D.Op → Prop + | [] => True + | o :: ops => D.valid s o ∧ ∀ s' a, D.step s o s' a → ValidOpSeq s' ops + +@[simp] +theorem validOpSeq_nil (s : D.State) : D.ValidOpSeq s [] ↔ True := Iff.rfl + +@[simp] +theorem validOpSeq_cons (s : D.State) (o : D.Op) (ops : List D.Op) : + D.ValidOpSeq s (o :: ops) ↔ D.valid s o ∧ ∀ s' a, D.step s o s' a → D.ValidOpSeq s' ops := + Iff.rfl + +/-- Total interfaces have no operation-sequence admissibility obligations. -/ +theorem validOpSeq_of_forall_valid (h : ∀ s o, D.valid s o) (s : D.State) (ops : List D.Op) : + D.ValidOpSeq s ops := by + induction ops generalizing s with + | nil => trivial + | cons o ops ih => exact ⟨h s o, fun s' _ _ => ih s'⟩ + +/-- Under `[D.Total]`, every demonically admissible operation sequence admits at least one valid +transcript. -/ +theorem exists_validTranscript_of_validOpSeq [D.Total] {s : D.State} {ops : List D.Op} + (hv : D.ValidOpSeq s ops) : + ∃ tr s', tr.ops = ops ∧ D.ValidTranscript s tr s' := by + induction ops generalizing s with + | nil => exact ⟨[], s, rfl, .nil s⟩ + | cons o ops ih => + obtain ⟨hv_o, hv_rest⟩ := hv + obtain ⟨s₁, a, hstep⟩ := D.exists_step hv_o + obtain ⟨tr, s₂, hops, htr⟩ := ih (hv_rest s₁ a hstep) + exact ⟨⟨o, a, s₁⟩ :: tr, s₂, congrArg (o :: ·) hops, .cons _ hv_o hstep htr⟩ + +/-- State-dependent charges summed along a transcript. -/ +def totalCost (charge : D.State → D.Op → ℕ) : D.State → D.Transcript → ℕ + | _, [] => 0 + | s, st :: tr => charge s st.op + totalCost charge st.nextState tr + +/-- Outcome-dependent charges summed along a transcript, where each step charge may also inspect +the post-state `st.nextState` and result `st.result`. -/ +def totalCostOutcome (charge : D.State → (o : D.Op) → D.State → D.Result o → ℕ) : + D.State → D.Transcript → ℕ + | _, [] => 0 + | s, st :: tr => + charge s st.op st.nextState st.result + totalCostOutcome charge st.nextState tr + +@[simp] +theorem totalCost_nil (charge : D.State → D.Op → ℕ) (s : D.State) : + D.totalCost charge s [] = 0 := rfl + +@[simp] +theorem totalCost_cons (charge : D.State → D.Op → ℕ) (s : D.State) (st : D.Step) + (tr : D.Transcript) : + D.totalCost charge s (st :: tr) = charge s st.op + D.totalCost charge st.nextState tr := rfl + +@[simp] +theorem totalCostOutcome_nil (charge : D.State → (o : D.Op) → D.State → D.Result o → ℕ) + (s : D.State) : + D.totalCostOutcome charge s [] = 0 := rfl + +@[simp] +theorem totalCostOutcome_cons (charge : D.State → (o : D.Op) → D.State → D.Result o → ℕ) + (s : D.State) (st : D.Step) (tr : D.Transcript) : + D.totalCostOutcome charge s (st :: tr) = + charge s st.op st.nextState st.result + D.totalCostOutcome charge st.nextState tr := rfl + +/-- `totalCost` is the special case of `totalCostOutcome` that ignores the post-state and +result. -/ +theorem totalCost_eq_totalCostOutcome (charge : D.State → D.Op → ℕ) (s : D.State) + (tr : D.Transcript) : + D.totalCost charge s tr = D.totalCostOutcome (fun s o _ _ => charge s o) s tr := by + induction tr generalizing s with + | nil => rfl + | cons st tr ih => simp [ih] + +/-- Along a valid transcript prefix `D.ValidTranscript s tr₁ s₁`, `totalCost` splits additively at +the intermediate state `s₁`. -/ +theorem totalCost_append {D : RelSpec.{u, v, w}} (charge : D.State → D.Op → ℕ) + {s s₁ : D.State} {tr₁ : D.Transcript} (h₁ : D.ValidTranscript s tr₁ s₁) + (tr₂ : D.Transcript) : + D.totalCost charge s (tr₁ ++ tr₂) = + D.totalCost charge s tr₁ + D.totalCost charge s₁ tr₂ := by + induction h₁ with + | nil _ => simp + | cons st _ _ _ ih => simp [ih, Nat.add_assoc] + +/-- Uniform per-operation bounds scale by the transcript length on any valid transcript. -/ +theorem totalCost_le_length_mul {D : RelSpec.{u, v, w}} (charge : D.State → D.Op → ℕ) (T : ℕ) + (h : ∀ s o, D.valid s o → charge s o ≤ T) {s s' : D.State} {tr : D.Transcript} + (hv : D.ValidTranscript s tr s') : + D.totalCost charge s tr ≤ tr.length * T := by + induction hv with + | nil _ => simp + | cons st hv_o _ _ ih => + have hstep := h _ st.op hv_o + simp only [totalCost_cons, List.length_cons, Nat.add_mul, Nat.one_mul] + omega + +/-- A constant charge `K` per operation totals `tr.length * K`. -/ +theorem totalCost_const (K : ℕ) (s : D.State) (tr : D.Transcript) : + D.totalCost (fun _ _ => K) s tr = tr.length * K := by + induction tr generalizing s with + | nil => simp + | cons st tr ih => simp [ih, Nat.add_mul, Nat.add_comm] + +/-- A unit charge per operation totals the transcript length. -/ +theorem totalCost_one (s : D.State) (tr : D.Transcript) : + D.totalCost (fun _ _ => 1) s tr = tr.length := by + simpa using D.totalCost_const 1 s tr + +end RelSpec + +/-! ## Adaptive clients (`ClientRun`, `DemonicValid`) and demonic `Hoare` / `CostHoare` -/ + +namespace RelSpec + +variable (D : RelSpec.{u, v, w}) {α β : Type y} + +/-- `D.ClientRun s p v s' tr` holds when running the adaptive client `p : D.Client α` from initial +state `s` can produce return value `v : α`, final state `s' : D.State`, and transcript `tr` by +making admissible and permitted transitions at each operation call. -/ +inductive ClientRun : D.State → D.Client α → α → D.State → D.Transcript → Prop + | pure (s : D.State) (a : α) : + ClientRun s (.pure a) a s [] + | liftBind {s s₁ s₂ : D.State} (o : D.Op) (k : D.Result o → D.Client α) + (a : D.Result o) {v : α} {tr : D.Transcript} : + D.valid s o → + D.step s o s₁ a → + ClientRun s₁ (k a) v s₂ tr → + ClientRun s (.liftBind o k) v s₂ (⟨o, a, s₁⟩ :: tr) + +/-- Every `ClientRun` produces a valid transcript. -/ +theorem ClientRun.validTranscript {D : RelSpec.{u, v, w}} {s s' : D.State} {p : D.Client α} + {v : α} {tr : D.Transcript} (h : D.ClientRun s p v s' tr) : + D.ValidTranscript s tr s' := by + induction h with + | pure s _ => exact .nil s + | liftBind _ _ _ hv hstep _ ih => exact .cons _ hv hstep ih + +theorem clientRun_pure_iff {D : RelSpec.{u, v, w}} (s : D.State) (a v : α) (s' : D.State) + (tr : D.Transcript) : + D.ClientRun s (.pure a) v s' tr ↔ v = a ∧ s' = s ∧ tr = [] := by + constructor + · intro h + cases h + exact ⟨rfl, rfl, rfl⟩ + · rintro ⟨rfl, rfl, rfl⟩ + exact .pure s' v + +theorem clientRun_liftBind_iff {D : RelSpec.{u, v, w}} (s : D.State) (o : D.Op) + (k : D.Result o → D.Client α) (v : α) (s₂ : D.State) (tr : D.Transcript) : + D.ClientRun s (.liftBind o k) v s₂ tr ↔ + D.valid s o ∧ ∃ a s₁ tr', D.step s o s₁ a ∧ + D.ClientRun s₁ (k a) v s₂ tr' ∧ tr = ⟨o, a, s₁⟩ :: tr' := by + constructor + · intro h + cases h with + | liftBind _ _ a hv hstep hrest => + exact ⟨hv, a, _, _, hstep, hrest, rfl⟩ + · rintro ⟨hv, a, s₁, tr', hstep, hrest, rfl⟩ + exact .liftBind o k a hv hstep hrest + +/-- Sequential client runs compose by concatenating their transcripts. -/ +theorem ClientRun.bind {D : RelSpec.{u, v, w}} {s s₁ s₂ : D.State} {p : D.Client α} + {k : α → D.Client β} {a : α} {b : β} {tr₁ tr₂ : D.Transcript} + (h₁ : D.ClientRun s p a s₁ tr₁) (h₂ : D.ClientRun s₁ (k a) b s₂ tr₂) : + D.ClientRun s (p.bind k) b s₂ (tr₁ ++ tr₂) := by + induction h₁ with + | pure _ _ => exact h₂ + | liftBind o cont res hv hstep _ ih => + exact .liftBind o (fun r => (cont r).bind k) res hv hstep (ih h₂) + +/-- A client run of `p.bind k` splits into a run of `p` and a run of `k` from the intermediate +result and state. -/ +theorem clientRun_bind_iff {D : RelSpec.{u, v, w}} {s s₂ : D.State} {p : D.Client α} + {k : α → D.Client β} {b : β} {tr : D.Transcript} : + D.ClientRun s (p.bind k) b s₂ tr ↔ + ∃ a s₁ tr₁ tr₂, D.ClientRun s p a s₁ tr₁ ∧ + D.ClientRun s₁ (k a) b s₂ tr₂ ∧ tr = tr₁ ++ tr₂ := by + constructor + · intro h + induction p generalizing s tr with + | pure a => + exact ⟨a, s, [], tr, .pure s a, h, rfl⟩ + | liftBind o cont ih => + change D.ClientRun s (.liftBind o fun r => (cont r).bind k) b s₂ tr at h + cases h with + | liftBind _ _ res hv hstep hrest => + obtain ⟨a, s₁, tr₁, tr₂, hp, hk, rfl⟩ := ih res hrest + exact ⟨a, s₁, ⟨o, res, _⟩ :: tr₁, tr₂, .liftBind o cont res hv hstep hp, hk, rfl⟩ + · rintro ⟨a, s₁, tr₁, tr₂, hp, hk, rfl⟩ + exact hp.bind hk + +/-- Demonic admissibility of an adaptive client `p` from state `s`: +* `.pure a` is always admissible; +* `.liftBind o k` requires `D.valid s o` now, and for *every* permitted outcome `(s', a)` of `o` + from `s`, the continuation `k a` must be demonically admissible from `s'`. -/ +inductive DemonicValid : D.State → D.Client α → Prop + | pure (s : D.State) (a : α) : + DemonicValid s (.pure a) + | liftBind {s : D.State} (o : D.Op) (k : D.Result o → D.Client α) : + D.valid s o → + (∀ s' a, D.step s o s' a → DemonicValid s' (k a)) → + DemonicValid s (.liftBind o k) + +theorem demonicValid_pure_iff {D : RelSpec.{u, v, w}} (s : D.State) (a : α) : + D.DemonicValid s (.pure a) ↔ True := + ⟨fun _ => trivial, fun _ => .pure s a⟩ + +theorem demonicValid_liftBind_iff {D : RelSpec.{u, v, w}} (s : D.State) (o : D.Op) + (k : D.Result o → D.Client α) : + D.DemonicValid s (.liftBind o k) ↔ + D.valid s o ∧ ∀ s' a, D.step s o s' a → D.DemonicValid s' (k a) := by + constructor + · intro h + cases h with + | liftBind _ _ hv hk => exact ⟨hv, hk⟩ + · rintro ⟨hv, hk⟩; exact .liftBind o k hv hk + +/-- On a specification where every operation is valid in every state, every client is demonically +admissible. -/ +theorem demonicValid_of_forall_valid (h : ∀ s o, D.valid s o) (s : D.State) (p : D.Client α) : + D.DemonicValid s p := by + induction p generalizing s with + | pure a => exact .pure s a + | liftBind o k ih => exact .liftBind o k (h s o) fun s' a _ => ih a s' + +/-- Under `[D.Total]`, every demonically admissible client admits at least one valid `ClientRun`. -/ +theorem DemonicValid.exists_clientRun {D : RelSpec.{u, v, w}} [D.Total] {s : D.State} + {p : D.Client α} (hv : D.DemonicValid s p) : + ∃ v s' tr, D.ClientRun s p v s' tr := by + induction hv with + | pure s a => exact ⟨a, s, [], .pure s a⟩ + | liftBind o k hv_o _ ih => + obtain ⟨s₁, a, hstep⟩ := D.exists_step hv_o + obtain ⟨v, s₂, tr, hrun⟩ := ih s₁ a hstep + exact ⟨v, s₂, ⟨o, a, s₁⟩ :: tr, .liftBind o k a hv_o hstep hrun⟩ + +/-- Demonic admissibility composes across `bind` when the continuation is demonically admissible +after every valid run of the first client. -/ +theorem DemonicValid.bind {D : RelSpec.{u, v, w}} {s : D.State} {p : D.Client α} + {k : α → D.Client β} (hp : D.DemonicValid s p) + (hk : ∀ a s' tr, D.ClientRun s p a s' tr → D.DemonicValid s' (k a)) : + D.DemonicValid s (p.bind k) := by + induction hp with + | pure s a => exact hk a s [] (.pure s a) + | liftBind o cont hv_o _ ih => + refine .liftBind o (fun r => (cont r).bind k) hv_o fun s₁ r hstep => ?_ + exact ih s₁ r hstep fun a s₂ tr hrun => hk a s₂ (⟨o, r, s₁⟩ :: tr) + (.liftBind o cont r hv_o hstep hrun) + +/-- Demonic Hoare triple for a relational specification: from any initial state satisfying `pre`, +the client `p` is demonically admissible and *every* valid `ClientRun` satisfies `post`. -/ +def Hoare (pre : D.State → Prop) (p : D.Client α) (post : α → D.State → Prop) : Prop := + ∀ s, pre s → D.DemonicValid s p ∧ ∀ v s' tr, D.ClientRun s p v s' tr → post v s' + +namespace Hoare + +variable {D : RelSpec.{u, v, w}} + +/-- Returning a pure value leaves the state unchanged. -/ +theorem pure (pre : D.State → Prop) (a : α) (post : α → D.State → Prop) + (h : ∀ s, pre s → post a s) : D.Hoare pre (.pure a) post := by + intro s hs + refine ⟨.pure s a, ?_⟩ + intro v s' tr hrun + cases hrun + exact h s hs + +/-- Calling an operation requires `D.valid s o` and establishes `post a s'` for every permitted +outcome `D.step s o s' a`. -/ +theorem call (pre : D.State → Prop) (o : D.Op) (post : D.Result o → D.State → Prop) + (h : ∀ s, pre s → D.valid s o ∧ ∀ s' a, D.step s o s' a → post a s') : + D.Hoare pre (.liftBind o fun a => .pure a) post := by + intro s hs + obtain ⟨hv, hpost⟩ := h s hs + refine ⟨.liftBind o (fun a => .pure a) hv fun s' a _ => .pure s' a, ?_⟩ + intro v s' tr hrun + cases hrun with + | liftBind _ _ a _ hstep hrest => + cases hrest + exact hpost s' v hstep + +/-- Sequential composition of demonic Hoare triples. -/ +theorem bind {pre : D.State → Prop} {mid : α → D.State → Prop} + {post : β → D.State → Prop} {p : D.Client α} {k : α → D.Client β} + (hp : D.Hoare pre p mid) (hk : ∀ a, D.Hoare (mid a) (k a) post) : + D.Hoare pre (p.bind k) post := by + intro s hs + obtain ⟨hvp, hpost_p⟩ := hp s hs + refine ⟨hvp.bind fun a s₁ tr₁ hrun₁ => (hk a s₁ (hpost_p a s₁ tr₁ hrun₁)).1, ?_⟩ + intro b s₂ tr hrun + obtain ⟨a, s₁, tr₁, tr₂, hrun₁, hrun₂, _⟩ := clientRun_bind_iff.mp hrun + exact (hk a s₁ (hpost_p a s₁ tr₁ hrun₁)).2 b s₂ tr₂ hrun₂ + +/-- Consequence rule for demonic Hoare triples. -/ +theorem consequence {pre pre' : D.State → Prop} {post post' : α → D.State → Prop} + {p : D.Client α} (h : D.Hoare pre p post) (hp : ∀ s, pre' s → pre s) + (hq : ∀ a s, post a s → post' a s) : D.Hoare pre' p post' := by + intro s hs + obtain ⟨hv, hpost⟩ := h s (hp s hs) + exact ⟨hv, fun v s' tr hrun => hq v s' (hpost v s' tr hrun)⟩ + +end Hoare + +/-- Demonic `CostHoare` triple: strengthens `D.Hoare` with a worst-case abstract charge bound +`D.totalCost charge s tr ≤ budget s` across *every* valid `ClientRun`. -/ +def CostHoare (charge : D.State → D.Op → ℕ) (pre : D.State → Prop) + (p : D.Client α) (post : α → D.State → Prop) (budget : D.State → ℕ) : Prop := + D.Hoare pre p post ∧ + ∀ s, pre s → ∀ v s' tr, D.ClientRun s p v s' tr → D.totalCost charge s tr ≤ budget s + +namespace CostHoare + +variable {D : RelSpec.{u, v, w}} {charge : D.State → D.Op → ℕ} {pre : D.State → Prop} + {post : α → D.State → Prop} {budget : D.State → ℕ} {p : D.Client α} + +/-- Project a `CostHoare` triple to its underlying `Hoare` triple. -/ +theorem hoare (h : D.CostHoare charge pre p post budget) : D.Hoare pre p post := h.1 + +/-- Returning a pure value incurs zero operation cost. -/ +theorem pure (a : α) (h : ∀ s, pre s → post a s) : + D.CostHoare charge pre (.pure a) post (fun _ => 0) := by + refine ⟨Hoare.pure pre a post h, ?_⟩ + intro s _ v s' tr hrun + cases hrun + exact Nat.le_refl 0 + +/-- Calling a single operation charges `charge s o` at the pre-state `s`. -/ +theorem call (o : D.Op) {post : D.Result o → D.State → Prop} + (h : ∀ s, pre s → D.valid s o ∧ ∀ s' a, D.step s o s' a → post a s') : + D.CostHoare charge pre (.liftBind o fun a => .pure a) post (fun s => charge s o) := by + refine ⟨Hoare.call pre o post h, ?_⟩ + intro s _ v s' tr hrun + cases hrun with + | liftBind _ _ a _ _ hrest => + cases hrest + simp + +/-- Compose `CostHoare` budgets at every intermediate state satisfying `mid`. -/ +theorem bind {mid : α → D.State → Prop} {post : β → D.State → Prop} + {k : α → D.Client β} {first : D.State → ℕ} {next : α → D.State → ℕ} + (hp : D.CostHoare charge pre p mid first) + (hk : ∀ a, D.CostHoare charge (mid a) (k a) post (next a)) + (hb : ∀ s, pre s → ∀ a s', mid a s' → first s + next a s' ≤ budget s) : + D.CostHoare charge pre (p.bind k) post budget := by + refine ⟨Hoare.bind hp.hoare (fun a => (hk a).hoare), ?_⟩ + intro s hs b s₂ tr hrun + obtain ⟨a, s₁, tr₁, tr₂, hrun₁, hrun₂, rfl⟩ := clientRun_bind_iff.mp hrun + have hmid : mid a s₁ := (hp.hoare s hs).2 a s₁ tr₁ hrun₁ + have hf := hp.2 s hs a s₁ tr₁ hrun₁ + have hn := (hk a).2 s₁ hmid b s₂ tr₂ hrun₂ + rw [D.totalCost_append charge hrun₁.validTranscript tr₂] + exact (Nat.add_le_add hf hn).trans (hb s hs a s₁ hmid) + +/-- Consequence rule for demonic `CostHoare` triples. -/ +theorem consequence {pre' : D.State → Prop} {post' : α → D.State → Prop} + {budget' : D.State → ℕ} (h : D.CostHoare charge pre p post budget) + (hp : ∀ s, pre' s → pre s) (hq : ∀ a s, post a s → post' a s) + (hb : ∀ s, pre' s → budget s ≤ budget' s) : + D.CostHoare charge pre' p post' budget' := + ⟨h.hoare.consequence hp hq, + fun s hs v s' tr hrun => (h.2 s (hp s hs) v s' tr hrun).trans (hb s hs)⟩ + +end CostHoare + +end RelSpec + +/-! ## Equivalence of relational and deterministic client semantics on `D.toRelSpec` -/ + +namespace Spec + +variable (D : Spec.{u, v, w}) {α : Type y} + +/-- On `D.toRelSpec`, non-adaptive operation-sequence admissibility `ValidOpSeq` coincides with +`D.ValidHistory`. -/ +theorem toRelSpec_validOpSeq_iff (s : D.State) (ops : List D.Op) : + D.toRelSpec.ValidOpSeq s ops ↔ D.ValidHistory s ops := by + induction ops generalizing s with + | nil => rfl + | cons o ops ih => + change (D.valid s o ∧ ∀ s' a, (s', a) = D.step s o → D.toRelSpec.ValidOpSeq s' ops) ↔ + D.valid s o ∧ D.ValidHistory (D.step s o).1 ops + refine and_congr_right fun _ => ?_ + constructor + · intro h + exact (ih (D.step s o).1).mp (h (D.step s o).1 (D.step s o).2 Prod.mk.eta) + · intro h s' a heq + have hs' : s' = (D.step s o).1 := congrArg Prod.fst heq + subst hs' + exact (ih (D.step s o).1).mpr h + +/-- The unique canonical transcript determined by a deterministic specification `D` on an +operation list `os` from state `s`. -/ +def canonicalTranscript (s : D.State) : List D.Op → D.toRelSpec.Transcript + | [] => [] + | o :: os => + let next := D.step s o + ⟨o, next.2, next.1⟩ :: canonicalTranscript next.1 os + +@[simp] +theorem canonicalTranscript_nil (s : D.State) : D.canonicalTranscript s [] = [] := rfl + +@[simp] +theorem canonicalTranscript_cons (s : D.State) (o : D.Op) (os : List D.Op) : + D.canonicalTranscript s (o :: os) = + ⟨o, (D.step s o).2, (D.step s o).1⟩ :: D.canonicalTranscript (D.step s o).1 os := rfl + +/-- The underlying operation list of `D.canonicalTranscript s os` is `os`. -/ +@[simp] +theorem canonicalTranscript_ops (s : D.State) (os : List D.Op) : + (D.canonicalTranscript s os).ops = os := by + induction os generalizing s with + | nil => rfl + | cons o os ih => simp [ih] + +/-- `totalCost` along `D.canonicalTranscript s os` equals `D.totalCost charge s os`. -/ +@[simp] +theorem toRelSpec_totalCost_canonicalTranscript (charge : D.State → D.Op → ℕ) (s : D.State) + (os : List D.Op) : + D.toRelSpec.totalCost charge s (D.canonicalTranscript s os) = D.totalCost charge s os := by + induction os generalizing s with + | nil => rfl + | cons o os ih => + change charge s o + + D.toRelSpec.totalCost charge (D.step s o).1 (D.canonicalTranscript (D.step s o).1 os) = + charge s o + D.totalCost charge (D.step s o).1 os + rw [ih] + +/-- On `D.toRelSpec`, demonic client admissibility `DemonicValid s p` is equivalent to +`D.ValidHistory s (D.runClient s p).2.2`. -/ +theorem toRelSpec_demonicValid_iff (s : D.State) (p : D.Client α) : + D.toRelSpec.DemonicValid s p ↔ D.ValidHistory s (D.runClient s p).2.2 := by + induction p generalizing s with + | pure a => + exact ⟨fun _ => trivial, fun _ => RelSpec.DemonicValid.pure (D := D.toRelSpec) s a⟩ + | liftBind o k ih => + rw [RelSpec.demonicValid_liftBind_iff (D := D.toRelSpec), runClient_liftBind, validHistory_cons] + refine and_congr_right fun _ => ?_ + constructor + · intro h + exact (ih (D.step s o).2 (D.step s o).1).mp + (h (D.step s o).1 (D.step s o).2 Prod.mk.eta) + · intro h s' a heq + have hs' : s' = (D.step s o).1 := congrArg Prod.fst heq + have ha : a = (D.step s o).2 := congrArg Prod.snd heq + subst hs'; subst ha + exact (ih (D.step s o).2 (D.step s o).1).mpr h + +/-- Every client with an admissible deterministic history executes canonically in `D.toRelSpec`. -/ +theorem toRelSpec_clientRun_canonical (s : D.State) (p : D.Client α) + (hv : D.ValidHistory s (D.runClient s p).2.2) : + D.toRelSpec.ClientRun s p (D.runClient s p).1 (D.runClient s p).2.1 + (D.canonicalTranscript s (D.runClient s p).2.2) := by + induction p generalizing s with + | pure a => + exact RelSpec.ClientRun.pure (D := D.toRelSpec) s a + | liftBind o k ih => + exact RelSpec.ClientRun.liftBind (D := D.toRelSpec) o k (D.step s o).2 hv.1 Prod.mk.eta + (ih (D.step s o).2 (D.step s o).1 hv.2) + +private theorem toRelSpec_clientRun_imp {s : D.toRelSpec.State} {p : D.toRelSpec.Client α} + {v : α} {s' : D.toRelSpec.State} {tr : D.toRelSpec.Transcript} + (h : D.toRelSpec.ClientRun s p v s' tr) : + D.ValidHistory s (D.runClient s p).2.2 ∧ + v = (D.runClient s p).1 ∧ + s' = (D.runClient s p).2.1 ∧ + tr = D.canonicalTranscript s (D.runClient s p).2.2 := by + induction h with + | pure s a => + exact ⟨trivial, rfl, rfl, rfl⟩ + | @liftBind s s₁ s₂ o k res v tr hv hstep _ ih => + have hs₁ : s₁ = (D.step s o).1 := congrArg Prod.fst hstep + have hres : res = (D.step s o).2 := congrArg Prod.snd hstep + subst hs₁; subst hres + obtain ⟨hv_rest, rfl, rfl, rfl⟩ := ih + exact ⟨⟨hv, hv_rest⟩, rfl, rfl, rfl⟩ + +/-- On `D.toRelSpec`, `ClientRun` collapses uniquely to `D.runClient` and `D.ValidHistory`. -/ +theorem toRelSpec_clientRun_iff (s : D.State) (p : D.Client α) (v : α) (s' : D.State) + (tr : D.toRelSpec.Transcript) : + D.toRelSpec.ClientRun s p v s' tr ↔ + D.ValidHistory s (D.runClient s p).2.2 ∧ + v = (D.runClient s p).1 ∧ + s' = (D.runClient s p).2.1 ∧ + tr = D.canonicalTranscript s (D.runClient s p).2.2 := + ⟨D.toRelSpec_clientRun_imp, fun ⟨hv, h₁, h₂, h₃⟩ => + h₁ ▸ h₂ ▸ h₃ ▸ D.toRelSpec_clientRun_canonical s p hv⟩ + +/-- On `D.toRelSpec`, `RelSpec.Hoare` is equivalent to `Spec.Hoare`. -/ +theorem toRelSpec_hoare_iff (pre : D.State → Prop) (p : D.Client α) (post : α → D.State → Prop) : + D.toRelSpec.Hoare pre p post ↔ D.Hoare pre p post := by + refine forall_congr' fun s => imp_congr_right fun _ => ?_ + rw [toRelSpec_demonicValid_iff] + constructor + · rintro ⟨hv, hpost⟩ + exact ⟨hv, hpost _ _ _ (D.toRelSpec_clientRun_canonical s p hv)⟩ + · rintro ⟨hv, hpost⟩ + refine ⟨hv, fun v s' tr hrun => ?_⟩ + obtain ⟨_, rfl, rfl, _⟩ := (D.toRelSpec_clientRun_iff s p v s' tr).mp hrun + exact hpost + +/-- On `D.toRelSpec`, `RelSpec.CostHoare` is equivalent to `Spec.CostHoare`. -/ +theorem toRelSpec_costHoare_iff (charge : D.State → D.Op → ℕ) (pre : D.State → Prop) + (p : D.Client α) (post : α → D.State → Prop) (budget : D.State → ℕ) : + D.toRelSpec.CostHoare charge pre p post budget ↔ D.CostHoare charge pre p post budget := by + unfold RelSpec.CostHoare Spec.CostHoare + rw [toRelSpec_hoare_iff] + refine and_congr_right fun hhoare => forall_congr' fun s => imp_congr_right fun hs => ?_ + have hv := (hhoare s hs).1 + constructor + · intro h + have hb := h _ _ _ (D.toRelSpec_clientRun_canonical s p hv) + rwa [D.toRelSpec_totalCost_canonicalTranscript] at hb + · intro h v s' tr hrun + obtain ⟨_, _, _, rfl⟩ := (D.toRelSpec_clientRun_iff s p v s' tr).mp hrun + rwa [D.toRelSpec_totalCost_canonicalTranscript] + +end Spec + +/-! ## Angelic backend realizations (`RelRealization`, `RelBound`, `RelAmortized`) -/ + +/-- Angelic semantic realization of a relational specification `D : RelSpec`: +* `total`: every represented state and admissible operation admits a native execution; +* `correct`: every native execution from a represented state on an admissible operation realizes + *some* permitted abstract post-state `s'` with `D.step s o s' a ∧ rep s' c'`. -/ +structure RelRealization (D : RelSpec.{u, v, w}) {C τ : Type x} + (exec : Execution D C τ) where + /-- Representation invariant linking abstract states to concrete backend states. -/ + rep : D.State → C → Prop + /-- Existence of a native execution for every represented state and admissible operation. -/ + total : ∀ {s c} o, rep s c → D.valid s o → ∃ a c' trace, exec c o a c' trace + /-- Angelic step correctness: every native execution realizes some permitted abstract step. -/ + correct : ∀ {s c o a c' trace}, rep s c → D.valid s o → exec c o a c' trace → + ∃ s', D.step s o s' a ∧ rep s' c' + +namespace RelRealization + +variable {D : RelSpec.{u, v, w}} {C τ : Type x} {α : Type y} + {exec : Execution D C τ} (R : RelRealization D exec) + +/-- From related states, every native execution of a demonically admissible client realizes a +valid `ClientRun` ending in a related state. -/ +theorem correct_client {p : D.Client α} {s : D.State} {c c' : C} {a : α} + {traces : List τ} (hr : R.rep s c) (hv : D.DemonicValid s p) + (he : Executes exec p c a c' traces) : + ∃ s' tr, D.ClientRun s p a s' tr ∧ R.rep s' c' ∧ traces.length = tr.length := by + induction he generalizing s with + | pure a c => + exact ⟨s, [], .pure s a, hr, rfl⟩ + | @call o k c res c₁ trace b c₂ traces ho hes ih => + obtain ⟨hv_o, hv_k⟩ := (D.demonicValid_liftBind_iff s o k).mp hv + obtain ⟨s₁, hstep, hr₁⟩ := R.correct hr hv_o ho + obtain ⟨s₂, tr, hrun, hr₂, hlen⟩ := ih hr₁ (hv_k s₁ res hstep) + exact ⟨s₂, ⟨o, res, s₁⟩ :: tr, .liftBind o k res hv_o hstep hrun, hr₂, + congrArg (· + 1) hlen⟩ + +/-- From related states, every demonically admissible client admits a native execution realizing a +valid `ClientRun` ending in a related state. -/ +theorem exists_executes (p : D.Client α) {s : D.State} {c : C} (hr : R.rep s c) + (hv : D.DemonicValid s p) : + ∃ a c' traces s' tr, Executes exec p c a c' traces ∧ + D.ClientRun s p a s' tr ∧ R.rep s' c' ∧ traces.length = tr.length := by + induction hv generalizing c with + | pure s a => + exact ⟨a, c, [], s, [], .pure a c, .pure s a, hr, rfl⟩ + | liftBind o k hv_o _ ih => + obtain ⟨res, c₁, trace, ho⟩ := R.total o hr hv_o + obtain ⟨s₁, hstep, hr₁⟩ := R.correct hr hv_o ho + obtain ⟨b, c₂, traces, s₂, tr, hes, hrun, hr₂, hlen⟩ := ih s₁ res hstep hr₁ + exact ⟨b, c₂, trace :: traces, s₂, ⟨o, res, s₁⟩ :: tr, + .call ho hes, .liftBind o k res hv_o hstep hrun, hr₂, congrArg (· + 1) hlen⟩ + +end RelRealization + +/-- Lift a deterministic `Realization D exec` to a relational `RelRealization D.toRelSpec exec`. -/ +def Realization.toRelRealization {D : Spec.{u, v, w}} {C τ : Type x} + {exec : Execution D C τ} (R : Realization D exec) : + RelRealization D.toRelSpec exec where + rep := R.rep + total o hr hv := R.total o hr hv + correct {s c o a c' trace} hr hv he := by + obtain ⟨ha, hr'⟩ := R.correct hr hv he + exact ⟨(D.step s o).1, Prod.ext rfl ha, hr'⟩ + +/-- Every native execution of a verified demonic `Hoare` client satisfies its abstract +postcondition in a related final state. -/ +theorem RelSpec.Hoare.realize {D : RelSpec.{u, v, w}} {C Trace : Type x} {α : Type y} + {exec : Execution D C Trace} (R : RelRealization D exec) + {pre : D.State → Prop} {post : α → D.State → Prop} {p : D.Client α} + (h : D.Hoare pre p post) {s : D.State} {c c' : C} {a : α} {traces : List Trace} + (hp : pre s) (hr : R.rep s c) (he : Executes exec p c a c' traces) : + ∃ s' tr, D.ClientRun s p a s' tr ∧ R.rep s' c' ∧ post a s' := by + obtain ⟨hv, hpost⟩ := h s hp + obtain ⟨s', tr, hrun, hr', _⟩ := R.correct_client hr hv he + exact ⟨s', tr, hrun, hr', hpost a s' tr hrun⟩ + +/-- Per-operation worst-case resource certificate over a relational realization `R`. -/ +def RelBound {D : RelSpec.{u, v, w}} {C τ : Type x} {exec : Execution D C τ} + (R : RelRealization D exec) (weight : τ → ℕ) (charge : D.State → D.Op → ℕ) : Prop := + ∀ {s c o a c' trace}, R.rep s c → D.valid s o → exec c o a c' trace → + weight trace ≤ charge s o + +/-- Lift a deterministic `Bound R weight charge` to `RelBound R.toRelRealization weight charge`. -/ +theorem Bound.toRelBound {D : Spec.{u, v, w}} {C τ : Type x} {exec : Execution D C τ} + {R : Realization D exec} {weight : τ → ℕ} {charge : D.State → D.Op → ℕ} + (B : Bound R weight charge) : + RelBound R.toRelRealization weight charge := + fun hr hv he => B hr hv he + +/-- Potential-method amortized resource certificate over a relational realization `R`: +for every native operation execution from `R.rep s c` on an admissible `o`, there exists a +permitted abstract post-state `s'` satisfying `D.step s o s' a`, `R.rep s' c'`, and the +single-step potential inequality `weight trace + potential s' c' ≤ charge s o + potential s c`. -/ +structure RelAmortized {D : RelSpec.{u, v, w}} {C Trace : Type x} + {exec : Execution D C Trace} (R : RelRealization D exec) (weight : Trace → ℕ) + (potential : D.State → C → ℕ) (charge : D.State → D.Op → ℕ) : Prop where + /-- Angelic single-step potential inequality witnessing a permitted post-state `s'`. -/ + step : ∀ {s c o a c' trace}, R.rep s c → D.valid s o → exec c o a c' trace → + ∃ s', D.step s o s' a ∧ R.rep s' c' ∧ + weight trace + potential s' c' ≤ charge s o + potential s c + +namespace RelAmortized + +variable {D : RelSpec.{u, v, w}} {C Trace : Type x} {α : Type y} + {exec : Execution D C Trace} {R : RelRealization D exec} + {weight : Trace → ℕ} {potential : D.State → C → ℕ} {charge : D.State → D.Op → ℕ} + (A : RelAmortized R weight potential charge) + +include A in +/-- **Amortized telescoping along adaptive clients (`executes_le`).** +For any demonically admissible client `p` and native execution `Executes exec p c a c' traces`, +there exist a final abstract state `s'` and transcript `tr` such that `D.ClientRun s p a s' tr`, +`R.rep s' c'`, and +`total weight traces + potential s' c' ≤ D.totalCost charge s tr + potential s c`. -/ +theorem executes_le {p : D.Client α} {s : D.State} {c c' : C} {a : α} + {traces : List Trace} (hr : R.rep s c) (hv : D.DemonicValid s p) + (he : Executes exec p c a c' traces) : + ∃ s' tr, D.ClientRun s p a s' tr ∧ R.rep s' c' ∧ + traces.length = tr.length ∧ + total weight traces + potential s' c' ≤ + D.totalCost charge s tr + potential s c := by + induction he generalizing s with + | pure a c => + exact ⟨s, [], .pure s a, hr, rfl, Nat.le_refl _⟩ + | @call o k c res c₁ trace b c₂ traces ho hes ih => + obtain ⟨hv_o, hv_k⟩ := (D.demonicValid_liftBind_iff s o k).mp hv + obtain ⟨s₁, hstep, hr₁, hpot₁⟩ := A.step hr hv_o ho + obtain ⟨s₂, tr, hrun, hr₂, hlen, hpot₂⟩ := ih hr₁ (hv_k s₁ res hstep) + refine ⟨s₂, ⟨o, res, s₁⟩ :: tr, .liftBind o k res hv_o hstep hrun, hr₂, + congrArg (· + 1) hlen, ?_⟩ + simp only [total_cons, RelSpec.totalCost_cons] + omega + +include A in +/-- **Existence, correctness, and amortized bound (`run_client_amortized`).** +From related initial states, every demonically admissible client `p` admits a native execution +satisfying `D.ClientRun`, `R.rep`, and the telescoping potential bound. -/ +theorem run_client_amortized (p : D.Client α) {s : D.State} {c : C} + (hr : R.rep s c) (hv : D.DemonicValid s p) : + ∃ a c' traces s' tr, Executes exec p c a c' traces ∧ + D.ClientRun s p a s' tr ∧ R.rep s' c' ∧ + traces.length = tr.length ∧ + total weight traces + potential s' c' ≤ + D.totalCost charge s tr + potential s c := by + obtain ⟨a, c', traces, _, _, he, _, _, _⟩ := R.exists_executes p hr hv + obtain ⟨s', tr, hrun, hr', hlen, hpot⟩ := A.executes_le hr hv he + exact ⟨a, c', traces, s', tr, he, hrun, hr', hlen, hpot⟩ + +/-- A worst-case `RelBound` is a zero-potential `RelAmortized` certificate. -/ +theorem of_relBound (B : RelBound R weight charge) : + RelAmortized R weight (fun _ _ => 0) charge where + step hr hv he := by + obtain ⟨s', hstep, hr'⟩ := R.correct hr hv he + exact ⟨s', hstep, hr', by simpa using B hr hv he⟩ + +end RelAmortized + +/-- Lift a deterministic `Amortized R weight potential charge` certificate to +`RelAmortized R.toRelRealization weight potential charge`. -/ +theorem Amortized.toRelAmortized {D : Spec.{u, v, w}} {C Trace : Type x} + {exec : Execution D C Trace} {R : Realization D exec} + {weight : Trace → ℕ} {potential : D.State → C → ℕ} {charge : D.State → D.Op → ℕ} + (A : Amortized R weight potential charge) : + RelAmortized R.toRelRealization weight potential charge where + step {s c o a c' trace} hr hv he := by + obtain ⟨ha, hr'⟩ := R.correct hr hv he + exact ⟨(D.step s o).1, Prod.ext rfl ha, hr', A.step hr hv he⟩ + +namespace RelBound + +variable {D : RelSpec.{u, v, w}} {C τ : Type x} {α : Type y} + {exec : Execution D C τ} {R : RelRealization D exec} + {weight : τ → ℕ} {charge : D.State → D.Op → ℕ} + (B : RelBound R weight charge) + +include B in +/-- Under a worst-case `RelBound`, every native execution of a demonically admissible client has +total weight at most `D.totalCost charge s tr` for some valid `ClientRun` transcript `tr`. -/ +theorem client {p : D.Client α} {s : D.State} {c c' : C} {a : α} {traces : List τ} + (hr : R.rep s c) (hv : D.DemonicValid s p) (he : Executes exec p c a c' traces) : + ∃ s' tr, D.ClientRun s p a s' tr ∧ R.rep s' c' ∧ + traces.length = tr.length ∧ + total weight traces ≤ D.totalCost charge s tr := by + obtain ⟨s', tr, hrun, hr', hlen, hpot⟩ := (RelAmortized.of_relBound B).executes_le hr hv he + exact ⟨s', tr, hrun, hr', hlen, by simpa using hpot⟩ + +include B in +/-- Under a worst-case `RelBound`, every demonically admissible client admits a native execution +meeting the total charge bound. -/ +theorem exists_executes (p : D.Client α) {s : D.State} {c : C} + (hr : R.rep s c) (hv : D.DemonicValid s p) : + ∃ a c' traces s' tr, Executes exec p c a c' traces ∧ + D.ClientRun s p a s' tr ∧ R.rep s' c' ∧ + traces.length = tr.length ∧ + total weight traces ≤ D.totalCost charge s tr := by + obtain ⟨a, c', traces, s', tr, he, hrun, hr', hlen, hpot⟩ := + (RelAmortized.of_relBound B).run_client_amortized p hr hv + exact ⟨a, c', traces, s', tr, he, hrun, hr', hlen, by simpa using hpot⟩ + +end RelBound + +/-- Transport a demonic `CostHoare` triple through a `RelAmortized` certificate to bound native +execution weight plus final potential by `budget s + potential s c`. -/ +theorem RelSpec.CostHoare.realize_amortized {D : RelSpec.{u, v, w}} {C τ : Type x} {α : Type y} + {exec : Execution D C τ} {R : RelRealization D exec} + {weight : τ → ℕ} {potential : D.State → C → ℕ} {charge : D.State → D.Op → ℕ} + {pre : D.State → Prop} {post : α → D.State → Prop} {budget : D.State → ℕ} + {p : D.Client α} (A : RelAmortized R weight potential charge) + (h : D.CostHoare charge pre p post budget) {s : D.State} {c c' : C} + {a : α} {traces : List τ} (hs : pre s) (hr : R.rep s c) + (he : Executes exec p c a c' traces) : + ∃ s' tr, D.ClientRun s p a s' tr ∧ R.rep s' c' ∧ post a s' ∧ + total weight traces + potential s' c' ≤ budget s + potential s c := by + obtain ⟨hv, hpost⟩ := h.1 s hs + obtain ⟨s', tr, hrun, hr', _, hpot⟩ := A.executes_le hr hv he + have hbud := h.2 s hs a s' tr hrun + exact ⟨s', tr, hrun, hr', hpost a s' tr hrun, by omega⟩ + +/-- Transport a demonic `CostHoare` triple through a worst-case `RelBound` certificate. -/ +theorem RelSpec.CostHoare.realize {D : RelSpec.{u, v, w}} {C τ : Type x} {α : Type y} + {exec : Execution D C τ} {R : RelRealization D exec} + {weight : τ → ℕ} {charge : D.State → D.Op → ℕ} + {pre : D.State → Prop} {post : α → D.State → Prop} {budget : D.State → ℕ} + {p : D.Client α} (B : RelBound R weight charge) + (h : D.CostHoare charge pre p post budget) {s : D.State} {c c' : C} + {a : α} {traces : List τ} (hs : pre s) (hr : R.rep s c) + (he : Executes exec p c a c' traces) : + (∃ s', R.rep s' c' ∧ post a s') ∧ total weight traces ≤ budget s := by + obtain ⟨s', _, _, hr', hpost, hpot⟩ := + h.realize_amortized (RelAmortized.of_relBound B) hs hr he + exact ⟨⟨s', hr', hpost⟩, by simpa using hpot⟩ + +/-- Existence of a native execution satisfying a demonic `CostHoare` contract under `RelBound`. -/ +theorem RelSpec.CostHoare.exists_executes {D : RelSpec.{u, v, w}} {C τ : Type x} {α : Type y} + {exec : Execution D C τ} {R : RelRealization D exec} + {weight : τ → ℕ} {charge : D.State → D.Op → ℕ} + {pre : D.State → Prop} {post : α → D.State → Prop} {budget : D.State → ℕ} + {p : D.Client α} (B : RelBound R weight charge) + (h : D.CostHoare charge pre p post budget) {s : D.State} {c : C} + (hs : pre s) (hr : R.rep s c) : + ∃ a c' traces, Executes exec p c a c' traces ∧ + (∃ s', R.rep s' c' ∧ post a s') ∧ total weight traces ≤ budget s := by + obtain ⟨a, c', traces, _, _, he, _, _, _⟩ := R.exists_executes p hr (h.1 s hs).1 + exact ⟨a, c', traces, he, h.realize B hs hr he⟩ + +end Cslib.Stateful diff --git a/ComplexityTests/StatefulRelational.lean b/ComplexityTests/StatefulRelational.lean new file mode 100644 index 000000000..1b6980b9c --- /dev/null +++ b/ComplexityTests/StatefulRelational.lean @@ -0,0 +1,241 @@ +/- +Copyright (c) 2026 Renfei Zhou. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Renfei Zhou +-/ + +module +-- upstream: local-only (test driver) + +public import Complexity.Init +public import Complexity.Foundations.Semantics.Stateful.Relational + +/-! +# Tests for Relational Stateful Specifications + +Concrete non-vacuous tests for `Cslib.Stateful.RelSpec`, its totality class `RelSpec.Total`, +projection `RelSpec.toLTS`, zero-cost deterministic embedding `Spec.toRelSpec`, demonic adaptive +client semantics (`ClientRun`, `DemonicValid`, `RelSpec.Hoare`, `RelSpec.CostHoare`), and angelic +backend realization and amortized analysis (`RelRealization`, `RelBound`, `RelAmortized`). +-/ + +@[expose] public section + +namespace ComplexityTests.StatefulRelational + +open Cslib.Stateful + +/-! ## 1. A genuinely nondeterministic relational specification (two-slot allocator) -/ + +/-- Operations of a two-slot index allocator: `alloc` nondeterministically returns any free +slot in `Fin 2`; `free i` releases slot `i`. -/ +inductive TwoSlotOp : Type + | alloc : TwoSlotOp + | free : Fin 2 → TwoSlotOp + deriving DecidableEq + +/-- Relational specification of a two-slot allocator whose state `(b₀, b₁) : Bool × Bool` +records whether slots `0` and `1` are currently occupied. When both slots are free, `alloc` +nondeterministically returns either `0` or `1`. -/ +abbrev TwoSlotAlloc : RelSpec where + State := Bool × Bool + Op := TwoSlotOp + Result + | .alloc => Fin 2 + | .free _ => Unit + valid s + | .alloc => s.1 = false ∨ s.2 = false + | .free _ => True + step s o s' a := + match o, a with + | .alloc, i => + (i = 0 ∧ s.1 = false ∧ s' = (true, s.2)) ∨ + (i = 1 ∧ s.2 = false ∧ s' = (s.1, true)) + | .free i, () => + s' = if i = 0 then (false, s.2) else (s.1, false) + +instance : TwoSlotAlloc.Total where + step_total s o hv := by + cases o with + | alloc => + rcases hv with h0 | h1 + · exact ⟨(true, s.2), 0, Or.inl ⟨rfl, h0, rfl⟩⟩ + · exact ⟨(s.1, true), 1, Or.inr ⟨rfl, h1, rfl⟩⟩ + | free i => + exact ⟨if i = 0 then (false, s.2) else (s.1, false), (), rfl⟩ + +/-- Both slot `0` and slot `1` are valid outcomes of `alloc` from `(false, false)`. -/ +theorem twoSlotAlloc_nondeterministic : + ((true, false), (0 : Fin 2)) ∈ TwoSlotAlloc.outcomes (false, false) .alloc ∧ + ((false, true), (1 : Fin 2)) ∈ TwoSlotAlloc.outcomes (false, false) .alloc := + ⟨Or.inl ⟨rfl, rfl, rfl⟩, Or.inr ⟨rfl, rfl, rfl⟩⟩ + +/-- Sample LTS transition via `RelSpec.toLTS`. -/ +theorem twoSlotAlloc_toLTS_tr : + TwoSlotAlloc.toLTS.Tr (false, false) ⟨.alloc, (1 : Fin 2)⟩ (false, true) := + Or.inr ⟨rfl, rfl, rfl⟩ + +/-! ## 2. Adaptive client, demonic admissibility, and Hoare / CostHoare verification -/ + +/-- Adaptive client that allocates two slots sequentially, frees the first slot returned, and +returns the pair `(i, j)` of allocated slots. -/ +abbrev allocTwoAndFreeFirst : TwoSlotAlloc.Client (Fin 2 × Fin 2) := + .liftBind TwoSlotOp.alloc fun i => + .liftBind TwoSlotOp.alloc fun j => + .liftBind (TwoSlotOp.free i) fun _ => + .pure (i, j) + +/-- From `(false, false)`, `allocTwoAndFreeFirst` is demonically admissible across all +nondeterministic choices of the first and second allocations. -/ +theorem allocTwoAndFreeFirst_demonicValid : + TwoSlotAlloc.DemonicValid (false, false) allocTwoAndFreeFirst := by + refine RelSpec.DemonicValid.liftBind (D := TwoSlotAlloc) TwoSlotOp.alloc _ (Or.inl rfl) + fun s₁ i h₁ => ?_ + have hv₂ : TwoSlotAlloc.valid s₁ TwoSlotOp.alloc := by + rcases h₁ with ⟨rfl, _, rfl⟩ | ⟨rfl, _, rfl⟩ + · exact Or.inr rfl + · exact Or.inl rfl + refine RelSpec.DemonicValid.liftBind (D := TwoSlotAlloc) TwoSlotOp.alloc _ hv₂ + fun s₂ j _ => ?_ + refine RelSpec.DemonicValid.liftBind (D := TwoSlotAlloc) (TwoSlotOp.free i) _ trivial + fun s₃ _ _ => .pure s₃ (i, j) + +/-- Demonic `CostHoare` specification: under unit per-operation charge, every relational execution +of `allocTwoAndFreeFirst` from `(false, false)` returns distinct slots `i ≠ j`, leaves `i` free +and `j` occupied, and incurs abstract cost `3`. -/ +theorem allocTwoAndFreeFirst_costHoare : + TwoSlotAlloc.CostHoare (fun _ _ => 1) + (fun s => s = (false, false)) + allocTwoAndFreeFirst + (fun p s' => p.1 ≠ p.2 ∧ (if p.1 = 0 then s'.1 = false else s'.2 = false)) + (fun _ => 3) := by + refine ⟨fun s hs => ?_, fun s hs v s' tr hrun => ?_⟩ + · subst hs + refine ⟨allocTwoAndFreeFirst_demonicValid, fun v s' tr hrun => ?_⟩ + obtain ⟨_, i, s₁, tr₁, h₁, hrun₁, _⟩ := (RelSpec.clientRun_liftBind_iff ..).mp hrun + obtain ⟨_, j, s₂, tr₂, h₂, hrun₂, _⟩ := (RelSpec.clientRun_liftBind_iff ..).mp hrun₁ + obtain ⟨_, _, s₃, tr₃, h₃, hrun₃, _⟩ := (RelSpec.clientRun_liftBind_iff ..).mp hrun₂ + obtain ⟨rfl, rfl, rfl⟩ := (RelSpec.clientRun_pure_iff ..).mp hrun₃ + rcases h₁ with ⟨rfl, _, rfl⟩ | ⟨rfl, _, rfl⟩ + · rcases h₂ with ⟨_, hfalse, _⟩ | ⟨rfl, _, rfl⟩ + · cases hfalse + · subst h₃ + exact ⟨by decide, by decide⟩ + · rcases h₂ with ⟨rfl, _, rfl⟩ | ⟨_, hfalse, _⟩ + · subst h₃ + exact ⟨by decide, by decide⟩ + · cases hfalse + · subst hs + obtain ⟨_, i, s₁, tr₁, _, hrun₁, rfl⟩ := (RelSpec.clientRun_liftBind_iff ..).mp hrun + obtain ⟨_, j, s₂, tr₂, _, hrun₂, rfl⟩ := (RelSpec.clientRun_liftBind_iff ..).mp hrun₁ + obtain ⟨_, _, s₃, tr₃, _, hrun₃, rfl⟩ := (RelSpec.clientRun_liftBind_iff ..).mp hrun₂ + obtain ⟨rfl, rfl, rfl⟩ := (RelSpec.clientRun_pure_iff ..).mp hrun₃ + simp + +/-- Consequence to plain demonic `Hoare`. -/ +theorem allocTwoAndFreeFirst_hoare : + TwoSlotAlloc.Hoare + (fun s => s = (false, false)) + allocTwoAndFreeFirst + (fun p s' => p.1 ≠ p.2 ∧ (if p.1 = 0 then s'.1 = false else s'.2 = false)) := + allocTwoAndFreeFirst_costHoare.hoare + +/-! ## 3. Concrete first-fit backend realization and amortized certificate -/ + +/-- Concrete first-fit implementation of `TwoSlotAlloc`: `alloc` chooses slot `0` when free and +slot `1` otherwise, recording `1` unit of execution trace cost per operation. -/ +def firstFitExec : Execution TwoSlotAlloc (Bool × Bool) ℕ + | c, .alloc, i, c', n => + n = 1 ∧ + if c.1 = false then i = 0 ∧ c' = (true, c.2) + else i = 1 ∧ c' = (c.1, true) + | c, .free i, (), c', n => + n = 1 ∧ c' = (if i = 0 then (false, c.2) else (c.1, false)) + +/-- Angelic realization of `TwoSlotAlloc` by `firstFitExec`. -/ +def firstFitRealization : RelRealization TwoSlotAlloc firstFitExec where + rep s c := s = c + total {s c} o hr _ := by + subst hr + cases o with + | alloc => + rcases Bool.dichotomy s.1 with h0 | h0 + · exact ⟨0, (true, s.2), 1, rfl, by simp [h0]⟩ + · exact ⟨1, (s.1, true), 1, rfl, by simp [h0]⟩ + | free i => + exact ⟨(), if i = 0 then (false, s.2) else (s.1, false), 1, rfl, rfl⟩ + correct {s c o a c' n} hr hv he := by + subst hr + cases o with + | alloc => + obtain ⟨_, hbranch⟩ := he + rcases Bool.dichotomy s.1 with h0 | h0 + · simp only [h0, ↓reduceIte] at hbranch + obtain ⟨rfl, rfl⟩ := hbranch + exact ⟨(true, s.2), Or.inl ⟨rfl, h0, rfl⟩, rfl⟩ + · simp only [h0, Bool.true_eq_false, ↓reduceIte] at hbranch + obtain ⟨rfl, rfl⟩ := hbranch + have h1 : s.2 = false := hv.resolve_left (by simp [h0]) + exact ⟨(s.1, true), Or.inr ⟨rfl, h1, rfl⟩, by simp [h0]⟩ + | free i => + obtain ⟨_, rfl⟩ := he + cases a + exact ⟨if i = 0 then (false, s.2) else (s.1, false), rfl, rfl⟩ + +/-- Per-operation worst-case bound for `firstFitRealization`. -/ +theorem firstFitBound : + RelBound firstFitRealization id (fun _ _ => 1) := by + intro s c o a c' n _ _ he + cases o with + | alloc => exact Nat.le_of_eq he.1 + | free _ => exact Nat.le_of_eq he.1 + +/-- Amortized certificate for `firstFitRealization` with zero potential. -/ +theorem firstFitAmortized : + RelAmortized firstFitRealization id (fun _ _ => 0) (fun _ _ => 1) := + RelAmortized.of_relBound firstFitBound + +/-- End-to-end execution of `allocTwoAndFreeFirst` on `firstFitRealization` via +`RelAmortized.run_client_amortized` and `RelSpec.CostHoare.realize_amortized`. -/ +theorem firstFit_allocTwoAndFreeFirst_executes : + ∃ v c' traces s' tr, + Executes firstFitExec allocTwoAndFreeFirst (false, false) v c' traces ∧ + TwoSlotAlloc.ClientRun (false, false) allocTwoAndFreeFirst v s' tr ∧ + firstFitRealization.rep s' c' ∧ + (v.1 ≠ v.2 ∧ (if v.1 = 0 then s'.1 = false else s'.2 = false)) ∧ + total id traces ≤ 3 := by + obtain ⟨v, c', traces, _, _, he, _, _, _, _⟩ := + firstFitAmortized.run_client_amortized allocTwoAndFreeFirst + (s := (false, false)) (c := (false, false)) rfl allocTwoAndFreeFirst_demonicValid + obtain ⟨s', tr, hrun, hr, hpost, hcost⟩ := + allocTwoAndFreeFirst_costHoare.realize_amortized firstFitAmortized rfl rfl he + exact ⟨v, c', traces, s', tr, he, hrun, hr, hpost, by omega⟩ + +/-! ## 4. Layer 1 (`Spec.toRelSpec`) embedding and collapse equivalences -/ + +/-- Simple deterministic counter specification to exercise `Spec.toRelSpec`. -/ +abbrev CounterSpec : Spec where + State := ℕ + Op := Unit + Result _ := ℕ + step n _ := (n + 1, n) + +/-- Adaptive client on `CounterSpec` that increments twice and returns the sum of results. -/ +def counterTwice : CounterSpec.Client ℕ := + .liftBind () fun a => .liftBind () fun b => .pure (a + b) + +/-- `DemonicValid` on `CounterSpec.toRelSpec` collapses to `ValidHistory` on `CounterSpec`. -/ +theorem counterTwice_demonicValid (n : ℕ) : + CounterSpec.toRelSpec.DemonicValid n counterTwice := by + rw [CounterSpec.toRelSpec_demonicValid_iff] + exact ⟨trivial, trivial, trivial⟩ + +/-- `RelSpec.Hoare` on `CounterSpec.toRelSpec` is equivalent to `Spec.Hoare`. -/ +theorem counterTwice_relHoare : + CounterSpec.toRelSpec.Hoare (fun n => n = 5) counterTwice (fun v s' => v = 11 ∧ s' = 7) := by + rw [CounterSpec.toRelSpec_hoare_iff] + intro s hs + subst hs + exact ⟨⟨trivial, trivial, trivial⟩, rfl, rfl⟩ + +end ComplexityTests.StatefulRelational diff --git a/ComplexityTests/WordRAMDynamic.lean b/ComplexityTests/WordRAMDynamic.lean index 3e98d9e66..d27a344b5 100644 --- a/ComplexityTests/WordRAMDynamic.lean +++ b/ComplexityTests/WordRAMDynamic.lean @@ -147,9 +147,11 @@ example : (fun (_, answer, _) => answer) = some none ∧ ((Stack.implementation 4 2).eval 4 0 .size (Stack.initial 3)).map (fun (_, answer, _) => answer) = some 0 ∧ - ({ exchangeProgram 4 with decode := fun _ _ => none }).eval 4 0 7 + ({ exchangeProgram 4 with decode := fun _ _ => none } : + DynamicProgram (exchangeSpec 4) 3 .standard).eval 4 0 7 ⟨fun _ => 0, fun _ => 0⟩ = none ∧ - ({ exchangeProgram 4 with code := .loop (.eq 0 0) (.op (.copy 0 0)) }).eval 4 2 7 + ({ exchangeProgram 4 with code := .loop (.eq 0 0) (.op (.copy 0 0)) } : + DynamicProgram (exchangeSpec 4) 3 .standard).eval 4 2 7 ⟨fun _ => 0, fun _ => 0⟩ = none := by decide diff --git a/ComplexityTests/WordRAMRelSubroutine.lean b/ComplexityTests/WordRAMRelSubroutine.lean new file mode 100644 index 000000000..b8aeee2fd --- /dev/null +++ b/ComplexityTests/WordRAMRelSubroutine.lean @@ -0,0 +1,351 @@ +/- +Copyright (c) 2026 Renfei Zhou. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Renfei Zhou +-/ + +module +-- upstream: local-only (test driver) + +public import Complexity.Init +public import Complexity.Computability.WordRAM.RelSubroutine + +/-! +# Tests for Relational Word-RAM Subroutine Realizations + +Concrete non-vacuous tests for `Cslib.WordRAM.RelSubroutineDS`, `DynamicProgram`, +`RelDynamicCertificate`, `SubroutineDS.toRelSubroutineDS`, `RelSubroutineDS.toStandard`, +`RelSubroutineDS.call_rename`, `RelSubroutineDS.run_client_amortized`, +`RelSubroutineDS.run_ops_exact_fresh`, and `RelSubroutineDS.run_ops_exact_reusable` on a +genuinely nondeterministic two-slot allocator specification. +-/ + +@[expose] public section + +namespace ComplexityTests.WordRAMRelSubroutine + +open Cslib Cslib.Stateful Cslib.WordRAM + +/-! ## 1. Nondeterministic two-slot allocator specification -/ + +/-- Relational specification of a two-slot allocator: from state `(b₀, b₁) : Bool × Bool`, an +allocation operation `()` nondeterministically returns any free slot `i : Fin 2` and marks slot +`i` occupied. When both slots are free (`(false, false)`), both `0` and `1` are permitted. -/ +abbrev TwoSlotAlloc : RelSpec where + State := Bool × Bool + Op := Unit + Result _ := Fin 2 + valid s _ := s.1 = false ∨ s.2 = false + step s _ s' i := + (i = 0 ∧ s.1 = false ∧ s' = (true, s.2)) ∨ + (i = 1 ∧ s.2 = false ∧ s' = (s.1, true)) + +instance : TwoSlotAlloc.Total where + step_total s _ hv := by + rcases hv with h0 | h1 + · exact ⟨(true, s.2), 0, Or.inl ⟨rfl, h0, rfl⟩⟩ + · exact ⟨(s.1, true), 1, Or.inr ⟨rfl, h1, rfl⟩⟩ + +/-! ## 2. Concrete `SubroutineCode .exact` implementation and `RelSubroutineDS` certificate -/ + +/-- Six-register `.exact` Word-RAM subroutine implementing `TwoSlotAlloc` by sequential +allocation of slot `0` followed by slot `1`: +* register `0`: `baseReg` (persistent) +* register `1`: `frontierReg` (persistent) +* register `2`: `curReg` (scratch) +* register `3`: `zeroReg` (scratch) +* register `4`: `oneReg` / `output` (scratch) +* register `5`: persistent next-free-slot counter (`0`, `1`, or `2`). -/ +abbrev twoSlotCode : SubroutineCode .exact 6 0 where + allocSpan := .op (.copy 0 1) + initSetup := .op (.const 5 0) + code := .seq (.op (.copy 4 5)) (.seq (.op (.const 3 1)) (.op (.exactBin .add 5 5 3))) + frontierReg := 1 + baseReg := 0 + initParams := Fin.elim0 + curReg := 2 + zeroReg := 3 + oneReg := 4 + incCur := .exactBin .add 2 2 4 + input i := i == 2 || i == 3 || i == 4 + output := 4 + +/-- Representation invariant for `twoSlotCode` at word width `w` and base address `base`. -/ +abbrev twoSlotRep (w base : ℕ) (s : Bool × Bool) (x : Data 6) : Prop := + x.Bounded w ∧ x.regs 0 = base ∧ + ((s = (false, false) ∧ x.regs 5 = 0) ∨ + (s = (true, false) ∧ x.regs 5 = 1) ∨ + (s = (true, true) ∧ x.regs 5 = 2)) + +/-- `RelSubroutineDS` certificate showing that `twoSlotCode` realizes the nondeterministic +`TwoSlotAlloc` specification whenever `Fits w 2`. -/ +abbrev twoSlotSubroutine (w base : ℕ) (h2 : Fits w 2) : + RelSubroutineDS twoSlotCode TwoSlotAlloc w base Fin.elim0 (false, false) where + space := 0 + spanTime := 1 + setupTime := 1 + initPotential := 0 + charge _ _ := 3 + rep := twoSlotRep w base + potential _ := 0 + encode _ _ := 0 + argMatch _ _ := True + decode _ regs := some ⟨regs 4 % 2, Nat.mod_lt _ (by decide)⟩ + bounded hr := hr.1 + arguments_fit _ _ _ _ := fits_zero w + encode_matches _ _ := trivial + rep_supported {s x x'} hregs _ hx' hr := by + refine ⟨hx', ?_, ?_⟩ + · rw [hregs 0 rfl]; exact hr.2.1 + · rw [hregs 5 rfl]; exact hr.2.2 + potential_supported _ _ _ := rfl + clear_nodup := by decide + params_not_clear j := j.elim0 + incCur_eval {x} hone hfit := by + simp only [Op.eval_exactBin, BinOp.evalExact, hone, Option.bind_some, Data.put_of_fits hfit] + span_exec {x} _ hfr _ := by + refine ⟨x.set 0 (x.regs 1), ⟨1, Nat.le_refl 1, Exec.copy⟩, rfl, ?_, ?_, fun j => j.elim0⟩ + · simp [Data.set, hfr] + · simp [Data.set, hfr] + setup_exec {x₁} hx₁ hb₁ hfr₁ _ _ := by + refine ⟨x₁.set 5 0, ⟨1, Nat.le_refl 1, Exec.const (fits_zero w)⟩, ?_, ?_, Nat.le_refl 0, + fun _ _ => rfl⟩ + · simp [Data.set, hfr₁] + · refine ⟨hx₁.set (fits_zero w), ?_, Or.inl ⟨rfl, by simp [Data.set]⟩⟩ + simp [Data.set, hb₁] + step_exec {s x} _ hr hv _ := by + have h1 : Fits w 1 := h2.of_le (by decide) + rcases hr with ⟨hx, hb, ⟨rfl, h5⟩ | ⟨rfl, h5⟩ | ⟨rfl, _⟩⟩ + · let x₁ := x.set 4 (x.regs 5) + let x₂ := x₁.set 3 1 + let y := x₂.set 5 (x₂.regs 5 + x₂.regs 3) + have hfit : Fits w (x₂.regs 5 + x₂.regs 3) := by + simpa [x₂, x₁, Data.set, h5] using h1 + have heval : (Op.exactBin .add (5 : Fin 6) 5 3).eval w x₂ = some y := by + simp only [Op.eval_exactBin, BinOp.evalExact, Option.bind_some, Data.put_of_fits hfit, y] + have hex : Exec w twoSlotCode.code x y 3 := + Exec.copy.seq ((Exec.const h1).seq (Exec.op heval)) + have hy_b : y.Bounded w := hex.bounded hx + refine ⟨y, (true, false), 0, 3, hex, Or.inl ⟨rfl, rfl, rfl⟩, ?_, ?_, + fun _ _ => rfl, Nat.le_refl 3⟩ + · simp [y, x₂, x₁, Data.set, h5] + · refine ⟨hy_b, ?_, Or.inr (Or.inl ⟨rfl, ?_⟩)⟩ + · simp [y, x₂, x₁, Data.set, hb] + · simp [y, x₂, x₁, Data.set, h5] + · let x₁ := x.set 4 (x.regs 5) + let x₂ := x₁.set 3 1 + let y := x₂.set 5 (x₂.regs 5 + x₂.regs 3) + have hfit : Fits w (x₂.regs 5 + x₂.regs 3) := by + simpa [x₂, x₁, Data.set, h5] using h2 + have heval : (Op.exactBin .add (5 : Fin 6) 5 3).eval w x₂ = some y := by + simp only [Op.eval_exactBin, BinOp.evalExact, Option.bind_some, Data.put_of_fits hfit, y] + have hex : Exec w twoSlotCode.code x y 3 := + Exec.copy.seq ((Exec.const h1).seq (Exec.op heval)) + have hy_b : y.Bounded w := hex.bounded hx + refine ⟨y, (true, true), 1, 3, hex, Or.inr ⟨rfl, rfl, rfl⟩, ?_, ?_, + fun _ _ => rfl, Nat.le_refl 3⟩ + · simp [y, x₂, x₁, Data.set, h5] + · refine ⟨hy_b, ?_, Or.inr (Or.inr ⟨rfl, ?_⟩)⟩ + · simp [y, x₂, x₁, Data.set, hb] + · simp [y, x₂, x₁, Data.set, h5] + · cases hv with + | inl h => cases h + | inr h => cases h + +/-! ## 3. Exercising `call_rename`, `run_client_amortized`, and flat-program execution -/ + +/-- Adaptive client allocating two slots sequentially and returning both indices. -/ +abbrev allocPair : TwoSlotAlloc.Client (Fin 2 × Fin 2) := + .liftBind () fun i => + .liftBind () fun j => + .pure (i, j) + +/-- `allocPair` is demonically admissible from `(false, false)` across all permitted outcomes. -/ +theorem allocPair_demonicValid : + TwoSlotAlloc.DemonicValid (false, false) allocPair := by + refine RelSpec.DemonicValid.liftBind (D := TwoSlotAlloc) () _ (Or.inl rfl) fun s₁ i h₁ => ?_ + have hv₂ : TwoSlotAlloc.valid s₁ () := by + rcases h₁ with ⟨rfl, _, rfl⟩ | ⟨rfl, _, rfl⟩ + · exact Or.inr rfl + · exact Or.inl rfl + exact RelSpec.DemonicValid.liftBind (D := TwoSlotAlloc) () _ hv₂ + fun s₂ j _ => .pure s₂ (i, j) + +/-- Standalone adaptive-client amortized theorem via `RelSubroutineDS.toStandard` and +`RelSubroutineDS.run_client_amortized`. -/ +theorem twoSlot_run_client_amortized (w base : ℕ) (h2 : Fits w 2) + {x : Data 6} (hr : (twoSlotSubroutine w base h2).rep (false, false) x) : + ∃ a y traces s' tr, + Stateful.Executes + ((twoSlotSubroutine w base h2).toStandard.toDynamicProgram.ExecOp w) + allocPair x a y traces ∧ + TwoSlotAlloc.ClientRun (false, false) allocPair a s' tr ∧ + (twoSlotSubroutine w base h2).rep s' y ∧ + traces.length = tr.length ∧ + Stateful.total DynamicProgram.OperationTrace.instructions traces ≤ + TwoSlotAlloc.totalCost (fun _ _ => 4) (false, false) tr := + (twoSlotSubroutine w base h2).toStandard.run_client_bound (fun _ => rfl) + allocPair hr allocPair_demonicValid + +/-- Canonical injective register embedding `Fin 6 → Fin 8`. -/ +def embed6to8 : Fin 6 → Fin 8 := Fin.castLE (by decide) + +theorem embed6to8_injective : Function.Injective embed6to8 := + (Fin.castLEOrderEmb (by decide : 6 ≤ 8)).injective + +/-- Relocated inline call via `RelSubroutineDS.call_rename` inside an 8-register caller. -/ +theorem twoSlot_call_rename (w base : ℕ) (h2 : Fits w 2) + {x : Data 8} (hx : (x.comap embed6to8).Bounded w) + (hr : (twoSlotSubroutine w base h2).rep (false, false) (x.comap embed6to8)) : + ∃ z s' a t, + ExecWithin w (.seq (.op (.const 2 0)) (twoSlotCode.code.rename embed6to8)) x z t ∧ + TwoSlotAlloc.step (false, false) () s' a ∧ + (twoSlotSubroutine w base h2).decode () + (z.comap embed6to8).regs = some a ∧ + (twoSlotSubroutine w base h2).rep s' (z.comap embed6to8) ∧ + Modifies (Block.mk base 0).footprint (x.set 2 0).mem z.mem ∧ + (∀ j ∉ Set.range embed6to8, z.regs j = (x.set 2 0).regs j) ∧ + t ≤ 4 := by + have hprep : ExecWithin (B := .exact) w (.op (.const (2 : Fin 8) 0)) x (x.set 2 0) 1 := + ⟨1, Nat.le_refl 1, Exec.const (fits_zero w)⟩ + have hb' : ((x.set 2 0).comap embed6to8).Bounded w := by + refine ⟨fun i => ?_, hx.2⟩ + by_cases hi : embed6to8 i = 2 + · simp [Data.comap, Data.set, hi, fits_zero w] + · simp only [Fin.isValue, Data.comap_regs, Data.set_regs, Function.update_of_ne hi] + exact hx.1 i + have hreg : ∀ i : Fin 6, twoSlotCode.input i = false → + (x.set 2 0).regs (embed6to8 i) = x.regs (embed6to8 i) := by + intro i hi + have hne : embed6to8 i ≠ 2 := by + fin_cases i <;> revert hi <;> decide + simp [Data.set, Function.update_of_ne hne] + obtain ⟨z, s', a, t, he, hstep, hdec, hrep, hmod, hframe, ht⟩ := + (twoSlotSubroutine w base h2).call_rename + embed6to8_injective () hr (Or.inl rfl) hprep hb' hreg (fun _ _ => rfl) trivial + exact ⟨z, s', a, t, he, hstep, hdec, hrep, hmod, hframe, by omega⟩ + +/-- Two-operation sequence `[(), ()]` is demonically valid from `(false, false)`. -/ +theorem twoSlot_validOpSeq : + TwoSlotAlloc.ValidOpSeq (false, false) [(), ()] := by + refine ⟨Or.inl rfl, fun s₁ a₁ h₁ => ?_⟩ + rcases h₁ with ⟨rfl, _, rfl⟩ | ⟨rfl, _, rfl⟩ + · exact ⟨Or.inr rfl, fun _ _ _ => trivial⟩ + · exact ⟨Or.inl rfl, fun _ _ _ => trivial⟩ + +/-- Flat-program execution on fresh zeroed memory via `RelSubroutineDS.run_ops_exact_fresh`. -/ +theorem twoSlot_run_ops_fresh (w base : ℕ) (h2 : Fits w 2) + {d₀ : Data 6} (hd₀ : d₀.Bounded w) (hfr : d₀.regs 1 = base) + (hz : ∀ a ∈ (Block.mk base 0).footprint, d₀.mem a = 0) : + ∃ pc₀ x₀ t_init tr s_fin z t_ops, + Runs w twoSlotCode.initFreshProg ⟨0, d₀⟩ ⟨pc₀, x₀⟩ t_init ∧ + (∃ out, step w twoSlotCode.initFreshProg ⟨pc₀, x₀⟩ = .halt out) ∧ + Modifies (Block.mk base 0).footprint d₀.mem x₀.mem ∧ + (twoSlotSubroutine w base h2).RunsRelOps + (twoSlotSubroutine w base h2).prepareRegs (false, false) x₀ [(), ()] tr s_fin z + t_ops ∧ + tr.ops = [(), ()] ∧ + TwoSlotAlloc.ValidTranscript (false, false) tr s_fin ∧ + (twoSlotSubroutine w base h2).rep s_fin z ∧ + t_init ≤ 2 ∧ + t_ops ≤ TwoSlotAlloc.totalCost (fun _ _ => 4) (false, false) tr := by + obtain ⟨pc₀, x₀, t_init, tr, s_fin, z, t_ops, hruns, hhalt, hmod, hops_run, hops, htr, hrep, + ht_init, ht_ops⟩ := + (twoSlotSubroutine w base h2).run_ops_exact_fresh + hd₀ hfr (fun j => j.elim0) hz [(), ()] twoSlot_validOpSeq + exact ⟨pc₀, x₀, t_init, tr, s_fin, z, t_ops, hruns, hhalt, hmod, hops_run, hops, htr, hrep, + ht_init, by simpa using ht_ops⟩ + +/-- Flat-program execution on reusable dirty memory via `RelSubroutineDS.run_ops_exact_reusable`. -/ +theorem twoSlot_run_ops_reusable (w base : ℕ) (h2 : Fits w 2) + {d₀ : Data 6} (hd₀ : d₀.Bounded w) (hfr : d₀.regs 1 = base) : + ∃ pc₀ x₀ t_init tr s_fin z t_ops, + Runs w twoSlotCode.initClearProg ⟨0, d₀⟩ ⟨pc₀, x₀⟩ t_init ∧ + (∃ out, step w twoSlotCode.initClearProg ⟨pc₀, x₀⟩ = .halt out) ∧ + Modifies (Block.mk base 0).footprint d₀.mem x₀.mem ∧ + (twoSlotSubroutine w base h2).RunsRelOps + (twoSlotSubroutine w base h2).prepareRegs (false, false) x₀ [(), ()] tr s_fin z + t_ops ∧ + tr.ops = [(), ()] ∧ + TwoSlotAlloc.ValidTranscript (false, false) tr s_fin ∧ + (twoSlotSubroutine w base h2).rep s_fin z ∧ + t_init ≤ 8 ∧ + t_ops ≤ TwoSlotAlloc.totalCost (fun _ _ => 4) (false, false) tr := by + obtain ⟨pc₀, x₀, t_init, tr, s_fin, z, t_ops, hruns, hhalt, hmod, hops_run, hops, htr, hrep, + ht_init, ht_ops⟩ := + (twoSlotSubroutine w base h2).run_ops_exact_reusable + hd₀ hfr (fun j => j.elim0) [(), ()] twoSlot_validOpSeq + exact ⟨pc₀, x₀, t_init, tr, s_fin, z, t_ops, hruns, hhalt, hmod, hops_run, hops, htr, hrep, + ht_init, by simpa using ht_ops⟩ + +/-! ## 4. Lifting a deterministic `SubroutineDS` via `SubroutineDS.toRelSubroutineDS` -/ + +/-- Deterministic specification of a bounded two-step counter (`0 → 1 → 2`). -/ +abbrev DetCounter2 : Spec where + State := ℕ + Op := Unit + Result _ := Fin 2 + valid n _ := n < 2 + step n _ := (n + 1, ⟨n % 2, Nat.mod_lt _ (by decide)⟩) + +/-- Deterministic `SubroutineDS` certificate for `twoSlotCode` over `DetCounter2`. -/ +abbrev detCounter2Subroutine (w base : ℕ) (h2 : Fits w 2) : + SubroutineDS twoSlotCode DetCounter2 w base Fin.elim0 0 where + space := 0 + spanTime := 1 + setupTime := 1 + initPotential := 0 + charge _ _ := 3 + rep n x := x.Bounded w ∧ x.regs 0 = base ∧ x.regs 5 = n ∧ n ≤ 2 + potential _ := 0 + encode _ _ := 0 + argMatch _ _ := True + decode _ regs := some ⟨regs 4 % 2, Nat.mod_lt _ (by decide)⟩ + bounded hr := hr.1 + rep_unique hr₁ hr₂ := hr₁.2.2.1.symm.trans hr₂.2.2.1 + arguments_fit _ _ _ _ := fits_zero w + encode_matches _ _ := trivial + rep_supported {s x x'} hregs _ hx' hr := by + refine ⟨hx', ?_, ?_, hr.2.2.2⟩ + · rw [hregs 0 rfl]; exact hr.2.1 + · rw [hregs 5 rfl]; exact hr.2.2.1 + potential_supported _ _ _ := rfl + clear_nodup := by decide + params_not_clear j := j.elim0 + incCur_eval {x} hone hfit := by + simp only [Op.eval_exactBin, BinOp.evalExact, hone, Option.bind_some, Data.put_of_fits hfit] + span_exec {x} _ hfr _ := by + refine ⟨x.set 0 (x.regs 1), ⟨1, Nat.le_refl 1, Exec.copy⟩, rfl, ?_, ?_, fun j => j.elim0⟩ + · simp [Data.set, hfr] + · simp [Data.set, hfr] + setup_exec {x₁} hx₁ hb₁ hfr₁ _ _ := by + refine ⟨x₁.set 5 0, ⟨1, Nat.le_refl 1, Exec.const (fits_zero w)⟩, ?_, ?_, Nat.le_refl 0, + fun _ _ => rfl⟩ + · simp [Data.set, hfr₁] + · refine ⟨hx₁.set (fits_zero w), ?_, ?_, Nat.zero_le 2⟩ + · simp [Data.set, hb₁] + · simp [Data.set] + step_exec {s x} _ hr hv _ := by + have h1 : Fits w 1 := h2.of_le (by decide) + obtain ⟨hx, hb, h5, _⟩ := hr + let x₁ := x.set 4 (x.regs 5) + let x₂ := x₁.set 3 1 + let y := x₂.set 5 (x₂.regs 5 + x₂.regs 3) + have hfit : Fits w (x₂.regs 5 + x₂.regs 3) := by + have hfit2 : Fits w (s + 1) := h2.of_le hv + simpa [x₂, x₁, Data.set, h5] using hfit2 + have heval : (Op.exactBin .add (5 : Fin 6) 5 3).eval w x₂ = some y := by + simp only [Op.eval_exactBin, BinOp.evalExact, Option.bind_some, Data.put_of_fits hfit, y] + have hex : Exec w twoSlotCode.code x y 3 := + Exec.copy.seq ((Exec.const h1).seq (Exec.op heval)) + refine ⟨y, 3, hex, ?_, ⟨hex.bounded hx, ?_, ?_, hv⟩, fun _ _ => rfl, Nat.le_refl 3⟩ + · simp [y, x₂, x₁, Data.set, h5] + · simp [y, x₂, x₁, Data.set, hb] + · simp [y, x₂, x₁, Data.set, h5] + +/-- Lifting `detCounter2Subroutine` to `RelSubroutineDS` via `SubroutineDS.toRelSubroutineDS`. -/ +def detCounter2RelSubroutine (w base : ℕ) (h2 : Fits w 2) : + RelSubroutineDS twoSlotCode DetCounter2.toRelSpec w base Fin.elim0 0 := + (detCounter2Subroutine w base h2).toRelSubroutineDS + +end ComplexityTests.WordRAMRelSubroutine