From 5909d1bae7648600e86ab49730835ce9d336ca95 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 17:07:14 -0500 Subject: [PATCH 01/27] feat(DataStructure/Memory): word-addressed memory oracle and named field arrays Memory.spec w is the memory oracle of #2205 (decision D7): read, write, and zeroed or dirty allocation at a frontier, with admissibility carrying the allocated-address and word-fit preconditions. Field arrays are WordRAM Blocks accessed by Block.get/Block.set programs over the oracle. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 2 + Complexity/DataStructure/Memory/Field.lean | 183 +++++++++++++++ Complexity/DataStructure/Memory/Spec.lean | 258 +++++++++++++++++++++ ComplexityTests/DataStructureMemory.lean | 117 ++++++++++ 4 files changed, 560 insertions(+) create mode 100644 Complexity/DataStructure/Memory/Field.lean create mode 100644 Complexity/DataStructure/Memory/Spec.lean create mode 100644 ComplexityTests/DataStructureMemory.lean diff --git a/Complexity.lean b/Complexity.lean index 7f9a77746d..00f4bf2faf 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2873,6 +2873,8 @@ public import Complexity.DataStructure.HashFamily.WordRAM.DoubleTabulation public import Complexity.DataStructure.HashFamily.WordRAM.KWise public import Complexity.DataStructure.HashFamily.WordRAM.MultiplyShift public import Complexity.DataStructure.HashFamily.WordRAM.Tabulation +public import Complexity.DataStructure.Memory.Field +public import Complexity.DataStructure.Memory.Spec public import Complexity.DataStructure.ObliviousSequence public import Complexity.DataStructure.Online.Basic public import Complexity.DataStructure.OrderedSet.Basic diff --git a/Complexity/DataStructure/Memory/Field.lean b/Complexity/DataStructure/Memory/Field.lean new file mode 100644 index 0000000000..f065798d88 --- /dev/null +++ b/Complexity/DataStructure/Memory/Field.lean @@ -0,0 +1,183 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.ListArray +public import Complexity.DataStructure.Memory.Spec + +/-! +# Named field arrays over the memory oracle + +A field array is a `Cslib.WordRAM.Block`: a base address and a length. A data structure lays out +its storage as a structure whose fields are blocks (a struct of arrays) and accesses entry `i` of +a field `f` with the programs `f.get i` and `f.set i v` over `Memory.spec w`. Addresses are thus +named (`par.get v`) rather than computed by hand (`base + n + v`), while computed indices, pointer +chasing (`do let p ← next.get v; key.get p`) and reuse of a block across operations remain +ordinary programs. + +The lemmas here evaluate these programs, discharge their admissibility from `Block.Allocated`, +and describe their frame: a write to a field leaves every other address unchanged. +`Block.entries` reads the first `n` entries of a field as a list, which is how an abstraction +function usually sees an array. It realizes `Block.HoldsList`. + +## Main definitions + +* `Cslib.WordRAM.Block.get`, `Cslib.WordRAM.Block.set`: entry access through the oracle. +* `Cslib.WordRAM.Block.Allocated`: the block lies below the frontier. +* `Cslib.WordRAM.Block.entries`: the first `n` entries of a field in index order. +* `Cslib.DataStructure.Memory.allocBlock`, `Cslib.DataStructure.Memory.allocBlockDirty`: + allocate a field. + +## Main results + +* `Cslib.WordRAM.Block.runClient_get_bind`, `Cslib.WordRAM.Block.runClient_set_bind`: one-step + evaluation of field accesses. +* `Cslib.WordRAM.Block.Allocated.lt_frontier`, `Cslib.WordRAM.Block.Allocated.mono`: allocated + entries are admissible addresses, and stay allocated. +* `Cslib.WordRAM.Block.entries_succ`, `Cslib.WordRAM.Block.entries_congr`, + `Cslib.WordRAM.Block.entries_update_of_le`, `Cslib.WordRAM.Block.entries_update_of_not_owns`: + the list view under appends and writes. +* `Cslib.WordRAM.Block.holdsList_entries`: the list view realizes `Block.HoldsList`. +* `Cslib.DataStructure.Memory.runClient_allocBlock_bind`: allocation hands out a zeroed, + allocated block at the frontier. +-/ + +@[expose] public section + +namespace Cslib.WordRAM.Block + +open Cslib.DataStructure + +/-- Read entry `i` of the field `b` through the memory oracle. -/ +abbrev get (b : Block) (i : ℕ) : Memory.Prog ℕ := Memory.read (b.base + i) + +/-- Write `v` to entry `i` of the field `b` through the memory oracle. -/ +abbrev set (b : Block) (i v : ℕ) : Memory.Prog Unit := Memory.write (b.base + i) v + +/-- The block lies below the frontier, so all of its entries are allocated. -/ +def Allocated (b : Block) (s : Memory.State) : Prop := b.base + b.length ≤ s.frontier + +/-- An entry of an allocated block is an allocated address. -/ +theorem Allocated.lt_frontier {b : Block} {s : Memory.State} (h : b.Allocated s) {i : ℕ} + (hi : i < b.length) : b.base + i < s.frontier := by + unfold Allocated at h + omega + +/-- A block stays allocated as the frontier grows. -/ +theorem Allocated.mono {b : Block} {s s' : Memory.State} (h : b.Allocated s) + (hs : s.frontier ≤ s'.frontier) : b.Allocated s' := + le_trans h hs + +/-- A block owns the address of each of its entries. -/ +theorem owns_add {b : Block} {i : ℕ} (hi : i < b.length) : b.Owns (b.base + i) := + ⟨Nat.le_add_right _ _, Nat.add_lt_add_left hi _⟩ + +/-- Reading an entry returns the cell and leaves memory unchanged. -/ +@[simp] theorem runClient_get_bind {w : ℕ} {α : Type} (b : Block) (i : ℕ) (k : ℕ → Memory.Prog α) + (s : Memory.State) : + (Memory.spec w).runClient s (b.get i >>= k) = + let (v, s', os) := (Memory.spec w).runClient s (k (s.mem (b.base + i))) + (v, s', .read (b.base + i) :: os) := rfl + +/-- Writing an entry updates its cell. -/ +@[simp] theorem runClient_set_bind {w : ℕ} {α : Type} (b : Block) (i v : ℕ) + (k : Unit → Memory.Prog α) (s : Memory.State) : + (Memory.spec w).runClient s (b.set i v >>= k) = + let (r, s', os) := (Memory.spec w).runClient + ⟨Function.update s.mem (b.base + i) v, s.frontier⟩ (k ()) + (r, s', .write (b.base + i) v :: os) := rfl + +/-! ## The list of entries -/ + +/-- The first `n` entries of the field `b` in memory `m`, in index order. -/ +def entries (b : Block) (n : ℕ) (m : ℕ → ℕ) : List ℕ := + (List.range n).map fun i => m (b.base + i) + +@[simp] theorem length_entries (b : Block) (n : ℕ) (m : ℕ → ℕ) : (b.entries n m).length = n := by + simp [entries] + +@[simp] theorem entries_zero (b : Block) (m : ℕ → ℕ) : b.entries 0 m = [] := rfl + +/-- Entry `i` of the list view is the cell at `b.base + i`. -/ +@[simp] theorem getElem_entries (b : Block) (n : ℕ) (m : ℕ → ℕ) (i : ℕ) + (hi : i < (b.entries n m).length) : (b.entries n m)[i] = m (b.base + i) := by + simp [entries] + +/-- One more entry appends its cell. -/ +theorem entries_succ (b : Block) (n : ℕ) (m : ℕ → ℕ) : + b.entries (n + 1) m = b.entries n m ++ [m (b.base + n)] := by + simp [entries, List.range_succ] + +/-- The list view depends only on the cells of its entries. -/ +theorem entries_congr (b : Block) (n : ℕ) {m m' : ℕ → ℕ} + (h : ∀ i < n, m' (b.base + i) = m (b.base + i)) : b.entries n m' = b.entries n m := + List.map_congr_left fun i hi => h i (List.mem_range.mp hi) + +/-- Writing at or beyond entry `n` leaves the first `n` entries unchanged. -/ +theorem entries_update_of_le (b : Block) {n i : ℕ} (hi : n ≤ i) (m : ℕ → ℕ) (v : ℕ) : + b.entries n (Function.update m (b.base + i) v) = b.entries n m := + b.entries_congr n fun j hj => Function.update_of_ne (by omega) v m + +/-- Writing outside the block leaves every entry unchanged. -/ +theorem entries_update_of_not_owns (b : Block) {n : ℕ} (hn : n ≤ b.length) {a : ℕ} + (ha : ¬b.Owns a) (m : ℕ → ℕ) (v : ℕ) : + b.entries n (Function.update m a v) = b.entries n m := + b.entries_congr n fun j hj => + Function.update_of_ne (fun h => ha (by rw [← h]; exact owns_add (by omega))) v m + +/-- The list view of `n` entries realizes the list at the block's base. -/ +theorem holdsList_entries (b : Block) (n : ℕ) (m : ℕ → ℕ) : + HoldsList b.base (b.entries n m) m := fun i => by + simp [Block.read] + +end Cslib.WordRAM.Block + +namespace Cslib.DataStructure.Memory + +open Cslib.WordRAM + +/-- Allocate a zeroed field of `k` cells. -/ +def allocBlock (k : ℕ) : Prog Block := do + let base ← alloc k + pure ⟨base, k⟩ + +/-- Allocate a field of `k` cells without clearing it. -/ +def allocBlockDirty (k : ℕ) : Prog Block := do + let base ← allocDirty k + pure ⟨base, k⟩ + +/-- A zeroed allocation hands out the block at the frontier, clears it, and advances the +frontier past it. -/ +@[simp] theorem runClient_allocBlock_bind {w : ℕ} {α : Type} (k : ℕ) (next : Block → Prog α) + (s : State) : + (spec w).runClient s (allocBlock k >>= next) = + let (v, s', os) := (spec w).runClient + ⟨clear s.mem s.frontier k, s.frontier + k⟩ (next ⟨s.frontier, k⟩) + (v, s', .alloc k :: os) := rfl + +/-- A dirty allocation hands out the block at the frontier without changing memory. -/ +@[simp] theorem runClient_allocBlockDirty_bind {w : ℕ} {α : Type} (k : ℕ) + (next : Block → Prog α) (s : State) : + (spec w).runClient s (allocBlockDirty k >>= next) = + let (v, s', os) := (spec w).runClient ⟨s.mem, s.frontier + k⟩ (next ⟨s.frontier, k⟩) + (v, s', .allocDirty k :: os) := rfl + +/-- A freshly allocated block is allocated in the new state. -/ +theorem allocated_alloc (s : State) (k : ℕ) : + (Block.mk s.frontier k).Allocated ⟨clear s.mem s.frontier k, s.frontier + k⟩ := + le_rfl + +/-- A freshly allocated zeroed block holds zeros. -/ +theorem entries_alloc (s : State) (k : ℕ) {n : ℕ} (hn : n ≤ k) : + (Block.mk s.frontier k).entries n (clear s.mem s.frontier k) = List.replicate n 0 := by + refine List.ext_getElem (by simp) fun i hi _ => ?_ + simp only [Block.length_entries] at hi + simp only [Block.getElem_entries, List.getElem_replicate] + exact clear_of_owns (Block.owns_add (b := ⟨s.frontier, k⟩) (by simp only; omega)) + +end Cslib.DataStructure.Memory diff --git a/Complexity/DataStructure/Memory/Spec.lean b/Complexity/DataStructure/Memory/Spec.lean new file mode 100644 index 0000000000..3507072aee --- /dev/null +++ b/Complexity/DataStructure/Memory/Spec.lean @@ -0,0 +1,258 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Heap +public import Complexity.DataStructure.StateTransition + +/-! +# Word-addressed memory as a state-transition specification + +`Memory.spec w` is a memory oracle: the interface through which a data-structure implementation +reaches storage. Its state is the contents of every cell together with an allocation frontier. +Every address below the frontier is allocated, and allocation hands out the block that starts at +the frontier. The operations read a cell, write a cell, and allocate a block, either zeroed +(`alloc`, charged per cell) or with its old contents (`allocDirty`, charged one unit). + +Admissibility carries the preconditions a word RAM needs. Accessed addresses must be allocated, +written values must fit a `w`-bit word, and the frontier after an allocation must fit. Under +these preconditions the frontier and every cell keep fitting a word (`WellFormed.step`), so an +allocated address fits too (`WellFormed.fits_of_lt_frontier`). + +An implementation over this interface is an ordinary `Memory.Prog` program. Its correctness and +its operation charges are proved against `step` and `charge`, without registers or instruction +counts. `charge` is the memory-level currency; executing such a program on the word RAM is a +separate step that exchanges it into instructions. Allocation never reuses addresses, as in +`Cslib.WordRAM.alloc`, so blocks allocated at different times are disjoint +(`Cslib.WordRAM.Block.disjoint_of_le`). A program reuses memory by keeping a block it allocated. + +## Main definitions + +* `Memory.State`, `Memory.Op`, `Memory.step`, `Memory.valid`: cells and frontier, operations + with their answers, the transition function and the admissibility preconditions. +* `Memory.spec`: the deterministic specification at word size `w`. +* `Memory.charge`: one unit per read, write and dirty allocation; `k + 1` for a zeroed block of + `k` cells. +* `Memory.Prog`, `Memory.read`, `Memory.write`, `Memory.alloc`, `Memory.allocDirty`: programs + over the oracle and its four one-call programs. +* `Memory.WellFormed`: the frontier and all cell contents fit a `w`-bit word. +* `Memory.State.init`: the word RAM's input convention as a memory state. + +## Main results + +* `Memory.WellFormed.step`, `Memory.WellFormed.run`: admissible operations preserve + well-formedness. +* `Memory.WellFormed.fits_of_lt_frontier`: allocated addresses fit a word. +* `Memory.frontier_le_step`, `Memory.frontier_le_run`: the frontier never decreases. +* `Memory.mem_step_of_lt_frontier`: an operation changes an allocated cell only by writing it. +* `Memory.wellFormed_init`: the input convention is well formed. +-/ + +@[expose] public section + +namespace Cslib.DataStructure.Memory + +open Cslib.WordRAM + +/-- Memory: the contents of every cell, and the frontier below which cells are allocated. -/ +@[ext] +structure State where + /-- The contents of the cell at each address. -/ + mem : ℕ → ℕ + /-- Every address below the frontier is allocated; the next block starts here. -/ + frontier : ℕ + +/-- Operations on memory. Addresses are words computed by the caller. -/ +inductive Op where + /-- Read the cell at an address. -/ + | read (addr : ℕ) + /-- Write a value to the cell at an address. -/ + | write (addr value : ℕ) + /-- Allocate `count` zeroed cells at the frontier and return their base address. -/ + | alloc (count : ℕ) + /-- Allocate `count` cells at the frontier without clearing them and return their base + address. -/ + | allocDirty (count : ℕ) + deriving DecidableEq + +/-- The answer type of each operation: a read and an allocation return a word. -/ +abbrev Op.Result : Op → Type + | .read _ => ℕ + | .write _ _ => Unit + | .alloc _ => ℕ + | .allocDirty _ => ℕ + +/-- The transition of each operation. An allocation returns the old frontier. -/ +def step (s : State) : (o : Op) → State × o.Result + | .read a => (s, s.mem a) + | .write a v => (⟨Function.update s.mem a v, s.frontier⟩, ()) + | .alloc k => (⟨clear s.mem s.frontier k, s.frontier + k⟩, s.frontier) + | .allocDirty k => (⟨s.mem, s.frontier + k⟩, s.frontier) + +/-- Admissibility at word size `w`: accessed addresses are allocated, written values fit a word, +and an allocation leaves a fitting frontier. -/ +def valid (w : ℕ) (s : State) : Op → Prop + | .read a => a < s.frontier + | .write a v => a < s.frontier ∧ Fits w v + | .alloc k => Fits w (s.frontier + k) + | .allocDirty k => Fits w (s.frontier + k) + +instance (w : ℕ) (s : State) (o : Op) : Decidable (valid w s o) := by + cases o <;> unfold valid <;> infer_instance + +/-- Word-addressed memory at word size `w`. -/ +abbrev spec (w : ℕ) : StateTransitionSpec := + StateTransitionSpec.ofFn (State := State) (Op := Op) (Result := Op.Result) step (valid w) + +/-- The memory-level currency: one unit per read, write and dirty allocation, and `k + 1` units +for a zeroed block of `k` cells. -/ +def charge (_ : State) : Op → ℕ + | .alloc k => k + 1 + | _ => 1 + +/-- Programs over the memory oracle. They are the clients of `Memory.spec w` at every word size. -/ +abbrev Prog (α : Type) : Type := PFunctor.FreeM ⟨Op, Op.Result⟩ α + +/-- Read the cell at address `a`. -/ +abbrev read (a : ℕ) : Prog ℕ := PFunctor.FreeM.lift (P := ⟨Op, Op.Result⟩) (.read a) + +/-- Write `v` to the cell at address `a`. -/ +abbrev write (a v : ℕ) : Prog Unit := PFunctor.FreeM.lift (P := ⟨Op, Op.Result⟩) (.write a v) + +/-- Allocate `k` zeroed cells and return their base address. -/ +abbrev alloc (k : ℕ) : Prog ℕ := PFunctor.FreeM.lift (P := ⟨Op, Op.Result⟩) (.alloc k) + +/-- Allocate `k` cells without clearing them and return their base address. -/ +abbrev allocDirty (k : ℕ) : Prog ℕ := + PFunctor.FreeM.lift (P := ⟨Op, Op.Result⟩) (.allocDirty k) + +@[simp] theorem step_read (s : State) (a : ℕ) : step s (.read a) = (s, s.mem a) := rfl + +@[simp] theorem step_write (s : State) (a v : ℕ) : + step s (.write a v) = (⟨Function.update s.mem a v, s.frontier⟩, ()) := rfl + +@[simp] theorem step_alloc (s : State) (k : ℕ) : + step s (.alloc k) = (⟨clear s.mem s.frontier k, s.frontier + k⟩, s.frontier) := rfl + +@[simp] theorem step_allocDirty (s : State) (k : ℕ) : + step s (.allocDirty k) = (⟨s.mem, s.frontier + k⟩, s.frontier) := rfl + +@[simp] theorem valid_read (w : ℕ) (s : State) (a : ℕ) : valid w s (.read a) ↔ a < s.frontier := + Iff.rfl + +@[simp] theorem valid_write (w : ℕ) (s : State) (a v : ℕ) : + valid w s (.write a v) ↔ a < s.frontier ∧ Fits w v := Iff.rfl + +@[simp] theorem valid_alloc (w : ℕ) (s : State) (k : ℕ) : + valid w s (.alloc k) ↔ Fits w (s.frontier + k) := Iff.rfl + +@[simp] theorem valid_allocDirty (w : ℕ) (s : State) (k : ℕ) : + valid w s (.allocDirty k) ↔ Fits w (s.frontier + k) := Iff.rfl + +@[simp] theorem charge_read (s : State) (a : ℕ) : charge s (.read a) = 1 := rfl + +@[simp] theorem charge_write (s : State) (a v : ℕ) : charge s (.write a v) = 1 := rfl + +@[simp] theorem charge_alloc (s : State) (k : ℕ) : charge s (.alloc k) = k + 1 := rfl + +@[simp] theorem charge_allocDirty (s : State) (k : ℕ) : charge s (.allocDirty k) = 1 := rfl + +/-- Admissibility of a concrete history is decidable. -/ +instance decidableValidHistory (w : ℕ) (s : State) (os : List Op) : + Decidable ((spec w).ValidHistory s os) := by + induction os generalizing s with + | nil => exact isTrue trivial + | cons o os ih => + exact decidable_of_iff (valid w s o ∧ (spec w).ValidHistory (step s o).1 os) Iff.rfl + +/-! ## Frontier and frame -/ + +/-- No operation lowers the frontier. -/ +theorem frontier_le_step (s : State) (o : Op) : s.frontier ≤ (step s o).1.frontier := by + cases o <;> simp + +/-- No sequence of operations lowers the frontier. -/ +theorem frontier_le_run {w : ℕ} (s : State) (os : List Op) : + s.frontier ≤ ((spec w).run s os).1.frontier := by + induction os generalizing s with + | nil => exact le_rfl + | cons o os ih => exact (frontier_le_step s o).trans (ih _) + +/-- An operation changes an allocated cell only by writing to it. Allocation clears cells at or +above the frontier only. -/ +theorem mem_step_of_lt_frontier (s : State) (o : Op) {a : ℕ} (ha : a < s.frontier) + (hw : ∀ v, o ≠ .write a v) : (step s o).1.mem a = s.mem a := by + cases o with + | read => rfl + | write b v => + have hab : a ≠ b := fun h => hw v (h ▸ rfl) + exact Function.update_of_ne hab v s.mem + | alloc k => exact clear_of_not_owns (by simp only [Block.Owns]; omega) + | allocDirty => rfl + +/-! ## Word fit -/ + +/-- The frontier and every cell fit a `w`-bit word. Admissible operations preserve this. -/ +structure WellFormed (w : ℕ) (s : State) : Prop where + /-- The frontier fits a word, hence so does every allocated address. -/ + frontier_fits : Fits w s.frontier + /-- Every cell holds a word. -/ + mem_fits : ∀ a, Fits w (s.mem a) + +/-- Allocated addresses fit a word. -/ +theorem WellFormed.fits_of_lt_frontier {w : ℕ} {s : State} (h : WellFormed w s) {a : ℕ} + (ha : a < s.frontier) : Fits w a := + h.frontier_fits.of_le ha.le + +/-- Admissible operations preserve well-formedness. -/ +theorem WellFormed.step {w : ℕ} {s : State} (h : WellFormed w s) {o : Op} (hv : valid w s o) : + WellFormed w (Memory.step s o).1 := by + cases o with + | read => exact h + | write a v => + refine ⟨h.frontier_fits, fun b => ?_⟩ + by_cases hb : b = a + · subst hb + simpa using hv.2 + · simpa [Function.update_of_ne hb] using h.mem_fits b + | alloc k => + refine ⟨hv, fun b => ?_⟩ + simp only [step_alloc, clear] + split + · exact fits_zero w + · exact h.mem_fits b + | allocDirty k => exact ⟨hv, h.mem_fits⟩ + +/-- Admissible histories preserve well-formedness. -/ +theorem WellFormed.run {w : ℕ} {s : State} (h : WellFormed w s) {os : List Op} + (hv : (spec w).ValidHistory s os) : WellFormed w ((spec w).run s os).1 := by + induction os generalizing s with + | nil => exact h + | cons o os ih => exact ih (h.step hv.1) hv.2 + +/-! ## The input convention -/ + +/-- The word RAM's input convention (`Cslib.WordRAM.Data.init`) as a memory state: the input +length at address `0`, the input words at addresses `1, …, n`, and everything up to the input +allocated. -/ +def State.init (input : List ℕ) : State := + ⟨(Data.init (r := 0) input).mem, input.length + 1⟩ + +/-- The input convention is well formed when the input and its frontier fit a word. -/ +theorem wellFormed_init {w : ℕ} {input : List ℕ} (hin : InputFits w input) + (hf : Fits w (input.length + 1)) : WellFormed w (State.init input) := by + refine ⟨hf, fun a => ?_⟩ + change Fits w (if a = 0 then input.length else input.getD (a - 1) 0) + split + · exact hin.1 + · rw [List.getD_eq_getElem?_getD] + cases hi : input[a - 1]? with + | none => exact fits_zero w + | some v => exact hin.2 v (List.mem_of_getElem? hi) + +end Cslib.DataStructure.Memory diff --git a/ComplexityTests/DataStructureMemory.lean b/ComplexityTests/DataStructureMemory.lean new file mode 100644 index 0000000000..01791c6332 --- /dev/null +++ b/ComplexityTests/DataStructureMemory.lean @@ -0,0 +1,117 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: local-only (test driver) + +public import Complexity.DataStructure.Memory.Field + +/-! +# Memory-oracle programs: named fields, pointer chasing and block reuse + +A singly linked list is stored as a struct of arrays with a `key` field and a `next` field; +pointers are indices, with `0` as null. The tests allocate the layout, build a two-node list, +chase pointers to sum the keys, and reuse one scratch block across two rounds. They check the +answers, the operation histories and charges, admissibility at an 8-bit word size, and two +rejected operations: a read above the frontier and a write of an oversized value. +-/ + +@[expose] public section + +namespace ComplexityTests.DataStructureMemory + +open Cslib.WordRAM Cslib.DataStructure Cslib.DataStructure.Memory + +/-- A linked-list node store as a struct of arrays. -/ +structure Nodes where + /-- The key of each node. -/ + key : Block + /-- The successor of each node, as an index; `0` is null. -/ + next : Block + +/-- Allocate both fields for `n` nodes. -/ +def Nodes.alloc (n : ℕ) : Prog Nodes := do + let key ← allocBlock n + let next ← allocBlock n + pure ⟨key, next⟩ + +/-- Follow at most `fuel` successors from node `v`, summing keys. Every address is computed from +an answer: the next node is read from memory. -/ +def Nodes.sumFrom (L : Nodes) : ℕ → ℕ → Prog ℕ + | 0, _ => pure 0 + | fuel + 1, v => do + let k ← L.key.get v + let nxt ← L.next.get v + if nxt = 0 then pure k else do + let rest ← L.sumFrom fuel nxt + pure (k + rest) + +/-- Build the list `1 → 2` with keys `5` and `7` (node `0` is reserved as null), then sum it. -/ +def linkedSum : Prog ℕ := do + let L ← Nodes.alloc 3 + L.key.set 1 5 + L.key.set 2 7 + L.next.set 1 2 + L.sumFrom 3 1 + +/-- The empty memory with nothing allocated. -/ +def empty : State := ⟨fun _ => 0, 0⟩ + +example : (spec 8).clientResult empty linkedSum = 12 := by decide + +/-- The history: two zeroed allocations, three writes and four reads by pointer chasing. -/ +example : (spec 8).clientHistory empty linkedSum = + [.alloc 3, .alloc 3, .write 1 5, .write 2 7, .write 4 2, + .read 1, .read 4, .read 2, .read 5] := by decide + +/-- The two zeroed allocations of three cells cost four each; seven unit operations follow. -/ +example : (spec 8).totalCost charge empty ((spec 8).clientHistory empty linkedSum) = 15 := by + decide + +/-- Every operation is admissible at an 8-bit word size. -/ +example : (spec 8).ValidHistory empty ((spec 8).clientHistory empty linkedSum) := by decide + +/-- One scratch block, allocated without clearing, is reused across two rounds. -/ +def reuse : Prog (ℕ × ℕ) := do + let scratch ← allocBlockDirty 1 + scratch.set 0 3 + let first ← scratch.get 0 + scratch.set 0 (first + 4) + let second ← scratch.get 0 + pure (first, second) + +example : (spec 8).clientResult empty reuse = (3, 7) := by decide + +example : ((spec 8).clientState empty reuse).frontier = 1 := by decide + +example : (spec 8).totalCost charge empty ((spec 8).clientHistory empty reuse) = 5 := by decide + +/-- A read above the frontier is rejected. -/ +example : ¬(spec 8).ValidHistory empty [.read 0] := by decide + +/-- A write of a value wider than the word is rejected, even at an allocated address. -/ +example : ¬(spec 8).ValidHistory ⟨fun _ => 0, 1⟩ [.write 0 256] := by decide + +/-- An allocation whose end does not fit the word is rejected. -/ +example : ¬(spec 8).ValidHistory empty [.alloc 256] := by decide + +/-- Well-formedness survives the pointer-chasing program. -/ +example : WellFormed 8 ((spec 8).clientState empty linkedSum) := by + have h : WellFormed 8 empty := ⟨by decide, fun _ => fits_zero 8⟩ + rw [← (spec 8).run_runClient] + exact h.run (by decide) + +/-- The input convention: the length at address `0`, the words from address `1`, and the +frontier just past them. -/ +example : (spec 8).clientResult (State.init [4, 9]) (do + let n ← read 0 + let x ← read 2 + pure (n, x)) = (2, 9) := by decide + +/-- The list view reads the live prefix of a field. -/ +example : (Block.mk 1 3).entries 2 (fun a => a * 10) = [10, 20] := by decide + +end ComplexityTests.DataStructureMemory From 412f614dacfeba6565117ed6b5eb8f586b7d0888 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 17:16:21 -0500 Subject: [PATCH 02/27] =?UTF-8?q?doc(bib):=20add=20Gu=C3=A9neau,=20Chargu?= =?UTF-8?q?=C3=A9raud=20and=20Pottier=20(ESOP=202018)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- references.bib | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/references.bib b/references.bib index e33942d5a5..0b4758dbf6 100644 --- a/references.bib +++ b/references.bib @@ -2303,6 +2303,18 @@ @inproceedings{Grover1996 doi = {10.1145/237814.237866} } +@inproceedings{GueneauChargueraudPottier2018, + author = {Gu{\'e}neau, Arma{\"e}l and Chargu{\'e}raud, Arthur and Pottier, Fran{\c{c}}ois}, + title = {A Fistful of Dollars: Formalizing Asymptotic Complexity Claims via Deductive + Program Verification}, + booktitle = {Programming Languages and Systems (ESOP 2018)}, + series = {Lecture Notes in Computer Science}, + pages = {533--560}, + publisher = {Springer}, + year = {2018}, + doi = {10.1007/978-3-319-89884-1_19} +} + @inproceedings{GuoKoppartySudan2013, author = {Guo, Alan and Kopparty, Swastik and Sudan, Madhu}, title = {New affine-invariant codes from lifting}, From 16a647fee63724bc26032a05d4bbf7a682232e1f Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 17:16:21 -0500 Subject: [PATCH 03/27] feat(DataStructure/StateTransition): refinements by abstraction functions with stacked potentials Refinement D E is a Module whose representation is an invariant plus an abstraction function (#2205 D2). Refinement.Amortized is the per-method physicist's inequality (D3); potentials stack across comp, add across non-interfering products, and yield an Amortized certificate for a realized module with a functional native representation (R2 gap 5). CostIsBigO is the exported asymptotic form (D6). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 2 + .../StateTransition/Refinement.lean | 384 ++++++++++++++++++ .../Analysis/Asymptotics/CostBound.lean | 81 ++++ .../StateTransitionRefinement.lean | 143 +++++++ 4 files changed, 610 insertions(+) create mode 100644 Complexity/DataStructure/StateTransition/Refinement.lean create mode 100644 Complexity/Foundations/Analysis/Asymptotics/CostBound.lean create mode 100644 ComplexityTests/StateTransitionRefinement.lean diff --git a/Complexity.lean b/Complexity.lean index 7f9a77746d..9c6c1c30b9 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2921,6 +2921,7 @@ public import Complexity.DataStructure.StateTransition.CostHoare public import Complexity.DataStructure.StateTransition.Hoare public import Complexity.DataStructure.StateTransition.Impl public import Complexity.DataStructure.StateTransition.Module +public import Complexity.DataStructure.StateTransition.Refinement public import Complexity.DataStructure.StateTransition.Resource public import Complexity.DataStructure.VanEmdeBoas.Basic public import Complexity.DataStructure.VanEmdeBoas.Elements @@ -2944,6 +2945,7 @@ public import Complexity.DataStructure.WordRAM.StateTransition public import Complexity.Foundations.Algebra.FiniteMagnitude public import Complexity.Foundations.Algebra.GeometricTail public import Complexity.Foundations.Algebra.Shear +public import Complexity.Foundations.Analysis.Asymptotics.CostBound public import Complexity.Foundations.Analysis.Asymptotics.Exponential public import Complexity.Foundations.Analysis.BernoulliInequality public import Complexity.Foundations.Analysis.ENNRealTsum diff --git a/Complexity/DataStructure/StateTransition/Refinement.lean b/Complexity/DataStructure/StateTransition/Refinement.lean new file mode 100644 index 0000000000..a70028a61c --- /dev/null +++ b/Complexity/DataStructure/StateTransition/Refinement.lean @@ -0,0 +1,384 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.DataStructure.StateTransition.Amortized +public import Complexity.DataStructure.StateTransition.CostHoare + +/-! +# Refinements by abstraction functions and stacked potentials + +A `Refinement D E` is a `Module D E` whose representation is single-valued: an implementation +invariant on lower states together with an abstraction function `abs : E.State → D.State`. Each +method, run from a lower state satisfying the invariant, must realize the specified step at the +abstract state, ending in a state satisfying the invariant. On deterministic specifications +(`Refinement.ofFn`) the obligation reads `D.stepFn (abs e) o = (abs e', a)`. + +A single-valued representation is what makes amortized costs compose. `Refinement.Amortized` +asks that every method satisfy the physicist's inequality +`lower cost + potential e' ≤ upper (abs e) o + potential e`, with a potential on lower states. +Composing two refinements adds their potentials, the upper one read through the lower +abstraction (`Refinement.Amortized.comp`). A relational representation would leave the +intermediate state existential, and the composed potential would not be a function of the lower +state. For the same reason an amortized refinement over a native implementation whose +representation is functional yields an `Amortized` certificate of the realized module +(`Refinement.Amortized.realize`). + +Products of refinements over one lower interface (`Refinement.prod`) need non-interference: the +methods of each side leave the other side's abstraction, invariant and potential unchanged. + +## Main definitions + +* `Refinement`: a module given by methods, an implementation invariant and an abstraction + function. +* `Refinement.ofFn`: the constructor for deterministic specifications. +* `Refinement.toModule`: the underlying relational module. +* `Refinement.id`, `Refinement.comp`, `Refinement.prod`: identity, composition, and products under + non-interference. +* `Refinement.Amortized`: per-method amortized cost refinement with a potential on lower states. +* `productCharge`: componentwise charges of a product specification. + +## Main results + +* `Refinement.simulates_demonic`, `Refinement.simulates_client`: whole adaptive clients run + through the abstraction function. +* `Refinement.comp_rep_iff`: composition agrees with `Module.comp` of the underlying modules. +* `Refinement.Amortized.demonic`, `Refinement.Amortized.client`: potentials telescope along + adaptive clients. +* `Refinement.Amortized.comp`: potentials stack across composition. +* `Refinement.Amortized.prod`: potentials add across non-interfering products. +* `Refinement.Amortized.realize`: an amortized certificate for the realized module. +* `Refinement.Amortized.id`: the identity refinement passes charges through. +* `Refinement.Amortized.chargeBound`, `Refinement.Amortized.of_chargeBound`: with zero + potential, the existing worst-case `Module.ChargeBound`. + +## References + +* [Cormen, Leiserson, Rivest and Stein, *Introduction to Algorithms*, Chapter 17][CLRS2009]. +-/ + +@[expose] public section + +namespace Cslib.DataStructure + +universe u v w x + +variable {D E F D₁ D₂ : StateTransitionSpec.{u, v, w}} {α : Type w} + +/-- A module whose representation is an implementation invariant `inv` together with an +abstraction function `abs`. From a lower state satisfying `inv`, every run of a method on an +admissible operation realizes a permitted step of the abstract state and preserves `inv`. -/ +structure Refinement (D E : StateTransitionSpec.{u, v, w}) where + /-- Bodies implementing the upper interface using lower-interface calls. -/ + methods : Methods D E + /-- The implementation invariant on lower states. -/ + inv : E.State → Prop + /-- The abstraction function from lower states to upper states. -/ + abs : E.State → D.State + /-- Each method is demonically admissible from a state satisfying the invariant. -/ + valid : ∀ {e} o, inv e → D.valid (abs e) o → E.DemonicValid e (methods o) + /-- Each method run realizes the specified step through `abs` and preserves `inv`. -/ + correct : ∀ {e} o {a e' tr}, inv e → D.valid (abs e) o → + E.ClientRun e (methods o) a e' tr → D.step (abs e) o (abs e') a ∧ inv e' + +namespace Refinement + +/-- The constructor for deterministic specifications: each method's history is admissible, and +`D.stepFn (abs e) o = (abs e', a)` for its final state `e'` and result `a`. -/ +abbrev ofFn {fD : (s : D.State) → (o : D.Op) → D.State × D.Result o} + {fE : (e : E.State) → (o : E.Op) → E.State × E.Result o} + [D.HasStepFn fD] [E.HasStepFn fE] + (methods : Methods D E) (inv : E.State → Prop) (abs : E.State → D.State) + (valid : ∀ {e} o, inv e → D.valid (abs e) o → + E.ValidHistory e (E.clientHistory e (methods o))) + (correct : ∀ {e} o, inv e → D.valid (abs e) o → + D.stepFn (abs e) o = (abs (E.clientState e (methods o)), E.clientResult e (methods o)) ∧ + inv (E.clientState e (methods o))) : + Refinement D E where + methods := methods + inv := inv + abs := abs + valid o hi hv := (E.demonicValid_iff_validHistory_runClient _ _).mpr (valid o hi hv) + correct {e} o {a e' tr} hi hv hrun := by + obtain ⟨_, rfl, rfl, _⟩ := (E.clientRun_iff_runClient e (methods o) a e' tr).mp hrun + obtain ⟨hstep, hi'⟩ := correct o hi hv + exact ⟨(D.step_iff_stepFn _ _ _ _).mpr hstep.symm, hi'⟩ + +variable (M : Refinement D E) + +/-- The underlying relational module: `s` is represented by `e` when `e` satisfies the invariant +and abstracts to `s`. -/ +def toModule : Module D E where + methods := M.methods + rep s e := M.inv e ∧ M.abs e = s + valid {_ _} o hr hv := by + obtain ⟨hi, rfl⟩ := hr + exact M.valid o hi hv + correct {_ _} o {_ _ _} hr hv hrun := by + obtain ⟨hi, rfl⟩ := hr + obtain ⟨hstep, hi'⟩ := M.correct o hi hv hrun + exact ⟨_, hstep, hi', rfl⟩ + +/-- Every run of a linked admissible client corresponds to a run of the client at the abstract +state, ending at the abstraction of the final lower state. -/ +theorem simulates_demonic (p : D.Client α) {e : E.State} (he : M.inv e) + (hv : D.DemonicValid (M.abs e) p) : + E.DemonicValid e (link M.methods p) ∧ + ∀ a e' tr_E, E.ClientRun e (link M.methods p) a e' tr_E → + M.inv e' ∧ ∃ tr_D, D.ClientRun (M.abs e) p a (M.abs e') tr_D := by + obtain ⟨hvE, hrun⟩ := M.toModule.simulates_demonic p ⟨he, rfl⟩ hv + refine ⟨hvE, fun a e' tr hr => ?_⟩ + obtain ⟨_, tr_D, hD, hi', rfl⟩ := hrun a e' tr hr + exact ⟨hi', tr_D, hD⟩ + +/-- On deterministic specifications, a linked client returns the specified result, and its final +lower state satisfies the invariant and abstracts to the specified final state. -/ +theorem simulates_client {fD : (s : D.State) → (o : D.Op) → D.State × D.Result o} + {fE : (e : E.State) → (o : E.Op) → E.State × E.Result o} + [D.HasStepFn fD] [E.HasStepFn fE] (p : D.Client α) {e : E.State} (he : M.inv e) + (hv : D.ValidHistory (M.abs e) (D.clientHistory (M.abs e) p)) : + E.ValidHistory e (E.clientHistory e (link M.methods p)) ∧ + E.clientResult e (link M.methods p) = D.clientResult (M.abs e) p ∧ + M.abs (E.clientState e (link M.methods p)) = D.clientState (M.abs e) p ∧ + M.inv (E.clientState e (link M.methods p)) := by + obtain ⟨hvE, hres, hi, hs⟩ := M.toModule.simulates_client p ⟨he, rfl⟩ hv + exact ⟨hvE, hres, hs, hi⟩ + +/-- The identity refinement: each operation calls itself, with no invariant. -/ +def id (D : StateTransitionSpec.{u, v, w}) : Refinement D D where + methods o := .liftBind o fun a => .pure a + inv _ := True + abs e := e + valid o _ hv := .liftBind o _ hv fun s' a _ => .pure s' a + correct {_} o {_ _ _} _ _ hrun := by + cases hrun with + | liftBind _ _ a _ hstep hrest => + cases hrest + exact ⟨hstep, trivial⟩ + +/-- Composition: the upper methods are linked through the lower ones, the invariants conjoin, +and the abstraction functions compose. -/ +def comp (M : Refinement D E) (N : Refinement E F) : Refinement D F where + methods o := link N.methods (M.methods o) + inv f := N.inv f ∧ M.inv (N.abs f) + abs f := M.abs (N.abs f) + valid o hi hv := (N.simulates_demonic (M.methods o) hi.1 (M.valid o hi.2 hv)).1 + correct {_} o {a f' tr} hi hv hrun := by + obtain ⟨hN', _, hE⟩ := + (N.simulates_demonic (M.methods o) hi.1 (M.valid o hi.2 hv)).2 a f' tr hrun + obtain ⟨hstep, hM'⟩ := M.correct o hi.2 hv hE + exact ⟨hstep, hN', hM'⟩ + +/-- Composing refinements represents the same states as composing their modules. -/ +theorem comp_rep_iff (M : Refinement D E) (N : Refinement E F) (s : D.State) (f : F.State) : + (M.comp N).toModule.rep s f ↔ (M.toModule.comp N.toModule).rep s f := by + constructor + · rintro ⟨⟨hN, hM⟩, rfl⟩ + exact ⟨_, ⟨hM, rfl⟩, hN, rfl⟩ + · rintro ⟨_, ⟨hM, rfl⟩, hN, rfl⟩ + exact ⟨⟨hN, hM⟩, rfl⟩ + +/-- Linking through a composition links through each refinement in turn. -/ +theorem link_comp (M : Refinement D E) (N : Refinement E F) (p : D.Client α) : + link (M.comp N).methods p = link N.methods (link M.methods p) := + (DataStructure.link_comp M.methods N.methods p).symm + +/-- Two refinements over one lower interface implement the product of their specifications, +provided the methods of each leave the other's abstraction and invariant unchanged. -/ +def prod (M₁ : Refinement D₁ E) (M₂ : Refinement D₂ E) + (frame₁ : ∀ {e} o {a e' tr}, M₁.inv e → M₂.inv e → D₁.valid (M₁.abs e) o → + E.ClientRun e (M₁.methods o) a e' tr → M₂.abs e' = M₂.abs e ∧ M₂.inv e') + (frame₂ : ∀ {e} o {a e' tr}, M₁.inv e → M₂.inv e → D₂.valid (M₂.abs e) o → + E.ClientRun e (M₂.methods o) a e' tr → M₁.abs e' = M₁.abs e ∧ M₁.inv e') : + Refinement (product D₁ D₂) E where + methods + | .inl o => M₁.methods o + | .inr o => M₂.methods o + inv e := M₁.inv e ∧ M₂.inv e + abs e := (M₁.abs e, M₂.abs e) + valid {_} o hi hv := by + cases o with + | inl o => exact M₁.valid o hi.1 hv + | inr o => exact M₂.valid o hi.2 hv + correct {_} o {_ _ _} hi hv hrun := by + cases o with + | inl o => + obtain ⟨hstep, hi₁⟩ := M₁.correct o hi.1 hv hrun + obtain ⟨habs, hi₂⟩ := frame₁ o hi.1 hi.2 hv hrun + exact ⟨⟨hstep, habs⟩, hi₁, hi₂⟩ + | inr o => + obtain ⟨hstep, hi₂⟩ := M₂.correct o hi.2 hv hrun + obtain ⟨habs, hi₁⟩ := frame₂ o hi.1 hi.2 hv hrun + exact ⟨⟨habs, hstep⟩, hi₁, hi₂⟩ + +/-! ## Amortized costs -/ + +/-- Amortized cost refinement: the lower charges of every method run, plus the potential after it, +are at most the upper charge of the operation plus the potential before it. Potentials are +natural numbers, hence nonnegative; a client bound subtracts nothing when the initial potential +is zero. -/ +def Amortized (lower : E.State → E.Op → ℕ) (upper : D.State → D.Op → ℕ) + (potential : E.State → ℕ) : Prop := + ∀ {e} o {a e' tr}, M.inv e → D.valid (M.abs e) o → E.ClientRun e (M.methods o) a e' tr → + E.totalCostTranscript lower e tr + potential e' ≤ upper (M.abs e) o + potential e + +variable {M} {lower : E.State → E.Op → ℕ} {upper : D.State → D.Op → ℕ} + {potential : E.State → ℕ} + +/-- The amortized inequality on a deterministic lower specification, along each method's +history. -/ +theorem Amortized.ofFn {fE : (e : E.State) → (o : E.Op) → E.State × E.Result o} + [E.HasStepFn fE] + (h : ∀ {e} o, M.inv e → D.valid (M.abs e) o → + E.totalCost lower e (E.clientHistory e (M.methods o)) + + potential (E.clientState e (M.methods o)) ≤ + upper (M.abs e) o + potential e) : + M.Amortized lower upper potential := by + intro e o a e' tr hi hv hrun + obtain ⟨_, _, rfl, rfl⟩ := (E.clientRun_iff_runClient e (M.methods o) a e' tr).mp hrun + rw [E.totalCostTranscript_canonicalTranscript] + exact h o hi hv + +/-- Potentials telescope along any run of a linked admissible client. -/ +theorem Amortized.demonic (A : M.Amortized lower upper potential) (p : D.Client α) + {e e' : E.State} {a : α} {tr_E : E.Transcript} (he : M.inv e) + (hv : D.DemonicValid (M.abs e) p) (hrun : E.ClientRun e (link M.methods p) a e' tr_E) : + M.inv e' ∧ ∃ tr_D, D.ClientRun (M.abs e) p a (M.abs e') tr_D ∧ + E.totalCostTranscript lower e tr_E + potential e' ≤ + D.totalCostTranscript upper (M.abs e) tr_D + potential e := by + induction p generalizing e tr_E with + | pure b => + cases hrun + exact ⟨he, [], .pure _ _, by simp⟩ + | liftBind o k ih => + obtain ⟨hv_o, hv_k⟩ := (D.demonicValid_liftBind_iff _ o k).mp hv + have heq : link M.methods (.liftBind o k) = + (M.methods o).bind (fun r => link M.methods (k r)) := rfl + rw [heq] at hrun + obtain ⟨res, e₁, tr₁, tr₂, hrun₁, hrun₂, rfl⟩ := E.clientRun_bind_iff.mp hrun + obtain ⟨hstep₁, he₁⟩ := M.correct o he hv_o hrun₁ + have hc₁ := A o he hv_o hrun₁ + obtain ⟨he₂, tr_D, hrun_D, hc₂⟩ := ih res he₁ (hv_k _ res hstep₁) hrun₂ + refine ⟨he₂, ⟨o, res, M.abs e₁⟩ :: tr_D, .liftBind o k res hv_o hstep₁ hrun_D, ?_⟩ + rw [E.totalCostTranscript_append lower hrun₁.validTranscript tr₂] + simp only [StateTransitionSpec.totalCostTranscript_cons] + omega + +/-- On deterministic specifications, the lower charges of a linked client plus the final +potential are at most the upper charges of its history plus the initial potential. -/ +theorem Amortized.client {fD : (s : D.State) → (o : D.Op) → D.State × D.Result o} + {fE : (e : E.State) → (o : E.Op) → E.State × E.Result o} + [D.HasStepFn fD] [E.HasStepFn fE] (A : M.Amortized lower upper potential) + (p : D.Client α) {e : E.State} (he : M.inv e) + (hv : D.ValidHistory (M.abs e) (D.clientHistory (M.abs e) p)) : + E.totalCost lower e (E.clientHistory e (link M.methods p)) + + potential (E.clientState e (link M.methods p)) ≤ + D.totalCost upper (M.abs e) (D.clientHistory (M.abs e) p) + potential e := by + have hvD := (D.demonicValid_iff_validHistory_runClient _ p).mpr hv + have hvE := (M.simulates_client p he hv).1 + obtain ⟨_, tr_D, hrun_D, hcost⟩ := A.demonic p he hvD (E.clientRun_canonical e _ hvE) + obtain ⟨_, _, _, rfl⟩ := (D.clientRun_iff_runClient _ p _ _ tr_D).mp hrun_D + simpa only [StateTransitionSpec.totalCostTranscript_canonicalTranscript] using hcost + +/-- **Potentials stack across composition.** If the upper refinement is amortized against the +middle charges and the lower refinement is amortized from the middle charges, the composite is +amortized with the sum of the upper potential, read through the lower abstraction, and the +lower potential. -/ +theorem Amortized.comp {N : Refinement E F} {lower : F.State → F.Op → ℕ} + {middle : E.State → E.Op → ℕ} {Φ : E.State → ℕ} {Ψ : F.State → ℕ} + (A : M.Amortized middle upper Φ) (B : N.Amortized lower middle Ψ) : + (M.comp N).Amortized lower upper (fun f => Φ (N.abs f) + Ψ f) := by + intro f o a f' tr hi hv hrun + obtain ⟨_, tr_E, hrun_E, hN⟩ := B.demonic (M.methods o) hi.1 (M.valid o hi.2 hv) hrun + have hM := A o hi.2 hv hrun_E + change F.totalCostTranscript lower f tr + (Φ (N.abs f') + Ψ f') ≤ + upper (M.abs (N.abs f)) o + (Φ (N.abs f) + Ψ f) + omega + +/-- Componentwise charges of a product specification. -/ +def productCharge (c₁ : D₁.State → D₁.Op → ℕ) (c₂ : D₂.State → D₂.Op → ℕ) : + (product D₁ D₂).State → (product D₁ D₂).Op → ℕ + | (s₁, _), .inl o => c₁ s₁ o + | (_, s₂), .inr o => c₂ s₂ o + +/-- **Potentials add across non-interfering products**, provided the methods of each side leave +the other side's potential unchanged. -/ +theorem Amortized.prod {M₁ : Refinement D₁ E} {M₂ : Refinement D₂ E} + {frame₁ : ∀ {e} o {a e' tr}, M₁.inv e → M₂.inv e → D₁.valid (M₁.abs e) o → + E.ClientRun e (M₁.methods o) a e' tr → M₂.abs e' = M₂.abs e ∧ M₂.inv e'} + {frame₂ : ∀ {e} o {a e' tr}, M₁.inv e → M₂.inv e → D₂.valid (M₂.abs e) o → + E.ClientRun e (M₂.methods o) a e' tr → M₁.abs e' = M₁.abs e ∧ M₁.inv e'} + {upper₁ : D₁.State → D₁.Op → ℕ} {upper₂ : D₂.State → D₂.Op → ℕ} + {Φ₁ Φ₂ : E.State → ℕ} + (A₁ : M₁.Amortized lower upper₁ Φ₁) (A₂ : M₂.Amortized lower upper₂ Φ₂) + (potential₁ : ∀ {e} o {a e' tr}, M₁.inv e → M₂.inv e → D₁.valid (M₁.abs e) o → + E.ClientRun e (M₁.methods o) a e' tr → Φ₂ e' = Φ₂ e) + (potential₂ : ∀ {e} o {a e' tr}, M₁.inv e → M₂.inv e → D₂.valid (M₂.abs e) o → + E.ClientRun e (M₂.methods o) a e' tr → Φ₁ e' = Φ₁ e) : + (M₁.prod M₂ frame₁ frame₂).Amortized lower (productCharge upper₁ upper₂) + (fun e => Φ₁ e + Φ₂ e) := by + intro e o a e' tr hi hv hrun + cases o with + | inl o => + have h := A₁ o hi.1 hv hrun + have hΦ := potential₁ o hi.1 hi.2 hv hrun + change E.totalCostTranscript lower e tr + (Φ₁ e' + Φ₂ e') ≤ + upper₁ (M₁.abs e) o + (Φ₁ e + Φ₂ e) + omega + | inr o => + have h := A₂ o hi.2 hv hrun + have hΦ := potential₂ o hi.1 hi.2 hv hrun + change E.totalCostTranscript lower e tr + (Φ₁ e' + Φ₂ e') ≤ + upper₂ (M₂.abs e) o + (Φ₁ e + Φ₂ e) + omega + +/-- **Potentials stack through a realization.** An amortized refinement over a native +implementation with an amortized certificate yields an amortized certificate of the realized +module, provided the native representation is functional: the represented lower state is +`absR c`. -/ +theorem Amortized.realize (A : M.Amortized lower upper potential) + {C τ : Type x} {exec : Execution E C τ} {R : StateTransitionImpl E exec} + {weight : τ → ℕ} {ψ : E.State → C → ℕ} (B : DataStructure.Amortized R weight ψ lower) + (absR : C → E.State) (hR : ∀ {e c}, R.rep e c → e = absR c) : + DataStructure.Amortized (M.toModule.realize R) (total weight) + (fun _ c => potential (absR c) + ψ (absR c) c) upper where + step {_ c o a c' ts} hr hv he := by + obtain ⟨e, ⟨hi, rfl⟩, hR₀⟩ := hr + obtain ⟨e', tr, hrun, hR', _, hcost⟩ := B.demonic hR₀ (M.valid o hi hv) he + obtain ⟨hstep, hi'⟩ := M.correct o hi hv hrun + have hc := A o hi hv hrun + refine ⟨M.abs e', hstep, ⟨e', ⟨hi', rfl⟩, hR'⟩, ?_⟩ + rw [← hR hR₀, ← hR hR'] + omega + +/-- The identity refinement passes each charge through unchanged. -/ +theorem Amortized.id (charge : D.State → D.Op → ℕ) : + (Refinement.id D).Amortized charge charge fun _ => 0 := by + intro e o a e' tr _ _ hrun + cases hrun with + | liftBind _ _ r _ _ hrest => + cases hrest + simp [Refinement.id] + +/-- With zero potential, an amortized refinement is a worst-case `Module.ChargeBound` of its +module. -/ +theorem Amortized.chargeBound (A : M.Amortized lower upper fun _ => 0) : + M.toModule.ChargeBound lower upper := by + rintro s e o a e' tr ⟨hi, rfl⟩ hv hrun + simpa using A o hi hv hrun + +/-- A worst-case `Module.ChargeBound` of the module is an amortized refinement with zero +potential. -/ +theorem Amortized.of_chargeBound (B : M.toModule.ChargeBound lower upper) : + M.Amortized lower upper fun _ => 0 := by + intro e o a e' tr hi hv hrun + simpa using B o ⟨hi, rfl⟩ hv hrun + +end Refinement + +end Cslib.DataStructure diff --git a/Complexity/Foundations/Analysis/Asymptotics/CostBound.lean b/Complexity/Foundations/Analysis/Asymptotics/CostBound.lean new file mode 100644 index 0000000000..d17a2b7965 --- /dev/null +++ b/Complexity/Foundations/Analysis/Asymptotics/CostBound.lean @@ -0,0 +1,81 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.Analysis.Asymptotics.Ring + +/-! +# Asymptotic cost bounds + +`Cslib.CostIsBigO l g size cost` says that the cost of every input is bounded by one cost +function of its size, chosen before the input: some monotone, nonnegative `f` with `f =O[l] g` +satisfies `cost a ≤ f (size a)` for all `a`. This is the form of asymptotic cost specification +used by Guéneau, Charguéraud and Pottier [GueneauChargueraudPottier2018]. Because `f` is chosen +once, the statement is `∃ f, ∀ a`. Inductive reasoning with "`O(1) + O(1) = O(1)`" proves +`∀ a, ∃ c` instead, which is unsound as a cost claim. The filter makes multivariate bounds +explicit: for example, `atTop` on `ℕ × ℕ` for a bound in two size parameters. + +Exact constants are proved internally and exported in this form: `CostIsBigO.of_le_nat` turns an +exact natural-number bound into an asymptotic one. + +## Main definitions + +* `Cslib.CostIsBigO`: a cost bounded by a monotone, nonnegative function that is `O(g)` along a + filter. + +## Main results + +* `Cslib.CostIsBigO.of_le_nat`: export an exact monotone natural-number bound. +* `Cslib.CostIsBigO.mono`: a smaller cost has the same bound. +* `Cslib.CostIsBigO.trans_isBigO`: weaken the comparison function. + +## References + +* [A. Guéneau, A. Charguéraud and F. Pottier, *A Fistful of Dollars: Formalizing Asymptotic + Complexity Claims via Deductive Program Verification*][GueneauChargueraudPottier2018] +-/ + +@[expose] public section + +open Asymptotics + +namespace Cslib + +variable {α ι : Type*} [Preorder ι] + +/-- The cost of every input `a` is at most `f (size a)` for one monotone, nonnegative cost +function `f` with `f =O[l] g`. -/ +def CostIsBigO (l : Filter ι) (g : ι → ℝ) (size : α → ι) (cost : α → ℕ) : Prop := + ∃ f : ι → ℝ, Monotone f ∧ 0 ≤ f ∧ f =O[l] g ∧ ∀ a, (cost a : ℝ) ≤ f (size a) + +namespace CostIsBigO + +variable {l : Filter ι} {g : ι → ℝ} {size : α → ι} {cost : α → ℕ} + +/-- An exact monotone natural-number bound that is `O(g)` gives an asymptotic cost bound. -/ +theorem of_le_nat {F : ι → ℕ} (hF : Monotone F) (hO : (fun n => (F n : ℝ)) =O[l] g) + (h : ∀ a, cost a ≤ F (size a)) : CostIsBigO l g size cost := + ⟨fun n => F n, fun _ _ hab => Nat.cast_le.mpr (hF hab), fun _ => Nat.cast_nonneg _, hO, + fun a => Nat.cast_le.mpr (h a)⟩ + +/-- A cost below a bounded cost has the same bound. -/ +theorem mono {cost' : α → ℕ} (h : CostIsBigO l g size cost) (hle : ∀ a, cost' a ≤ cost a) : + CostIsBigO l g size cost' := by + obtain ⟨f, hf, hf0, hO, hb⟩ := h + exact ⟨f, hf, hf0, hO, fun a => (Nat.cast_le.mpr (hle a)).trans (hb a)⟩ + +/-- A cost that is `O(g)` is `O(g')` whenever `g =O[l] g'`. -/ +theorem trans_isBigO {g' : ι → ℝ} (h : CostIsBigO l g size cost) (hg : g =O[l] g') : + CostIsBigO l g' size cost := by + obtain ⟨f, hf, hf0, hO, hb⟩ := h + exact ⟨f, hf, hf0, hO.trans hg, hb⟩ + +end CostIsBigO + +end Cslib diff --git a/ComplexityTests/StateTransitionRefinement.lean b/ComplexityTests/StateTransitionRefinement.lean new file mode 100644 index 0000000000..7288b6efcc --- /dev/null +++ b/ComplexityTests/StateTransitionRefinement.lean @@ -0,0 +1,143 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: local-only (test driver) + +public import Complexity.DataStructure.Queue.Spec +public import Complexity.DataStructure.StateTransition.Refinement +public import Complexity.Foundations.Analysis.Asymptotics.CostBound + +/-! +# Amortized refinements with a nonzero potential + +A bag supports adding an element and clearing. It is implemented over the stack interface: +`add` pushes, and `clear` asks for the size and pops that many times. A clear can cost +arbitrarily many stack calls, but each element pays for its own pop when it is added, so with +the potential "one credit per stacked element" every bag operation costs at most two stack calls. +The tests check this per-operation certificate, telescope it along a concrete client, stack it +over the identity refinement, and export the linear bound as `CostIsBigO`. +-/ + +@[expose] public section + +namespace ComplexityTests.StateTransitionRefinement + +open Cslib Cslib.DataStructure Cslib.DataStructure.Queue + +/-- Bag operations. -/ +inductive BagOp where + | add (n : ℕ) + | clear + deriving DecidableEq + +/-- A bag with `clear`, recording its elements newest first. -/ +abbrev bag : StateTransitionSpec := + StateTransitionSpec.ofFn (State := List ℕ) (Op := BagOp) (Result := fun _ => Unit) + fun s o => match o with + | .add n => (n :: s, ()) + | .clear => ([], ()) + +/-- Pop `n` times. -/ +def popAll : ℕ → (stack ℕ).Client Unit + | 0 => pure () + | n + 1 => .liftBind .pop fun _ => popAll n + +/-- Bag operations as stack programs. -/ +def methods : Methods bag (stack ℕ) + | .add n => .liftBind (.push n) fun _ => .pure () + | .clear => .liftBind .size fun n => popAll n + +/-- Popping as many times as the stack is long empties it, one call per element. -/ +theorem runClient_popAll (s : List ℕ) : + (stack ℕ).runClient s (popAll s.length) = ((), [], List.replicate s.length .pop) := by + induction s with + | nil => rfl + | cons a s ih => + change (stack ℕ).runClient (a :: s) (.liftBind StackOp.pop fun _ => popAll s.length) = _ + rw [StateTransitionSpec.runClient_liftBind] + change (let (v, s', os) := (stack ℕ).runClient s (popAll s.length) + (v, s', StackOp.pop :: os)) = _ + rw [ih] + rfl + +/-- The stack holds the bag itself; there is no invariant. -/ +def refinement : Refinement bag (stack ℕ) := + Refinement.ofFn methods (fun _ => True) id + (fun _ _ _ => (stack ℕ).validHistory_of_forall_valid (fun _ _ => trivial) _ _) + (by + intro s o _ _ + cases o with + | add n => exact ⟨rfl, trivial⟩ + | clear => + refine ⟨?_, trivial⟩ + simp [methods, StateTransitionSpec.clientState, runClient_popAll]) + +/-- Two calls per `add`, one per `clear`. -/ +def bagCharge (_ : List ℕ) : BagOp → ℕ + | .add _ => 2 + | .clear => 1 + +/-- One credit per stacked element pays for its eventual pop. -/ +theorem amortized : refinement.Amortized (fun _ _ => 1) bagCharge List.length := by + apply Refinement.Amortized.ofFn + intro s o _ _ + cases o with + | add n => + simp [refinement, methods, bagCharge, StateTransitionSpec.clientState, + StateTransitionSpec.clientHistory] + omega + | clear => + simp [refinement, methods, bagCharge, StateTransitionSpec.clientState, + StateTransitionSpec.clientHistory, runClient_popAll, StateTransitionSpec.totalCost_const] + +/-- A client with one expensive clear. -/ +def client : bag.Client Unit := do + bag.call (.add 1) + bag.call (.add 2) + bag.call (.add 3) + bag.call .clear + bag.call (.add 4) + +/-- The clear costs four stack calls, yet the five bag operations stay within their nine +amortized units, with one credit left for the remaining element. -/ +example : (stack ℕ).totalCost (fun _ _ => 1) [] + ((stack ℕ).clientHistory [] (link refinement.methods client)) = 8 ∧ + bag.totalCost bagCharge [] (bag.clientHistory [] client) = 9 ∧ + ((stack ℕ).clientState [] (link refinement.methods client)).length = 1 := by + decide + +/-- The general telescoped bound from an empty stack. -/ +theorem client_bound (p : bag.Client Unit) : + (stack ℕ).totalCost (fun _ _ => 1) [] ((stack ℕ).clientHistory [] (link refinement.methods p)) + + ((stack ℕ).clientState [] (link refinement.methods p)).length ≤ + bag.totalCost bagCharge [] (bag.clientHistory [] p) := by + have h := amortized.client p (e := []) trivial + (bag.validHistory_of_forall_valid (fun _ _ => trivial) _ _) + exact h + +/-- Stacking over the identity refinement adds a zero potential. -/ +example : (refinement.comp (Refinement.id (stack ℕ))).Amortized (fun _ _ => 1) bagCharge + (fun s => s.length + 0) := + amortized.comp (Refinement.Amortized.id _) + +/-- The exported form: the stack calls of a bag client from an empty stack are `O(m)` in the +number `m` of bag operations, with the constant `2`. -/ +example : CostIsBigO Filter.atTop (fun m : ℕ => (m : ℝ)) + (fun p : bag.Client Unit => (bag.clientHistory [] p).length) + (fun p => (stack ℕ).totalCost (fun _ _ => 1) [] + ((stack ℕ).clientHistory [] (link refinement.methods p))) := by + refine CostIsBigO.of_le_nat (F := fun m => 2 * m) (fun _ _ h => Nat.mul_le_mul_left 2 h) + ?_ fun p => ?_ + · simpa using (Asymptotics.isBigO_refl (fun m : ℕ => (m : ℝ)) _).const_mul_left 2 + · have h := client_bound p + have hc : bag.totalCost bagCharge [] (bag.clientHistory [] p) ≤ + (bag.clientHistory [] p).length * 2 := + bag.totalCost_le_length_mul bagCharge 2 (fun _ o _ => by cases o <;> simp [bagCharge]) + (bag.validHistory_of_forall_valid (fun _ _ => trivial) _ _) + omega + +end ComplexityTests.StateTransitionRefinement From 2f12c49d3c05a6905a3484b2b0efa0591cda0021 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 17:23:20 -0500 Subject: [PATCH 04/27] feat(DataStructure/StateTransition): weaken and rescale amortized refinements Amortized.mono and Amortized.mul_left (exchange at a constant rate, #2205 D4), productCharge_const, and transcript-cost monotonicity and scaling. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../StateTransition/Refinement.lean | 60 ++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/Complexity/DataStructure/StateTransition/Refinement.lean b/Complexity/DataStructure/StateTransition/Refinement.lean index a70028a61c..ee80ab1581 100644 --- a/Complexity/DataStructure/StateTransition/Refinement.lean +++ b/Complexity/DataStructure/StateTransition/Refinement.lean @@ -43,6 +43,10 @@ methods of each side leave the other side's abstraction, invariant and potential * `Refinement.Amortized`: per-method amortized cost refinement with a potential on lower states. * `productCharge`: componentwise charges of a product specification. +The two transcript-cost lemmas `StateTransitionSpec.totalCostTranscript_mono` and +`StateTransitionSpec.totalCostTranscript_mul_left` sit here, beside their only clients, rather +than in the widely imported `StateTransition` module. + ## Main results * `Refinement.simulates_demonic`, `Refinement.simulates_client`: whole adaptive clients run @@ -54,6 +58,12 @@ methods of each side leave the other side's abstraction, invariant and potential * `Refinement.Amortized.prod`: potentials add across non-interfering products. * `Refinement.Amortized.realize`: an amortized certificate for the realized module. * `Refinement.Amortized.id`: the identity refinement passes charges through. +* `Refinement.Amortized.mono`, `Refinement.Amortized.mul_left`: weaken charges, and exchange + both currencies at a constant rate. +* `productCharge_const`: equal constant charges on a product. +* `StateTransitionSpec.totalCostTranscript_mono`, + `StateTransitionSpec.totalCostTranscript_mul_left`: monotonicity and scaling of transcript + costs. * `Refinement.Amortized.chargeBound`, `Refinement.Amortized.of_chargeBound`: with zero potential, the existing worst-case `Module.ChargeBound`. @@ -86,6 +96,25 @@ structure Refinement (D E : StateTransitionSpec.{u, v, w}) where correct : ∀ {e} o {a e' tr}, inv e → D.valid (abs e) o → E.ClientRun e (methods o) a e' tr → D.step (abs e) o (abs e') a ∧ inv e' +/-- Smaller charges total less along any transcript. -/ +theorem StateTransitionSpec.totalCostTranscript_mono {c c' : D.State → D.Op → ℕ} + (h : ∀ s o, c s o ≤ c' s o) (s : D.State) (tr : D.Transcript) : + D.totalCostTranscript c s tr ≤ D.totalCostTranscript c' s tr := by + induction tr generalizing s with + | nil => simp + | cons st tr ih => + simp only [StateTransitionSpec.totalCostTranscript_cons] + exact Nat.add_le_add (h _ _) (ih _) + +/-- Scaling every charge by `K` scales the total by `K`. -/ +theorem StateTransitionSpec.totalCostTranscript_mul_left (K : ℕ) (c : D.State → D.Op → ℕ) + (s : D.State) (tr : D.Transcript) : + D.totalCostTranscript (fun s o => K * c s o) s tr = K * D.totalCostTranscript c s tr := by + induction tr generalizing s with + | nil => simp + | cons st tr ih => + simp only [StateTransitionSpec.totalCostTranscript_cons, ih, Nat.mul_add] + namespace Refinement /-- The constructor for deterministic specifications: each method's history is admissible, and @@ -244,6 +273,27 @@ theorem Amortized.ofFn {fE : (e : E.State) → (o : E.Op) → E.State × E.Resul rw [E.totalCostTranscript_canonicalTranscript] exact h o hi hv +/-- Weaker charges: cheaper lower charges and dearer upper charges on admissible operations. -/ +theorem Amortized.mono (A : M.Amortized lower upper potential) + {lower' : E.State → E.Op → ℕ} {upper' : D.State → D.Op → ℕ} + (hlower : ∀ e o, lower' e o ≤ lower e o) + (hupper : ∀ s o, D.valid s o → upper s o ≤ upper' s o) : + M.Amortized lower' upper' potential := by + intro e o a e' tr hi hv hrun + have h := A o hi hv hrun + have hl := E.totalCostTranscript_mono hlower e tr + have hu := hupper _ o hv + omega + +/-- **Exchange at a constant rate.** Scaling both currencies and the potential by `K` preserves +the amortized inequality. -/ +theorem Amortized.mul_left (A : M.Amortized lower upper potential) (K : ℕ) : + M.Amortized (fun e o => K * lower e o) (fun s o => K * upper s o) + (fun e => K * potential e) := by + intro e o a e' tr hi hv hrun + rw [E.totalCostTranscript_mul_left, ← Nat.mul_add, ← Nat.mul_add] + exact Nat.mul_le_mul_left K (A o hi hv hrun) + /-- Potentials telescope along any run of a linked admissible client. -/ theorem Amortized.demonic (A : M.Amortized lower upper potential) (p : D.Client α) {e e' : E.State} {a : α} {tr_E : E.Transcript} (he : M.inv e) @@ -303,8 +353,14 @@ theorem Amortized.comp {N : Refinement E F} {lower : F.State → F.Op → ℕ} /-- Componentwise charges of a product specification. -/ def productCharge (c₁ : D₁.State → D₁.Op → ℕ) (c₂ : D₂.State → D₂.Op → ℕ) : (product D₁ D₂).State → (product D₁ D₂).Op → ℕ - | (s₁, _), .inl o => c₁ s₁ o - | (_, s₂), .inr o => c₂ s₂ o + | s, .inl o => c₁ s.1 o + | s, .inr o => c₂ s.2 o + +/-- Equal constant charges on both components are that constant charge on the product. -/ +@[simp] theorem productCharge_const (K : ℕ) : + productCharge (D₁ := D₁) (D₂ := D₂) (fun _ _ => K) (fun _ _ => K) = fun _ _ => K := by + funext s o + cases o <;> rfl /-- **Potentials add across non-interfering products**, provided the methods of each side leave the other side's potential unchanged. -/ From 98e3290dbc1bda9719bde60ccf0b57ecc992f5da Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 17:27:24 -0500 Subject: [PATCH 05/27] fix(DataStructure/StateTransition): place productCharge beside product, outside Refinement Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../StateTransition/Refinement.lean | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Complexity/DataStructure/StateTransition/Refinement.lean b/Complexity/DataStructure/StateTransition/Refinement.lean index ee80ab1581..08e10929f6 100644 --- a/Complexity/DataStructure/StateTransition/Refinement.lean +++ b/Complexity/DataStructure/StateTransition/Refinement.lean @@ -96,6 +96,18 @@ structure Refinement (D E : StateTransitionSpec.{u, v, w}) where correct : ∀ {e} o {a e' tr}, inv e → D.valid (abs e) o → E.ClientRun e (methods o) a e' tr → D.step (abs e) o (abs e') a ∧ inv e' +/-- Componentwise charges of a product specification. -/ +def productCharge (c₁ : D₁.State → D₁.Op → ℕ) (c₂ : D₂.State → D₂.Op → ℕ) : + (product D₁ D₂).State → (product D₁ D₂).Op → ℕ + | s, .inl o => c₁ s.1 o + | s, .inr o => c₂ s.2 o + +/-- Equal constant charges on both components are that constant charge on the product. -/ +@[simp] theorem productCharge_const (K : ℕ) : + productCharge (D₁ := D₁) (D₂ := D₂) (fun _ _ => K) (fun _ _ => K) = fun _ _ => K := by + funext s o + cases o <;> rfl + /-- Smaller charges total less along any transcript. -/ theorem StateTransitionSpec.totalCostTranscript_mono {c c' : D.State → D.Op → ℕ} (h : ∀ s o, c s o ≤ c' s o) (s : D.State) (tr : D.Transcript) : @@ -350,18 +362,6 @@ theorem Amortized.comp {N : Refinement E F} {lower : F.State → F.Op → ℕ} upper (M.abs (N.abs f)) o + (Φ (N.abs f) + Ψ f) omega -/-- Componentwise charges of a product specification. -/ -def productCharge (c₁ : D₁.State → D₁.Op → ℕ) (c₂ : D₂.State → D₂.Op → ℕ) : - (product D₁ D₂).State → (product D₁ D₂).Op → ℕ - | s, .inl o => c₁ s.1 o - | s, .inr o => c₂ s.2 o - -/-- Equal constant charges on both components are that constant charge on the product. -/ -@[simp] theorem productCharge_const (K : ℕ) : - productCharge (D₁ := D₁) (D₂ := D₂) (fun _ _ => K) (fun _ _ => K) = fun _ _ => K := by - funext s o - cases o <;> rfl - /-- **Potentials add across non-interfering products**, provided the methods of each side leave the other side's potential unchanged. -/ theorem Amortized.prod {M₁ : Refinement D₁ E} {M₂ : Refinement D₂ E} From 56232f9738389bd1f3c285dfddc6b4cd1048c766 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 17:28:34 -0500 Subject: [PATCH 06/27] refactor(DataStructure/StateTransition): make Refinement.Amortized a structure A Prop-valued def whose body starts with an implicit binder is unfolded and auto-introduced during elaboration against an expected type, which broke term-mode uses such as Amortized.prod. A one-field structure, like the existing DataStructure.Amortized, avoids this. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../StateTransition/Refinement.lean | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/Complexity/DataStructure/StateTransition/Refinement.lean b/Complexity/DataStructure/StateTransition/Refinement.lean index 08e10929f6..e6d94e5d59 100644 --- a/Complexity/DataStructure/StateTransition/Refinement.lean +++ b/Complexity/DataStructure/StateTransition/Refinement.lean @@ -263,10 +263,12 @@ def prod (M₁ : Refinement D₁ E) (M₂ : Refinement D₂ E) are at most the upper charge of the operation plus the potential before it. Potentials are natural numbers, hence nonnegative; a client bound subtracts nothing when the initial potential is zero. -/ -def Amortized (lower : E.State → E.Op → ℕ) (upper : D.State → D.Op → ℕ) - (potential : E.State → ℕ) : Prop := - ∀ {e} o {a e' tr}, M.inv e → D.valid (M.abs e) o → E.ClientRun e (M.methods o) a e' tr → - E.totalCostTranscript lower e tr + potential e' ≤ upper (M.abs e) o + potential e +structure Amortized (lower : E.State → E.Op → ℕ) (upper : D.State → D.Op → ℕ) + (potential : E.State → ℕ) : Prop where + /-- The physicist's inequality for one method run. -/ + step : ∀ {e} o {a e' tr}, M.inv e → D.valid (M.abs e) o → + E.ClientRun e (M.methods o) a e' tr → + E.totalCostTranscript lower e tr + potential e' ≤ upper (M.abs e) o + potential e variable {M} {lower : E.State → E.Op → ℕ} {upper : D.State → D.Op → ℕ} {potential : E.State → ℕ} @@ -280,7 +282,7 @@ theorem Amortized.ofFn {fE : (e : E.State) → (o : E.Op) → E.State × E.Resul potential (E.clientState e (M.methods o)) ≤ upper (M.abs e) o + potential e) : M.Amortized lower upper potential := by - intro e o a e' tr hi hv hrun + refine ⟨fun {e} o {a e' tr} hi hv hrun => ?_⟩ obtain ⟨_, _, rfl, rfl⟩ := (E.clientRun_iff_runClient e (M.methods o) a e' tr).mp hrun rw [E.totalCostTranscript_canonicalTranscript] exact h o hi hv @@ -291,8 +293,8 @@ theorem Amortized.mono (A : M.Amortized lower upper potential) (hlower : ∀ e o, lower' e o ≤ lower e o) (hupper : ∀ s o, D.valid s o → upper s o ≤ upper' s o) : M.Amortized lower' upper' potential := by - intro e o a e' tr hi hv hrun - have h := A o hi hv hrun + refine ⟨fun {e} o {a e' tr} hi hv hrun => ?_⟩ + have h := A.step o hi hv hrun have hl := E.totalCostTranscript_mono hlower e tr have hu := hupper _ o hv omega @@ -302,9 +304,9 @@ the amortized inequality. -/ theorem Amortized.mul_left (A : M.Amortized lower upper potential) (K : ℕ) : M.Amortized (fun e o => K * lower e o) (fun s o => K * upper s o) (fun e => K * potential e) := by - intro e o a e' tr hi hv hrun + refine ⟨fun {e} o {a e' tr} hi hv hrun => ?_⟩ rw [E.totalCostTranscript_mul_left, ← Nat.mul_add, ← Nat.mul_add] - exact Nat.mul_le_mul_left K (A o hi hv hrun) + exact Nat.mul_le_mul_left K (A.step o hi hv hrun) /-- Potentials telescope along any run of a linked admissible client. -/ theorem Amortized.demonic (A : M.Amortized lower upper potential) (p : D.Client α) @@ -324,7 +326,7 @@ theorem Amortized.demonic (A : M.Amortized lower upper potential) (p : D.Client rw [heq] at hrun obtain ⟨res, e₁, tr₁, tr₂, hrun₁, hrun₂, rfl⟩ := E.clientRun_bind_iff.mp hrun obtain ⟨hstep₁, he₁⟩ := M.correct o he hv_o hrun₁ - have hc₁ := A o he hv_o hrun₁ + have hc₁ := A.step o he hv_o hrun₁ obtain ⟨he₂, tr_D, hrun_D, hc₂⟩ := ih res he₁ (hv_k _ res hstep₁) hrun₂ refine ⟨he₂, ⟨o, res, M.abs e₁⟩ :: tr_D, .liftBind o k res hv_o hstep₁ hrun_D, ?_⟩ rw [E.totalCostTranscript_append lower hrun₁.validTranscript tr₂] @@ -355,9 +357,9 @@ theorem Amortized.comp {N : Refinement E F} {lower : F.State → F.Op → ℕ} {middle : E.State → E.Op → ℕ} {Φ : E.State → ℕ} {Ψ : F.State → ℕ} (A : M.Amortized middle upper Φ) (B : N.Amortized lower middle Ψ) : (M.comp N).Amortized lower upper (fun f => Φ (N.abs f) + Ψ f) := by - intro f o a f' tr hi hv hrun + refine ⟨fun {f} o {a f' tr} hi hv hrun => ?_⟩ obtain ⟨_, tr_E, hrun_E, hN⟩ := B.demonic (M.methods o) hi.1 (M.valid o hi.2 hv) hrun - have hM := A o hi.2 hv hrun_E + have hM := A.step o hi.2 hv hrun_E change F.totalCostTranscript lower f tr + (Φ (N.abs f') + Ψ f') ≤ upper (M.abs (N.abs f)) o + (Φ (N.abs f) + Ψ f) omega @@ -378,16 +380,16 @@ theorem Amortized.prod {M₁ : Refinement D₁ E} {M₂ : Refinement D₂ E} E.ClientRun e (M₂.methods o) a e' tr → Φ₁ e' = Φ₁ e) : (M₁.prod M₂ frame₁ frame₂).Amortized lower (productCharge upper₁ upper₂) (fun e => Φ₁ e + Φ₂ e) := by - intro e o a e' tr hi hv hrun + refine ⟨fun {e} o {a e' tr} hi hv hrun => ?_⟩ cases o with | inl o => - have h := A₁ o hi.1 hv hrun + have h := A₁.step o hi.1 hv hrun have hΦ := potential₁ o hi.1 hi.2 hv hrun change E.totalCostTranscript lower e tr + (Φ₁ e' + Φ₂ e') ≤ upper₁ (M₁.abs e) o + (Φ₁ e + Φ₂ e) omega | inr o => - have h := A₂ o hi.2 hv hrun + have h := A₂.step o hi.2 hv hrun have hΦ := potential₂ o hi.1 hi.2 hv hrun change E.totalCostTranscript lower e tr + (Φ₁ e' + Φ₂ e') ≤ upper₂ (M₂.abs e) o + (Φ₁ e + Φ₂ e) @@ -407,7 +409,7 @@ theorem Amortized.realize (A : M.Amortized lower upper potential) obtain ⟨e, ⟨hi, rfl⟩, hR₀⟩ := hr obtain ⟨e', tr, hrun, hR', _, hcost⟩ := B.demonic hR₀ (M.valid o hi hv) he obtain ⟨hstep, hi'⟩ := M.correct o hi hv hrun - have hc := A o hi hv hrun + have hc := A.step o hi hv hrun refine ⟨M.abs e', hstep, ⟨e', ⟨hi', rfl⟩, hR'⟩, ?_⟩ rw [← hR hR₀, ← hR hR'] omega @@ -415,7 +417,7 @@ theorem Amortized.realize (A : M.Amortized lower upper potential) /-- The identity refinement passes each charge through unchanged. -/ theorem Amortized.id (charge : D.State → D.Op → ℕ) : (Refinement.id D).Amortized charge charge fun _ => 0 := by - intro e o a e' tr _ _ hrun + refine ⟨fun {e} o {a e' tr} _ _ hrun => ?_⟩ cases hrun with | liftBind _ _ r _ _ hrest => cases hrest @@ -426,13 +428,13 @@ module. -/ theorem Amortized.chargeBound (A : M.Amortized lower upper fun _ => 0) : M.toModule.ChargeBound lower upper := by rintro s e o a e' tr ⟨hi, rfl⟩ hv hrun - simpa using A o hi hv hrun + simpa using A.step o hi hv hrun /-- A worst-case `Module.ChargeBound` of the module is an amortized refinement with zero potential. -/ theorem Amortized.of_chargeBound (B : M.toModule.ChargeBound lower upper) : M.Amortized lower upper fun _ => 0 := by - intro e o a e' tr hi hv hrun + refine ⟨fun {e} o {a e' tr} hi hv hrun => ?_⟩ simpa using B o ⟨hi, rfl⟩ hv hrun end Refinement From fe7d60dfa1f451062f231851e21fa89d4b413f61 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 17:31:13 -0500 Subject: [PATCH 07/27] wip: array stack over memory and queue method lemmas Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity/DataStructure/Queue/Spec.lean | 59 +-- Complexity/DataStructure/Stack/Memory.lean | 397 +++++++++++++++++++++ 2 files changed, 434 insertions(+), 22 deletions(-) create mode 100644 Complexity/DataStructure/Stack/Memory.lean diff --git a/Complexity/DataStructure/Queue/Spec.lean b/Complexity/DataStructure/Queue/Spec.lean index 7a0ec18c25..ef315844d5 100644 --- a/Complexity/DataStructure/Queue/Spec.lean +++ b/Complexity/DataStructure/Queue/Spec.lean @@ -28,6 +28,8 @@ method is described in Chapter 17. ## Main results * `transfer_run`: stack operations reverse the rear contents into the front stack. +* `runClient_dequeue_nil`, `stepFn_methods`: the methods realize the FIFO step on the represented + list. * `potential_step`: at most three amortized stack calls, including the size query. * `realized_client`: every adaptive client has a correct backend execution with its bound. * `realized_client_bound`: every supplied linked execution satisfies correctness and the bound. @@ -123,6 +125,36 @@ theorem transfer_run (front back : List α) : (fun r : Unit × (stackPair α).State × List (stackPair α).Op => (r.1, r.2.1, .inr .pop :: .inl (.push a) :: r.2.2)) (ih (a :: front)) +/-- A dequeue from an empty front stack asks for the rear size, transfers the rear, and pops the +front again. -/ +theorem runClient_dequeue_nil (back : List α) : + (stackPair α).runClient ([], back) (methods .dequeue) = + (back.reverse.head?, (back.reverse.tail, []), + .inl .pop :: .inr .size :: (transferTrace back ++ [.inl .pop])) := by + simp only [methods, bind, StateTransitionSpec.runClient_liftBind, stackPair, stack, + List.tail_nil, List.head?_nil] + rw [StateTransitionSpec.runClient_bind, transfer_run] + simp + +/-- Each method returns the FIFO answer, and the stacks then represent the FIFO successor of the +represented list. -/ +theorem stepFn_methods (front back : List α) (o : QueueOp α) : + (queue α).stepFn (front ++ back.reverse) o = + (((stackPair α).clientState (front, back) (methods o)).1 ++ + ((stackPair α).clientState (front, back) (methods o)).2.reverse, + (stackPair α).clientResult (front, back) (methods o)) := by + cases o with + | enqueue n => + change ((front ++ back.reverse) ++ [n], ()) = (front ++ (n :: back).reverse, ()) + simp [List.reverse_cons, List.append_assoc] + | dequeue => + cases front with + | cons a front => rfl + | nil => + simp only [StateTransitionSpec.clientState, StateTransitionSpec.clientResult] + rw [runClient_dequeue_nil] + simp + /-- Representation and behavior are proved once, before selecting a backend or cost model. -/ def implementation : Module (queue α) (stackPair α) := Module.ofFn @@ -134,21 +166,8 @@ def implementation : Module (queue α) (stackPair α) := intro q s o hr _ subst q obtain ⟨front, back⟩ := s - cases o with - | enqueue n => - constructor - · rfl - · change (front ++ back.reverse) ++ [n] = front ++ (n :: back).reverse - simp [List.reverse_cons, List.append_assoc] - | dequeue => - cases front with - | cons a front => exact ⟨rfl, rfl⟩ - | nil => - simp only [methods, bind, StateTransitionSpec.clientResult, - StateTransitionSpec.clientState, StateTransitionSpec.runClient_liftBind, - stackPair, stack, List.tail_nil, List.head?_nil] - rw [StateTransitionSpec.runClient_bind, transfer_run] - simp) + have h := stepFn_methods front back o + exact ⟨(congrArg Prod.snd h).symm, congrArg Prod.fst h⟩) /-- Each transferred element incurs one pop and one push. -/ theorem transferTrace_length (back : List α) : (transferTrace back).length = 2 * back.length := by @@ -173,13 +192,9 @@ theorem potential_step (s : (stackPair α).State) (o : QueueOp α) : change 1 + 2 * back.length ≤ 3 + 2 * back.length omega | nil => - simp only [methods, bind, StateTransitionSpec.runClient_liftBind, stackPair, - stack, List.tail_nil, List.head?_nil] - rw [StateTransitionSpec.runClient_bind, transfer_run] - simp only [StateTransitionSpec.runClient_liftBind, StateTransitionSpec.runClient_pure] - change (transferTrace back ++ [Sum.inl StackOp.pop]).length + 2 ≤ 3 + 2 * back.length - rw [List.length_append, transferTrace_length] - simp only [List.length_singleton] + rw [runClient_dequeue_nil] + simp only [List.length_cons, List.length_append, List.length_nil] + rw [transferTrace_length] omega private theorem weighted_potential_step (K : Nat) {q : (queue α).State} {s : (stackPair α).State} diff --git a/Complexity/DataStructure/Stack/Memory.lean b/Complexity/DataStructure/Stack/Memory.lean new file mode 100644 index 0000000000..b70e19923f --- /dev/null +++ b/Complexity/DataStructure/Stack/Memory.lean @@ -0,0 +1,397 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.DataStructure.Memory.Field +public import Complexity.DataStructure.StateTransition.Refinement +public import Complexity.DataStructure.Stack.WordRAM.Basic + +/-! +# An array stack over the memory oracle + +The bounded stack `Stack.WordRAM.spec w C` is implemented by three short programs over +`Memory.spec w`. The storage is a `Layout` with two named fields: a one-cell `count` and an +`items` array of `C` cells, bottom first. The abstraction function reads the first `count` items +and reverses them, so the top comes first. Each operation is one read, two writes at most, and a +single branch; its evaluation is definitional (`runClient_push`, `runClient_pop_of_pos`, …), and +the refinement obligations follow from those equations and the list view of the array. + +Every operation costs at most three memory units, with no potential (`amortized`). The methods +write only the layout's cells and never allocate (`modifies_methods`), and the abstraction and +invariant depend only on those cells (`abs_congr`, `Inv.congr`). Two stacks with disjoint +layouts therefore implement the product of two stacks (`pairRefinement`). + +This is the same specification that `Stack.WordRAM` implements by a hand-stepped nine-register +dispatcher. Here no register, instruction count or word-fit chain appears; fits enter only +through the admissibility of `Memory.spec w`. + +## Main definitions + +* `Layout`, `Layout.footprint`, `Layout.Valid`: the two fields, the cells they own, and their + static well-formedness. +* `push`, `pop`, `size`, `methods`: the operations as memory programs. +* `abs`, `Inv`: the abstraction function and the implementation invariant. +* `refinement`: the array stack as a `Refinement` of the bounded stack. +* `pairRefinement`: two array stacks with disjoint layouts implement the pair of stacks. +* `allocLayout`: allocate an empty stack. + +## Main results + +* `amortized`: every operation costs at most three memory units. +* `pair_amortized`: so does every operation of the pair. +* `modifies_methods`, `abs_congr`, `Inv.congr`: the frame of a stack. +* `runClient_allocLayout_bind`, `inv_allocLayout`, `abs_allocLayout`: allocation yields an empty + stack in three memory units. + +## References + +* [T. H. Cormen, C. E. Leiserson, R. L. Rivest and C. Stein, *Introduction to Algorithms*, + Section 10.1][CLRS2009] +-/ + +@[expose] public section + +namespace Cslib.DataStructure.Stack.Memory + +open Cslib.WordRAM Cslib.DataStructure Cslib.DataStructure.Memory Cslib.DataStructure.Queue + +/-- The storage of an array stack: a one-cell field holding the number of items, and the item +array, bottom first. -/ +structure Layout where + /-- One cell holding the number of stored items. -/ + count : Block + /-- The items, bottom first. -/ + items : Block + +namespace Layout + +/-- The cells a stack owns. -/ +def footprint (L : Layout) : Finset ℕ := L.count.footprint ∪ L.items.footprint + +/-- Static well-formedness for capacity `C` at word size `w`: the count field is one cell outside +the item array, the array has `C` cells, and `C` fits a word. -/ +structure Valid (L : Layout) (w C : ℕ) : Prop where + /-- The count field is one cell. -/ + count_length : L.count.length = 1 + /-- The count cell is not an item. -/ + count_not_owns : ¬L.items.Owns L.count.base + /-- The item array has the capacity. -/ + items_length : L.items.length = C + /-- The capacity fits a word, so every count does. -/ + capacity_fits : Fits w C + +/-- The count cell belongs to the footprint. -/ +theorem count_mem_footprint (L : Layout) (hL : L.count.length = 1) : + L.count.base ∈ L.footprint := + Finset.mem_union_left _ (Block.mem_footprint.mpr ⟨le_rfl, by omega⟩) + +/-- Every item cell belongs to the footprint. -/ +theorem item_mem_footprint (L : Layout) {i : ℕ} (hi : i < L.items.length) : + L.items.base + i ∈ L.footprint := + Finset.mem_union_right _ (Block.mem_footprint.mpr (Block.owns_add hi)) + +end Layout + +variable (L : Layout) + +/-- Push: write the item above the top, then increment the count. -/ +def push (v : ℕ) : Prog Unit := do + let n ← L.count.get 0 + L.items.set n v + L.count.set 0 (n + 1) + +/-- Pop: an empty stack returns `none`; otherwise read the top item and decrement the count. -/ +def pop : Prog (Option ℕ) := do + let n ← L.count.get 0 + if n = 0 then pure none else do + let v ← L.items.get (n - 1) + L.count.set 0 (n - 1) + pure (some v) + +/-- Size: read the count. -/ +def size : Prog ℕ := L.count.get 0 + +/-- The stack operations as memory programs. -/ +def methods (w C : ℕ) : Methods (Stack.WordRAM.spec w C) (Memory.spec w) + | .push v => push L v + | .pop => pop L + | .size => size L + +/-- The stored stack, top first: the first `count` items, reversed. -/ +def abs (s : Memory.State) : List ℕ := (L.items.entries (s.mem L.count.base) s.mem).reverse + +/-- The implementation invariant: both fields are allocated and the count is within capacity. -/ +structure Inv (s : Memory.State) : Prop where + /-- The count cell is allocated. -/ + count_allocated : L.count.Allocated s + /-- The item array is allocated. -/ + items_allocated : L.items.Allocated s + /-- The count is at most the capacity. -/ + count_le : s.mem L.count.base ≤ L.items.length + +/-! ## Evaluation -/ + +variable {w : ℕ} + +/-- A push reads the count, writes the item and writes the new count. -/ +theorem runClient_push (s : Memory.State) (v : ℕ) : + (Memory.spec w).runClient s (push L v) = + ((), ⟨Function.update (Function.update s.mem (L.items.base + s.mem L.count.base) v) + L.count.base (s.mem L.count.base + 1), s.frontier⟩, + [.read L.count.base, .write (L.items.base + s.mem L.count.base) v, + .write L.count.base (s.mem L.count.base + 1)]) := rfl + +/-- A pop from an empty stack reads the count only. -/ +theorem runClient_pop_of_eq_zero (s : Memory.State) (h : s.mem L.count.base = 0) : + (Memory.spec w).runClient s (pop L) = (none, s, [.read L.count.base]) := by + simp [pop, h] + +/-- A pop from a nonempty stack reads the count and the top item and writes the new count. -/ +theorem runClient_pop_of_pos (s : Memory.State) (h : s.mem L.count.base ≠ 0) : + (Memory.spec w).runClient s (pop L) = + (some (s.mem (L.items.base + (s.mem L.count.base - 1))), + ⟨Function.update s.mem L.count.base (s.mem L.count.base - 1), s.frontier⟩, + [.read L.count.base, .read (L.items.base + (s.mem L.count.base - 1)), + .write L.count.base (s.mem L.count.base - 1)]) := by + unfold pop + simp only [Block.runClient_get_bind, Nat.add_zero, h, ↓reduceIte] + rfl + +/-- A size query reads the count. -/ +theorem runClient_size (s : Memory.State) : + (Memory.spec w).runClient s (size L) = (s.mem L.count.base, s, [.read L.count.base]) := rfl + +/-! ## The list view -/ + +variable {L} {C : ℕ} + +/-- The stack has as many elements as the count. -/ +@[simp] theorem length_abs (s : Memory.State) : (abs L s).length = s.mem L.count.base := by + simp [abs] + +/-- Writing the count leaves the items in place. -/ +theorem abs_update_count (hL : L.Valid w C) (m : ℕ → ℕ) (f : ℕ) {n : ℕ} (hn : n ≤ C) : + abs L ⟨Function.update m L.count.base n, f⟩ = (L.items.entries n m).reverse := by + simp only [abs, Function.update_self] + rw [Block.entries_update_of_not_owns _ (hL.items_length ▸ hn) hL.count_not_owns] + +/-- The abstraction and the invariant read only the layout's cells and the frontier. -/ +theorem abs_congr {s s' : Memory.State} (hL : L.Valid w C) (hs : Inv L s) + (h : ∀ a ∈ L.footprint, s'.mem a = s.mem a) : abs L s' = abs L s := by + have hc : s'.mem L.count.base = s.mem L.count.base := + h _ (L.count_mem_footprint hL.count_length) + simp only [abs, hc] + rw [Block.entries_congr _ _ fun i hi => h _ (L.item_mem_footprint (by + have := hs.count_le + omega))] + +/-- The invariant reads only the layout's cells and survives a growing frontier. -/ +theorem Inv.congr {s s' : Memory.State} (hL : L.Valid w C) (hs : Inv L s) + (h : ∀ a ∈ L.footprint, s'.mem a = s.mem a) (hf : s.frontier ≤ s'.frontier) : Inv L s' where + count_allocated := hs.count_allocated.mono hf + items_allocated := hs.items_allocated.mono hf + count_le := by + rw [h _ (L.count_mem_footprint hL.count_length)] + exact hs.count_le + +/-! ## The refinement -/ + +/-- The count cell is an admissible address. -/ +theorem Inv.count_lt (hL : L.Valid w C) {s : Memory.State} (hs : Inv L s) : + L.count.base < s.frontier := by + simpa using hs.count_allocated.lt_frontier (i := 0) (by rw [hL.count_length]; omega) + +/-- Item `i` below the capacity is an admissible address. -/ +theorem Inv.item_lt {s : Memory.State} (hs : Inv L s) {i : ℕ} (hi : i < L.items.length) : + L.items.base + i < s.frontier := + hs.items_allocated.lt_frontier hi + +/-- The array stack implements the bounded stack of capacity `C`. -/ +def refinement (hL : L.Valid w C) : Refinement (Stack.WordRAM.spec w C) (Memory.spec w) := + Refinement.ofFn (methods L w C) (Inv L) (abs L) + (by + intro s o hs hv + have hcount := hs.count_lt hL + have hC := hL.items_length + cases o with + | push v => + obtain ⟨hlen, hfit⟩ := hv + rw [length_abs] at hlen + simp only [methods, StateTransitionSpec.clientHistory, runClient_push] + simp only [StateTransitionSpec.validHistory_cons, StateTransitionSpec.stepFn, + step_read, step_write, valid_read, valid_write] + exact ⟨hcount, ⟨hs.item_lt (by omega), hfit⟩, ⟨hcount, + hL.capacity_fits.of_le (by omega)⟩, trivial⟩ + | pop => + by_cases h0 : s.mem L.count.base = 0 + · simp only [methods, StateTransitionSpec.clientHistory, runClient_pop_of_eq_zero L s h0] + exact ⟨hcount, trivial⟩ + · simp only [methods, StateTransitionSpec.clientHistory, runClient_pop_of_pos L s h0] + simp only [StateTransitionSpec.validHistory_cons, StateTransitionSpec.stepFn, + step_read, step_write, valid_read, valid_write] + have := hs.count_le + exact ⟨hcount, hs.item_lt (by omega), ⟨hcount, + hL.capacity_fits.of_le (by omega)⟩, trivial⟩ + | size => + simp only [methods, StateTransitionSpec.clientHistory, runClient_size] + exact ⟨hcount, trivial⟩) + (by + intro s o hs hv + have hle := hs.count_le + have hC := hL.items_length + cases o with + | push v => + obtain ⟨hlen, -⟩ := hv + rw [length_abs] at hlen + simp only [methods, StateTransitionSpec.clientState, StateTransitionSpec.clientResult, + runClient_push] + refine ⟨?_, ⟨hs.count_allocated, hs.items_allocated, by simp; omega⟩⟩ + rw [abs_update_count hL _ _ (by omega), Block.entries_succ, + Block.entries_update_of_le _ le_rfl, Function.update_self] + simp [abs] + | pop => + by_cases h0 : s.mem L.count.base = 0 + · simp only [methods, StateTransitionSpec.clientState, StateTransitionSpec.clientResult, + runClient_pop_of_eq_zero L s h0] + have he : abs L s = [] := List.eq_nil_of_length_eq_zero (by simp [h0]) + simp [he, hs] + · simp only [methods, StateTransitionSpec.clientState, StateTransitionSpec.clientResult, + runClient_pop_of_pos L s h0] + refine ⟨?_, ⟨hs.count_allocated, hs.items_allocated, by simp; omega⟩⟩ + obtain ⟨n, hn⟩ := Nat.exists_eq_succ_of_ne_zero h0 + have habs : abs L s = + s.mem (L.items.base + n) :: (L.items.entries n s.mem).reverse := by + simp [abs, hn, Block.entries_succ] + rw [abs_update_count hL _ _ (by omega), habs] + simp [hn] + | size => + simp only [methods, StateTransitionSpec.clientState, StateTransitionSpec.clientResult, + runClient_size] + exact ⟨by simp, hs⟩) + +/-- Every operation costs at most three memory units, so the stack needs no potential. -/ +theorem amortized (hL : L.Valid w C) : + (refinement hL).Amortized charge (fun _ _ => 3) fun _ => 0 := by + apply Refinement.Amortized.ofFn + intro s o _ _ + cases o with + | push v => simp [refinement, methods, StateTransitionSpec.clientHistory, runClient_push] + | pop => + by_cases h0 : s.mem L.count.base = 0 + · simp [refinement, methods, StateTransitionSpec.clientHistory, + runClient_pop_of_eq_zero L s h0] + · simp [refinement, methods, StateTransitionSpec.clientHistory, runClient_pop_of_pos L s h0] + | size => simp [refinement, methods, StateTransitionSpec.clientHistory, runClient_size] + +/-! ## The frame of a stack -/ + +/-- Each admissible operation writes only the layout's cells and never allocates. -/ +theorem modifies_methods (hL : L.Valid w C) {s : Memory.State} (o : StackOp ℕ) + (hv : (Stack.WordRAM.spec w C).valid (abs L s) o) : + (∀ a ∉ L.footprint, + ((Memory.spec w).clientState s (methods L w C o)).mem a = s.mem a) ∧ + ((Memory.spec w).clientState s (methods L w C o)).frontier = s.frontier := by + have hc : L.count.base ∈ L.footprint := L.count_mem_footprint hL.count_length + cases o with + | push v => + obtain ⟨hlen, -⟩ := hv + rw [length_abs] at hlen + have hi : L.items.base + s.mem L.count.base ∈ L.footprint := + L.item_mem_footprint (by rw [hL.items_length]; exact hlen) + simp only [methods, StateTransitionSpec.clientState, runClient_push] + exact ⟨fun a ha => by + rw [Function.update_of_ne (ne_of_mem_of_not_mem hc ha).symm, + Function.update_of_ne (ne_of_mem_of_not_mem hi ha).symm], trivial⟩ + | pop => + by_cases h0 : s.mem L.count.base = 0 + · simp only [methods, StateTransitionSpec.clientState, runClient_pop_of_eq_zero L s h0] + exact ⟨fun _ _ => trivial, trivial⟩ + · simp only [methods, StateTransitionSpec.clientState, runClient_pop_of_pos L s h0] + exact ⟨fun a ha => Function.update_of_ne (ne_of_mem_of_not_mem hc ha).symm _ _, trivial⟩ + | size => exact ⟨fun _ _ => rfl, rfl⟩ + +/-- A stack's operations leave a stack with a disjoint layout unchanged. -/ +theorem frame {L' : Layout} (hL : L.Valid w C) (hL' : L'.Valid w C) + (hd : Disjoint L.footprint L'.footprint) {s : Memory.State} (hs' : Inv L' s) + (o : StackOp ℕ) (hv : (Stack.WordRAM.spec w C).valid (abs L s) o) : + abs L' ((Memory.spec w).clientState s (methods L w C o)) = abs L' s ∧ + Inv L' ((Memory.spec w).clientState s (methods L w C o)) := by + obtain ⟨hm, hf⟩ := modifies_methods hL o hv + have h : ∀ a ∈ L'.footprint, + ((Memory.spec w).clientState s (methods L w C o)).mem a = s.mem a := + fun a ha => hm a (Finset.disjoint_right.mp hd ha) + exact ⟨abs_congr hL' hs' h, hs'.congr hL' h hf.ge⟩ + +/-! ## Two stacks in one memory -/ + +/-- Two array stacks with disjoint layouts implement a pair of bounded stacks. -/ +def pairRefinement {L₁ L₂ : Layout} (h₁ : L₁.Valid w C) (h₂ : L₂.Valid w C) + (hd : Disjoint L₁.footprint L₂.footprint) : + Refinement (product (Stack.WordRAM.spec w C) (Stack.WordRAM.spec w C)) (Memory.spec w) := + (refinement h₁).prod (refinement h₂) + (fun {s} o {a s' tr} hs₁ hs₂ hv hrun => by + obtain ⟨_, _, rfl, _⟩ := ((Memory.spec w).clientRun_iff_runClient _ _ _ _ _).mp hrun + exact frame h₁ h₂ hd hs₂ o hv) + (fun {s} o {a s' tr} hs₁ hs₂ hv hrun => by + obtain ⟨_, _, rfl, _⟩ := ((Memory.spec w).clientRun_iff_runClient _ _ _ _ _).mp hrun + exact frame h₂ h₁ hd.symm hs₁ o hv) + +/-- Every operation of the pair costs at most three memory units. -/ +theorem pair_amortized {L₁ L₂ : Layout} (h₁ : L₁.Valid w C) (h₂ : L₂.Valid w C) + (hd : Disjoint L₁.footprint L₂.footprint) : + (pairRefinement h₁ h₂ hd).Amortized charge (fun _ _ => 3) fun _ => 0 := by + have h : (pairRefinement h₁ h₂ hd).Amortized charge + (productCharge (fun _ _ => 3) (fun _ _ => 3)) fun _ => 0 := + (amortized h₁).prod (amortized h₂) (fun _ _ _ _ _ _ _ _ => rfl) + (fun _ _ _ _ _ _ _ _ => rfl) + rwa [productCharge_const] at h + +/-! ## Allocation -/ + +/-- Allocate an empty stack of capacity `C`: a zeroed count cell and an item array that is not +cleared. -/ +def allocLayout (C : ℕ) : Prog Layout := do + let count ← allocBlock 1 + let items ← allocBlockDirty C + pure ⟨count, items⟩ + +/-- Allocation places the count at the frontier and the items just above it. -/ +theorem runClient_allocLayout_bind {α : Type} (C : ℕ) (next : Layout → Prog α) + (s : Memory.State) : + (Memory.spec w).runClient s (allocLayout C >>= next) = + let (v, s', os) := (Memory.spec w).runClient + ⟨clear s.mem s.frontier 1, s.frontier + 1 + C⟩ + (next ⟨⟨s.frontier, 1⟩, ⟨s.frontier + 1, C⟩⟩) + (v, s', .alloc 1 :: .allocDirty C :: os) := rfl + +/-- The allocated layout is well formed when its end fits a word. -/ +theorem valid_allocLayout {f : ℕ} (hf : Fits w (f + 1 + C)) : + Layout.Valid ⟨⟨f, 1⟩, ⟨f + 1, C⟩⟩ w C where + count_length := rfl + count_not_owns := by simp [Block.Owns] + items_length := rfl + capacity_fits := hf.of_le (by omega) + +/-- The allocated stack satisfies the invariant and is empty. -/ +theorem inv_allocLayout (s : Memory.State) : + Inv ⟨⟨s.frontier, 1⟩, ⟨s.frontier + 1, C⟩⟩ ⟨clear s.mem s.frontier 1, s.frontier + 1 + C⟩ ∧ + abs ⟨⟨s.frontier, 1⟩, ⟨s.frontier + 1, C⟩⟩ + ⟨clear s.mem s.frontier 1, s.frontier + 1 + C⟩ = [] := by + have h0 : clear s.mem s.frontier 1 s.frontier = 0 := + clear_of_owns ⟨le_rfl, show s.frontier < s.frontier + 1 from Nat.lt_add_one _⟩ + refine ⟨⟨?_, ?_, ?_⟩, ?_⟩ + · change s.frontier + 1 ≤ s.frontier + 1 + C + omega + · change s.frontier + 1 + C ≤ s.frontier + 1 + C + exact le_rfl + · change clear s.mem s.frontier 1 s.frontier ≤ C + rw [h0] + exact Nat.zero_le C + · simp [abs, h0] + +end Cslib.DataStructure.Stack.Memory From aad5f3ee860b445a7bde96ab39ab69d50b6f6a0f Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 17:31:23 -0500 Subject: [PATCH 08/27] feat(Foundations): affine cost bounds are O(size) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Foundations/Analysis/Asymptotics/CostBound.lean | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Complexity/Foundations/Analysis/Asymptotics/CostBound.lean b/Complexity/Foundations/Analysis/Asymptotics/CostBound.lean index d17a2b7965..3a99eace52 100644 --- a/Complexity/Foundations/Analysis/Asymptotics/CostBound.lean +++ b/Complexity/Foundations/Analysis/Asymptotics/CostBound.lean @@ -32,6 +32,7 @@ exact natural-number bound into an asymptotic one. ## Main results * `Cslib.CostIsBigO.of_le_nat`: export an exact monotone natural-number bound. +* `Cslib.CostIsBigO.of_le_add_mul`: an affine bound `a + b * size` is `O(size)`. * `Cslib.CostIsBigO.mono`: a smaller cost has the same bound. * `Cslib.CostIsBigO.trans_isBigO`: weaken the comparison function. @@ -76,6 +77,18 @@ theorem trans_isBigO {g' : ι → ℝ} (h : CostIsBigO l g size cost) (hg : g =O obtain ⟨f, hf, hf0, hO, hb⟩ := h exact ⟨f, hf, hf0, hO.trans hg, hb⟩ +/-- A cost at most `a + b * size` is `O(size)` along `atTop`. -/ +theorem of_le_add_mul {size : α → ℕ} (a b : ℕ) (h : ∀ x, cost x ≤ a + b * size x) : + CostIsBigO Filter.atTop (fun n : ℕ => (n : ℝ)) size cost := by + refine of_le_nat (F := fun n => a + b * n) + (fun _ _ hmn => Nat.add_le_add_left (Nat.mul_le_mul_left b hmn) a) ?_ h + refine IsBigO.of_bound (a + b) ?_ + filter_upwards [Filter.eventually_ge_atTop 1] with n hn + have hn' : (1 : ℝ) ≤ n := by exact_mod_cast hn + rw [Real.norm_of_nonneg (by positivity), Real.norm_of_nonneg (by positivity)] + push_cast + nlinarith + end CostIsBigO end Cslib From 6698e86529717d14e635feadf264726885f9cbb9 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 17:34:38 -0500 Subject: [PATCH 09/27] wip: queue over memory Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity/DataStructure/Queue/Memory.lean | 185 +++++++++++++++++++++ Complexity/DataStructure/Stack/Memory.lean | 17 +- 2 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 Complexity/DataStructure/Queue/Memory.lean diff --git a/Complexity/DataStructure/Queue/Memory.lean b/Complexity/DataStructure/Queue/Memory.lean new file mode 100644 index 0000000000..28a37a7a87 --- /dev/null +++ b/Complexity/DataStructure/Queue/Memory.lean @@ -0,0 +1,185 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.DataStructure.Queue.WordRAM.Model +public import Complexity.DataStructure.Stack.Memory +public import Complexity.Foundations.Analysis.Asymptotics.CostBound + +/-! +# A queue over two array stacks over the memory oracle + +The bounded FIFO queue `Queue.WordRAM.spec w C` is implemented in two refinement steps, each +proved once: + +1. `refinement`: the two-stack queue `Queue.methods` over a pair of bounded stacks. It reuses the + queue's existing methods and their behavior (`Queue.stepFn_methods`) and potential argument + (`Queue.potential_step`); only the capacity and word-fit admissibility of the bounded stacks is + new. Each queue operation costs at most three stack calls, amortized, with two credits per + rear element. +2. `Stack.Memory.pairRefinement`: two array stacks with disjoint layouts over `Memory.spec w`, + each stack call costing at most three memory units. + +`memRefinement` composes them, and `mem_amortized` stacks the potentials across the composition +(`Refinement.Amortized.comp`): every queue operation costs at most nine memory units, amortized, +with six credits per rear element. `run` allocates the two stacks and then runs a queue client; +`run_correct` and `run_cost` state its result and its exact memory cost from any well-formed memory, +and `run_cost_isBigO` exports the cost as `O(m)` in the number `m` of queue operations. + +The machine-level counterpart is `Queue.WordRAM`, whose dispatcher is hand-stepped instruction by +instruction. Here the proofs mention neither registers nor instructions; executing the memory +programs on the word RAM is the separate realization step of #2205. + +## Main definitions + +* `boundedStacks`: the pair of bounded stacks. +* `Inv`: a FIFO list within capacity holding words. +* `refinement`: the two-stack queue over the pair of bounded stacks. +* `memRefinement`: the queue over two array stacks over memory. +* `run`: allocate the stacks, then run a queue client. + +## Main results + +* `amortized`: three stack calls per queue operation, with two credits per rear element. +* `mem_amortized`: nine memory units per queue operation, with six credits per rear element. +* `run_correct`, `run_cost`: the allocated queue answers like the specification and costs at + most `6 + 9 m` memory units for `m` queue operations. +* `run_cost_isBigO`: the same cost as `O(m)`. + +## References + +* [T. H. Cormen, C. E. Leiserson, R. L. Rivest and C. Stein, *Introduction to Algorithms*, + Exercise 10.1-6 and Chapter 17][CLRS2009] +-/ + +@[expose] public section + +namespace Cslib.DataStructure.Queue.Memory + +open Cslib.WordRAM Cslib.DataStructure Cslib.DataStructure.Memory Cslib.DataStructure.Queue + +/-- Two bounded stacks of capacity `C` at word size `w`. -/ +abbrev boundedStacks (w C : ℕ) : StateTransitionSpec := + product (Stack.WordRAM.spec w C) (Stack.WordRAM.spec w C) + +/-- A FIFO list within capacity `C` whose entries fit a `w`-bit word. -/ +structure Inv (w C : ℕ) (q : List ℕ) : Prop where + /-- The list is within capacity. -/ + length_le : q.length ≤ C + /-- Every entry fits a word. -/ + fits : ∀ x ∈ q, Fits w x + +namespace Inv + +variable {w C : ℕ} + +/-- Admissible FIFO steps keep the list within capacity and made of words. -/ +theorem step {q : List ℕ} (h : Inv w C q) {o : QueueOp ℕ} + (hv : (Queue.WordRAM.spec w C).valid q o) : Inv w C ((queue ℕ).stepFn q o).1 := by + cases o with + | enqueue n => + obtain ⟨hlen, hfit⟩ := hv + refine ⟨by simp; omega, fun x hx => ?_⟩ + simp only [List.mem_append, List.mem_singleton] at hx + rcases hx with hx | rfl + · exact h.fits x hx + · exact hfit + | dequeue => + exact ⟨by simp; have := h.length_le; omega, fun x hx => h.fits x (List.mem_of_mem_tail hx)⟩ + +end Inv + +variable {w C : ℕ} + +/-- The bounded stacks step exactly as the unbounded ones; only admissibility differs. -/ +theorem runClient_boundedStacks {β : Type} (s : List ℕ × List ℕ) (p : (stackPair ℕ).Client β) : + (boundedStacks w C).runClient s p = (stackPair ℕ).runClient s p := by + induction p generalizing s with + | pure a => rfl + | liftBind o k ih => + have hstep : (boundedStacks w C).stepFn s o = (stackPair ℕ).stepFn s o := by cases o <;> rfl + simp only [StateTransitionSpec.runClient_liftBind, hstep, ih] + +/-- Moving the rear into the front is admissible for bounded stacks when both fit together and +the rear holds words. -/ +theorem validHistory_transferTrace {front back : List ℕ} (hlen : front.length + back.length ≤ C) + (hfit : ∀ x ∈ back, Fits w x) : (boundedStacks w C).ValidHistory (front, back) (transferTrace back) := by + induction back generalizing front with + | nil => trivial + | cons a back ih => + simp only [List.length_cons] at hlen + refine ⟨trivial, ⟨by simp only; omega, hfit a (by simp)⟩, ?_⟩ + exact ih (front := a :: front) (by simp only [List.length_cons]; omega) + fun x hx => hfit x (by simp [hx]) + +/-- The two-stack queue over a pair of bounded stacks: the stacks represent `front ++ rear⁻¹`. -/ +def refinement (w C : ℕ) : Refinement (Queue.WordRAM.spec w C) (boundedStacks w C) := + Refinement.ofFn (methods (α := ℕ)) (fun s => Inv w C (Queue.WordRAM.contents s)) + Queue.WordRAM.contents + (by + rintro ⟨front, back⟩ o hs hv + have hlen := hs.length_le + simp only [Queue.WordRAM.contents, List.length_append, List.length_reverse] at hlen + cases o with + | enqueue n => + obtain ⟨hq, hfit⟩ := hv + simp only [Queue.WordRAM.contents, List.length_append, List.length_reverse] at hq + exact ⟨⟨by omega, hfit⟩, trivial⟩ + | dequeue => + cases front with + | cons a front => exact ⟨trivial, trivial⟩ + | nil => + change (boundedStacks w C).ValidHistory ([], back) + ((boundedStacks w C).runClient ([], back) (methods .dequeue)).2.2 + rw [runClient_boundedStacks, runClient_dequeue_nil] + refine ⟨trivial, trivial, ?_⟩ + rw [StateTransitionSpec.validHistory_append] + refine ⟨validHistory_transferTrace (by simpa using hlen) fun x hx => + hs.fits x (by simp [Queue.WordRAM.contents, hx]), trivial, trivial⟩) + (by + rintro ⟨front, back⟩ o hs hv + have h := stepFn_methods front back o + refine ⟨h, ?_⟩ + have hstep := hs.step hv + rw [show Queue.WordRAM.contents (front, back) = front ++ back.reverse from rfl, h] at hstep + exact hstep) + +/-- Each queue operation costs at most three stack calls, amortized, with two credits per rear +element. -/ +theorem amortized : + (refinement w C).Amortized (fun _ _ => 1) (fun _ _ => 3) fun s => 2 * s.2.length := + Refinement.Amortized.ofFn fun {s} o _ _ => by + have h := potential_step s o + rw [← runClient_boundedStacks (w := w) (C := C)] at h + rw [StateTransitionSpec.totalCost_one] + exact h + +/-! ## Over memory -/ + +/-- The queue over two array stacks over memory. -/ +def memRefinement {L₁ L₂ : Stack.Memory.Layout} (h₁ : L₁.Valid w C) (h₂ : L₂.Valid w C) + (hd : Disjoint L₁.footprint L₂.footprint) : + Refinement (Queue.WordRAM.spec w C) (Memory.spec w) := + (refinement w C).comp (Stack.Memory.pairRefinement h₁ h₂ hd) + +/-- **Stacked potentials.** Every queue operation costs at most nine memory units, amortized, +with six credits per element of the rear stack: the queue's potential, at three memory units per +stack call, read through the stacks' abstraction. -/ +theorem mem_amortized {L₁ L₂ : Stack.Memory.Layout} (h₁ : L₁.Valid w C) (h₂ : L₂.Valid w C) + (hd : Disjoint L₁.footprint L₂.footprint) : + (memRefinement h₁ h₂ hd).Amortized charge (fun _ _ => 9) + fun s => 6 * (Stack.Memory.abs L₂ s).length := by + have h := (amortized.mul_left 3).comp (Stack.Memory.pair_amortized h₁ h₂ hd) + have hΦ : (fun s : Memory.State => + 3 * (2 * (Stack.Memory.abs L₂ s).length) + 0) = + fun s => 6 * (Stack.Memory.abs L₂ s).length := by + funext s + omega + exact hΦ ▸ h + +end Cslib.DataStructure.Queue.Memory diff --git a/Complexity/DataStructure/Stack/Memory.lean b/Complexity/DataStructure/Stack/Memory.lean index b70e19923f..8f5069fe44 100644 --- a/Complexity/DataStructure/Stack/Memory.lean +++ b/Complexity/DataStructure/Stack/Memory.lean @@ -37,13 +37,15 @@ through the admissibility of `Memory.spec w`. * `push`, `pop`, `size`, `methods`: the operations as memory programs. * `abs`, `Inv`: the abstraction function and the implementation invariant. * `refinement`: the array stack as a `Refinement` of the bounded stack. -* `pairRefinement`: two array stacks with disjoint layouts implement the pair of stacks. +* `pairMethods`, `pairRefinement`: two array stacks with disjoint layouts implement the pair of + stacks. * `allocLayout`: allocate an empty stack. ## Main results * `amortized`: every operation costs at most three memory units. * `pair_amortized`: so does every operation of the pair. +* `pairRefinement_methods`: the pair's methods do not depend on the proofs about its layouts. * `modifies_methods`, `abs_congr`, `Inv.congr`: the frame of a stack. * `runClient_allocLayout_bind`, `inv_allocLayout`, `abs_allocLayout`: allocation yields an empty stack in three memory units. @@ -329,6 +331,12 @@ theorem frame {L' : Layout} (hL : L.Valid w C) (hL' : L'.Valid w C) /-! ## Two stacks in one memory -/ +/-- The operations of two stacks, each on its own layout. -/ +def pairMethods (L₁ L₂ : Layout) (w C : ℕ) : + Methods (product (Stack.WordRAM.spec w C) (Stack.WordRAM.spec w C)) (Memory.spec w) + | .inl o => methods L₁ w C o + | .inr o => methods L₂ w C o + /-- Two array stacks with disjoint layouts implement a pair of bounded stacks. -/ def pairRefinement {L₁ L₂ : Layout} (h₁ : L₁.Valid w C) (h₂ : L₂.Valid w C) (hd : Disjoint L₁.footprint L₂.footprint) : @@ -341,6 +349,13 @@ def pairRefinement {L₁ L₂ : Layout} (h₁ : L₁.Valid w C) (h₂ : L₂.Val obtain ⟨_, _, rfl, _⟩ := ((Memory.spec w).clientRun_iff_runClient _ _ _ _ _).mp hrun exact frame h₂ h₁ hd.symm hs₁ o hv) +/-- The pair's methods are `pairMethods`, whatever the proofs about the layouts. -/ +theorem pairRefinement_methods {L₁ L₂ : Layout} (h₁ : L₁.Valid w C) (h₂ : L₂.Valid w C) + (hd : Disjoint L₁.footprint L₂.footprint) : + (pairRefinement h₁ h₂ hd).methods = pairMethods L₁ L₂ w C := by + funext o + cases o <;> rfl + /-- Every operation of the pair costs at most three memory units. -/ theorem pair_amortized {L₁ L₂ : Layout} (h₁ : L₁.Valid w C) (h₂ : L₂.Valid w C) (hd : Disjoint L₁.footprint L₂.footprint) : From 7dda2489654c6b0a3a10ca4687f4147aae32e819 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 17:35:57 -0500 Subject: [PATCH 10/27] refactor(DataStructure/StateTransition): product answer types via Sum.elim With a match, the answer types of product D E mention D and E through the matcher's parameters, so the elaborator cannot identify clients of two products whose components differ only in admissibility (for example the pair of stacks and the pair of bounded stacks). Sum.elim D.Result E.Result depends only on the component answer types. Queue.Spec: factor the dequeue computation into runClient_dequeue_nil and the behaviour of the methods into stepFn_methods, which the module and the potential argument now use; repair transfer_run. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity/DataStructure/Queue/Spec.lean | 67 ++++++++++++------- Complexity/DataStructure/StateTransition.lean | 9 +-- 2 files changed, 46 insertions(+), 30 deletions(-) diff --git a/Complexity/DataStructure/Queue/Spec.lean b/Complexity/DataStructure/Queue/Spec.lean index 7a0ec18c25..0986e07b05 100644 --- a/Complexity/DataStructure/Queue/Spec.lean +++ b/Complexity/DataStructure/Queue/Spec.lean @@ -28,6 +28,8 @@ method is described in Chapter 17. ## Main results * `transfer_run`: stack operations reverse the rear contents into the front stack. +* `runClient_dequeue_nil`, `stepFn_methods`: the methods realize the FIFO step on the represented + list. * `potential_step`: at most three amortized stack calls, including the size query. * `realized_client`: every adaptive client has a correct backend execution with its bound. * `realized_client_bound`: every supplied linked execution satisfies correctness and the bound. @@ -118,10 +120,40 @@ theorem transfer_run (front back : List α) : induction back generalizing front with | nil => rfl | cons a back ih => - simpa [transfer, stackPair, stack, transferTrace, - List.reverse_cons, List.append_assoc] using congrArg - (fun r : Unit × (stackPair α).State × List (stackPair α).Op => - (r.1, r.2.1, .inr .pop :: .inl (.push a) :: r.2.2)) (ih (a :: front)) + change (let (v, s, os) := (stackPair α).runClient (a :: front, back) (transfer back.length) + (v, s, (Sum.inr StackOp.pop : (stackPair α).Op) :: Sum.inl (StackOp.push a) :: os)) = _ + rw [ih] + simp [transferTrace, List.reverse_cons, List.append_assoc] + +/-- A dequeue from an empty front stack asks for the rear size, transfers the rear, and pops the +front again. -/ +theorem runClient_dequeue_nil (back : List α) : + (stackPair α).runClient ([], back) (methods .dequeue) = + (back.reverse.head?, (back.reverse.tail, []), + .inl .pop :: .inr .size :: (transferTrace back ++ [.inl .pop])) := by + simp only [methods, bind, StateTransitionSpec.runClient_liftBind, stackPair, stack, + List.tail_nil, List.head?_nil] + rw [StateTransitionSpec.runClient_bind, transfer_run, List.append_nil] + rfl + +/-- Each method returns the FIFO answer, and the stacks then represent the FIFO successor of the +represented list. -/ +theorem stepFn_methods (front back : List α) (o : QueueOp α) : + (queue α).stepFn (front ++ back.reverse) o = + (((stackPair α).clientState (front, back) (methods o)).1 ++ + ((stackPair α).clientState (front, back) (methods o)).2.reverse, + (stackPair α).clientResult (front, back) (methods o)) := by + cases o with + | enqueue n => + change ((front ++ back.reverse) ++ [n], ()) = (front ++ (n :: back).reverse, ()) + simp [List.reverse_cons, List.append_assoc] + | dequeue => + cases front with + | cons a front => rfl + | nil => + simp only [StateTransitionSpec.clientState, StateTransitionSpec.clientResult] + rw [runClient_dequeue_nil] + simp /-- Representation and behavior are proved once, before selecting a backend or cost model. -/ def implementation : Module (queue α) (stackPair α) := @@ -134,21 +166,8 @@ def implementation : Module (queue α) (stackPair α) := intro q s o hr _ subst q obtain ⟨front, back⟩ := s - cases o with - | enqueue n => - constructor - · rfl - · change (front ++ back.reverse) ++ [n] = front ++ (n :: back).reverse - simp [List.reverse_cons, List.append_assoc] - | dequeue => - cases front with - | cons a front => exact ⟨rfl, rfl⟩ - | nil => - simp only [methods, bind, StateTransitionSpec.clientResult, - StateTransitionSpec.clientState, StateTransitionSpec.runClient_liftBind, - stackPair, stack, List.tail_nil, List.head?_nil] - rw [StateTransitionSpec.runClient_bind, transfer_run] - simp) + have h := stepFn_methods front back o + exact ⟨(congrArg Prod.snd h).symm, congrArg Prod.fst h⟩) /-- Each transferred element incurs one pop and one push. -/ theorem transferTrace_length (back : List α) : (transferTrace back).length = 2 * back.length := by @@ -173,13 +192,9 @@ theorem potential_step (s : (stackPair α).State) (o : QueueOp α) : change 1 + 2 * back.length ≤ 3 + 2 * back.length omega | nil => - simp only [methods, bind, StateTransitionSpec.runClient_liftBind, stackPair, - stack, List.tail_nil, List.head?_nil] - rw [StateTransitionSpec.runClient_bind, transfer_run] - simp only [StateTransitionSpec.runClient_liftBind, StateTransitionSpec.runClient_pure] - change (transferTrace back ++ [Sum.inl StackOp.pop]).length + 2 ≤ 3 + 2 * back.length - rw [List.length_append, transferTrace_length] - simp only [List.length_singleton] + rw [runClient_dequeue_nil] + simp only [List.length_cons, List.length_append, List.length_nil] + rw [transferTrace_length] omega private theorem weighted_potential_step (K : Nat) {q : (queue α).State} {s : (stackPair α).State} diff --git a/Complexity/DataStructure/StateTransition.lean b/Complexity/DataStructure/StateTransition.lean index 114e38b86c..8c0ae6041b 100644 --- a/Complexity/DataStructure/StateTransition.lean +++ b/Complexity/DataStructure/StateTransition.lean @@ -1098,13 +1098,14 @@ theorem eval_eq_liftM {D : StateTransitionSpec.{u, v, u}} | pure a => rfl | liftBind o k ih => exact ih (D.stepFn s o).2 (D.stepFn s o).1 -/-- Two independent abstract components. A backend must separately justify their independence. -/ +/-- Two independent abstract components. A backend must separately justify their independence. +The answer types are `Sum.elim D.Result E.Result`, so the signature of a product depends only on +the signatures of its components: clients of `product D E` are clients of `product D' E'` when +`D`, `D'` and `E`, `E'` differ only in their states' admissibility. -/ abbrev product (D E : StateTransitionSpec.{u, v, w}) : StateTransitionSpec where State := D.State × E.State Op := D.Op ⊕ E.Op - Result - | .inl o => D.Result o - | .inr o => E.Result o + Result := Sum.elim D.Result E.Result step s | .inl o, s', a => D.step s.1 o s'.1 a ∧ s'.2 = s.2 | .inr o, s', a => s'.1 = s.1 ∧ E.step s.2 o s'.2 a From 412ad374dc88c09a00992d8f8318792accd69144 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 17:42:57 -0500 Subject: [PATCH 11/27] feat(DataStructure): queue over two array stacks over the memory oracle The M1 slice of #2205. Stack.Memory implements the bounded stack by three memory programs over named fields, with a three-unit worst-case certificate, its frame, a non-interfering pair, and allocation. Queue.Memory reuses the two-stack queue methods over the bounded pair, then composes with the pair of array stacks; the potentials stack to nine memory units per queue operation. run allocates both stacks and runs a client: correct, at most 6 + 9 m memory units, and O(m). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 2 + Complexity/DataStructure/Queue/Memory.lean | 181 ++++++++++++++++-- Complexity/DataStructure/Queue/Spec.lean | 30 --- Complexity/DataStructure/Stack/Memory.lean | 18 +- ComplexityTests/DataStructureQueueMemory.lean | 79 ++++++++ 5 files changed, 259 insertions(+), 51 deletions(-) create mode 100644 ComplexityTests/DataStructureQueueMemory.lean diff --git a/Complexity.lean b/Complexity.lean index b6a10c1394..8599d2709f 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2902,6 +2902,7 @@ public import Complexity.DataStructure.PriorityQueue.WordRAM.FibonacciHeap.MinPQ public import Complexity.DataStructure.PriorityQueue.WordRAM.FibonacciHeap.Model public import Complexity.DataStructure.PriorityQueue.WordRAM.FibonacciHeap.Program public import Complexity.DataStructure.PriorityQueue.WordRAM.FibonacciHeap.Trees +public import Complexity.DataStructure.Queue.Memory public import Complexity.DataStructure.Queue.Spec public import Complexity.DataStructure.Queue.WordRAM.Basic public import Complexity.DataStructure.Queue.WordRAM.Dynamic @@ -2914,6 +2915,7 @@ public import Complexity.DataStructure.SearchTree.Impl public import Complexity.DataStructure.SplayTree.AccessLemma public import Complexity.DataStructure.SplayTree.Basic public import Complexity.DataStructure.SplayTree.Impl +public import Complexity.DataStructure.Stack.Memory public import Complexity.DataStructure.Stack.WordRAM.Basic public import Complexity.DataStructure.Stack.WordRAM.Controller public import Complexity.DataStructure.StateTransition diff --git a/Complexity/DataStructure/Queue/Memory.lean b/Complexity/DataStructure/Queue/Memory.lean index 28a37a7a87..d3fa6805cf 100644 --- a/Complexity/DataStructure/Queue/Memory.lean +++ b/Complexity/DataStructure/Queue/Memory.lean @@ -27,24 +27,28 @@ proved once: `memRefinement` composes them, and `mem_amortized` stacks the potentials across the composition (`Refinement.Amortized.comp`): every queue operation costs at most nine memory units, amortized, -with six credits per rear element. `run` allocates the two stacks and then runs a queue client; -`run_correct` and `run_cost` state its result and its exact memory cost from any well-formed memory, -and `run_cost_isBigO` exports the cost as `O(m)` in the number `m` of queue operations. +with six credits per rear element. `run` allocates the two stacks and then runs a queue client. +From any memory with room for both stacks, `run_correct` gives its answer and the admissibility of +its memory operations, `run_cost` bounds its memory cost by `6 + 9 m` for `m` queue operations, +and `run_cost_isBigO` exports that bound as `O(m)`. The machine-level counterpart is `Queue.WordRAM`, whose dispatcher is hand-stepped instruction by -instruction. Here the proofs mention neither registers nor instructions; executing the memory -programs on the word RAM is the separate realization step of #2205. +instruction. Here the proofs mention neither registers nor instructions. Executing the memory +programs on the word RAM is a separate, generic realization step, which this file does not +provide. ## Main definitions * `boundedStacks`: the pair of bounded stacks. * `Inv`: a FIFO list within capacity holding words. * `refinement`: the two-stack queue over the pair of bounded stacks. -* `memRefinement`: the queue over two array stacks over memory. +* `memRefinement`, `memMethods`: the queue over two array stacks over memory, and its methods as + plain memory programs. * `run`: allocate the stacks, then run a queue client. ## Main results +* `runClient_boundedStacks`: bounded stacks run clients exactly as unbounded ones. * `amortized`: three stack calls per queue operation, with two credits per rear element. * `mem_amortized`: nine memory units per queue operation, with six credits per rear element. * `run_correct`, `run_cost`: the allocated queue answers like the specification and costs at @@ -105,10 +109,29 @@ theorem runClient_boundedStacks {β : Type} (s : List ℕ × List ℕ) (p : (sta have hstep : (boundedStacks w C).stepFn s o = (stackPair ℕ).stepFn s o := by cases o <;> rfl simp only [StateTransitionSpec.runClient_liftBind, hstep, ih] +/-- The final state of a client of the bounded stacks. -/ +theorem clientState_boundedStacks {β : Type} (s : List ℕ × List ℕ) + (p : (stackPair ℕ).Client β) : + (boundedStacks w C).clientState s p = (stackPair ℕ).clientState s p := by + simp only [StateTransitionSpec.clientState, runClient_boundedStacks] + +/-- The result of a client of the bounded stacks. -/ +theorem clientResult_boundedStacks {β : Type} (s : List ℕ × List ℕ) + (p : (stackPair ℕ).Client β) : + (boundedStacks w C).clientResult s p = (stackPair ℕ).clientResult s p := by + simp only [StateTransitionSpec.clientResult, runClient_boundedStacks] + +/-- The history of a client of the bounded stacks. -/ +theorem clientHistory_boundedStacks {β : Type} (s : List ℕ × List ℕ) + (p : (stackPair ℕ).Client β) : + (boundedStacks w C).clientHistory s p = (stackPair ℕ).clientHistory s p := by + simp only [StateTransitionSpec.clientHistory, runClient_boundedStacks] + /-- Moving the rear into the front is admissible for bounded stacks when both fit together and the rear holds words. -/ theorem validHistory_transferTrace {front back : List ℕ} (hlen : front.length + back.length ≤ C) - (hfit : ∀ x ∈ back, Fits w x) : (boundedStacks w C).ValidHistory (front, back) (transferTrace back) := by + (hfit : ∀ x ∈ back, Fits w x) : + (boundedStacks w C).ValidHistory (front, back) (transferTrace back) := by induction back generalizing front with | nil => trivial | cons a back ih => @@ -129,14 +152,15 @@ def refinement (w C : ℕ) : Refinement (Queue.WordRAM.spec w C) (boundedStacks | enqueue n => obtain ⟨hq, hfit⟩ := hv simp only [Queue.WordRAM.contents, List.length_append, List.length_reverse] at hq - exact ⟨⟨by omega, hfit⟩, trivial⟩ + refine ⟨⟨?_, hfit⟩, trivial⟩ + change back.length < C + omega | dequeue => cases front with | cons a front => exact ⟨trivial, trivial⟩ | nil => - change (boundedStacks w C).ValidHistory ([], back) - ((boundedStacks w C).runClient ([], back) (methods .dequeue)).2.2 - rw [runClient_boundedStacks, runClient_dequeue_nil] + rw [clientHistory_boundedStacks, StateTransitionSpec.clientHistory, + runClient_dequeue_nil] refine ⟨trivial, trivial, ?_⟩ rw [StateTransitionSpec.validHistory_append] refine ⟨validHistory_transferTrace (by simpa using hlen) fun x hx => @@ -144,6 +168,7 @@ def refinement (w C : ℕ) : Refinement (Queue.WordRAM.spec w C) (boundedStacks (by rintro ⟨front, back⟩ o hs hv have h := stepFn_methods front back o + rw [clientState_boundedStacks, clientResult_boundedStacks] refine ⟨h, ?_⟩ have hstep := hs.step hv rw [show Queue.WordRAM.contents (front, back) = front ++ back.reverse from rfl, h] at hstep @@ -155,8 +180,10 @@ theorem amortized : (refinement w C).Amortized (fun _ _ => 1) (fun _ _ => 3) fun s => 2 * s.2.length := Refinement.Amortized.ofFn fun {s} o _ _ => by have h := potential_step s o - rw [← runClient_boundedStacks (w := w) (C := C)] at h - rw [StateTransitionSpec.totalCost_one] + change (boundedStacks w C).totalCost (fun _ _ => 1) s + ((boundedStacks w C).clientHistory s (methods o)) + + 2 * ((boundedStacks w C).clientState s (methods o)).2.length ≤ 3 + 2 * s.2.length + rw [StateTransitionSpec.totalCost_one, clientHistory_boundedStacks, clientState_boundedStacks] exact h /-! ## Over memory -/ @@ -182,4 +209,132 @@ theorem mem_amortized {L₁ L₂ : Stack.Memory.Layout} (h₁ : L₁.Valid w C) omega exact hΦ ▸ h +/-! ## Allocation and a whole run -/ + +/-- The queue methods over two array stacks, as plain memory programs. -/ +def memMethods (L₁ L₂ : Stack.Memory.Layout) (w C : ℕ) : + Methods (Queue.WordRAM.spec w C) (Memory.spec w) := + fun o => link (Stack.Memory.pairMethods L₁ L₂ w C) (methods o) + +/-- The composite's methods are `memMethods`, whatever the proofs about the layouts. -/ +theorem memRefinement_methods {L₁ L₂ : Stack.Memory.Layout} (h₁ : L₁.Valid w C) + (h₂ : L₂.Valid w C) (hd : Disjoint L₁.footprint L₂.footprint) : + (memRefinement h₁ h₂ hd).methods = memMethods L₁ L₂ w C := by + funext o + change link (Stack.Memory.pairRefinement h₁ h₂ hd).methods (methods o) = _ + rw [Stack.Memory.pairRefinement_methods] + rfl + +/-- Allocate two empty stacks of capacity `C`, then run a queue client on them. -/ +def run (w C : ℕ) {β : Type} (p : (Queue.WordRAM.spec w C).Client β) : Prog β := do + let L₁ ← Stack.Memory.allocLayout C + let L₂ ← Stack.Memory.allocLayout C + link (memMethods L₁ L₂ w C) p + +/-- The first stack allocated from `s`. -/ +abbrev frontLayout (s : Memory.State) (C : ℕ) : Stack.Memory.Layout := + ⟨⟨s.frontier, 1⟩, ⟨s.frontier + 1, C⟩⟩ + +/-- The second stack allocated from `s`. -/ +abbrev rearLayout (s : Memory.State) (C : ℕ) : Stack.Memory.Layout := + ⟨⟨s.frontier + 1 + C, 1⟩, ⟨s.frontier + 1 + C + 1, C⟩⟩ + +/-- The memory after both allocations. -/ +abbrev allocated (s : Memory.State) (C : ℕ) : Memory.State := + ⟨clear (clear s.mem s.frontier 1) (s.frontier + 1 + C) 1, s.frontier + 1 + C + 1 + C⟩ + +/-- A run allocates both stacks, then runs the linked client from the allocated memory. -/ +theorem runClient_run {β : Type} (p : (Queue.WordRAM.spec w C).Client β) (s : Memory.State) : + (Memory.spec w).runClient s (run w C p) = + let (v, s', os) := (Memory.spec w).runClient (allocated s C) + (link (memMethods (frontLayout s C) (rearLayout s C) w C) p) + (v, s', .alloc 1 :: .allocDirty C :: .alloc 1 :: .allocDirty C :: os) := rfl + +/-- The facts that start the refinement after allocation. -/ +private theorem setup (s : Memory.State) (hfit : Fits w (s.frontier + 2 * (C + 1))) : + ∃ (h₁ : (frontLayout s C).Valid w C) (h₂ : (rearLayout s C).Valid w C) + (hd : Disjoint (frontLayout s C).footprint (rearLayout s C).footprint), + (memRefinement h₁ h₂ hd).inv (allocated s C) ∧ + (memRefinement h₁ h₂ hd).abs (allocated s C) = [] ∧ + Stack.Memory.abs (rearLayout s C) (allocated s C) = [] := by + have h₁ : (frontLayout s C).Valid w C := Stack.Memory.valid_allocLayout (hfit.of_le (by omega)) + have h₂ : (rearLayout s C).Valid w C := Stack.Memory.valid_allocLayout (hfit.of_le (by omega)) + have hd : Disjoint (frontLayout s C).footprint (rearLayout s C).footprint := by + rw [Finset.disjoint_left] + intro a ha hb + simp only [Stack.Memory.Layout.footprint, Finset.mem_union, Block.mem_footprint, + Block.Owns] at ha hb + omega + -- The first stack, as allocated, before the second allocation. + obtain ⟨hi₁, ha₁⟩ := Stack.Memory.inv_allocLayout (C := C) s + -- The second stack, allocated from the state after the first. + obtain ⟨hi₂, ha₂⟩ := Stack.Memory.inv_allocLayout (C := C) + ⟨clear s.mem s.frontier 1, s.frontier + 1 + C⟩ + -- The second allocation leaves the first stack's cells alone. + have hframe : ∀ a ∈ (frontLayout s C).footprint, + (allocated s C).mem a = clear s.mem s.frontier 1 a := by + intro a ha + simp only [Stack.Memory.Layout.footprint, Finset.mem_union, Block.mem_footprint, + Block.Owns] at ha + exact clear_of_not_owns (by simp only [Block.Owns]; omega) + have hi₁' := hi₁.congr h₁ hframe (by simp only; omega) + have ha₁' := (Stack.Memory.abs_congr h₁ hi₁ hframe).trans ha₁ + refine ⟨h₁, h₂, hd, ⟨⟨hi₁', hi₂⟩, ?_⟩, ?_, ha₂⟩ + · change Inv w C (Queue.WordRAM.contents + (Stack.Memory.abs (frontLayout s C) (allocated s C), + Stack.Memory.abs (rearLayout s C) (allocated s C))) + rw [ha₁', ha₂] + exact ⟨by simp [Queue.WordRAM.contents], by simp [Queue.WordRAM.contents]⟩ + · change Queue.WordRAM.contents + (Stack.Memory.abs (frontLayout s C) (allocated s C), + Stack.Memory.abs (rearLayout s C) (allocated s C)) = [] + rw [ha₁', ha₂] + rfl + +/-- **Correctness.** From any memory with room for both stacks, an admissible queue client run +over two freshly allocated array stacks issues only admissible memory operations and returns the +specified answer. -/ +theorem run_correct {β : Type} (p : (Queue.WordRAM.spec w C).Client β) {s : Memory.State} + (hfit : Fits w (s.frontier + 2 * (C + 1))) + (hv : (Queue.WordRAM.spec w C).ValidHistory [] ((Queue.WordRAM.spec w C).clientHistory [] p)) : + (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s (run w C p)) ∧ + (Memory.spec w).clientResult s (run w C p) = (Queue.WordRAM.spec w C).clientResult [] p := by + obtain ⟨h₁, h₂, hd, hinv, habs, -⟩ := setup s hfit + obtain ⟨hvE, hres, -, -⟩ := + (memRefinement h₁ h₂ hd).simulates_client p hinv (by rw [habs]; exact hv) + rw [memRefinement_methods] at hvE hres + simp only [StateTransitionSpec.clientHistory, StateTransitionSpec.clientResult, + runClient_run] at hvE hres ⊢ + simp only [StateTransitionSpec.validHistory_cons, StateTransitionSpec.stepFn, step_alloc, + step_allocDirty, valid_alloc, valid_allocDirty] + refine ⟨⟨hfit.of_le (by omega), hfit.of_le (by omega), hfit.of_le (by omega), + hfit.of_le (by omega), hvE⟩, hres.trans (by rw [habs])⟩ + +/-- **Cost.** The same run costs at most `6 + 9 m` memory units for a client of `m` queue +operations: six for the two allocations and nine per operation, amortized. -/ +theorem run_cost {β : Type} (p : (Queue.WordRAM.spec w C).Client β) {s : Memory.State} + (hfit : Fits w (s.frontier + 2 * (C + 1))) + (hv : (Queue.WordRAM.spec w C).ValidHistory [] ((Queue.WordRAM.spec w C).clientHistory [] p)) : + (Memory.spec w).totalCost charge s ((Memory.spec w).clientHistory s (run w C p)) ≤ + 6 + 9 * ((Queue.WordRAM.spec w C).clientHistory [] p).length := by + obtain ⟨h₁, h₂, hd, hinv, habs, hrear⟩ := setup s hfit + have h := (mem_amortized h₁ h₂ hd).client p hinv (by rw [habs]; exact hv) + rw [memRefinement_methods, habs, StateTransitionSpec.totalCost_const, hrear] at h + simp only [StateTransitionSpec.clientHistory, runClient_run, + StateTransitionSpec.totalCost_cons, charge_alloc, charge_allocDirty, + StateTransitionSpec.stepFn, step_alloc, step_allocDirty] at h ⊢ + simp only [List.length_nil, Nat.mul_zero, Nat.add_zero, allocated] at h ⊢ + omega + +/-- **The exported form.** Over admissible clients, the memory cost of a run is `O(m)` in the +number `m` of queue operations. -/ +theorem run_cost_isBigO (s : Memory.State) (hfit : Fits w (s.frontier + 2 * (C + 1))) : + CostIsBigO Filter.atTop (fun m : ℕ => (m : ℝ)) + (fun p : {p : (Queue.WordRAM.spec w C).Client Unit // + (Queue.WordRAM.spec w C).ValidHistory [] ((Queue.WordRAM.spec w C).clientHistory [] p)} => + ((Queue.WordRAM.spec w C).clientHistory [] p.1).length) + (fun p => (Memory.spec w).totalCost charge s + ((Memory.spec w).clientHistory s (run w C p.1))) := + CostIsBigO.of_le_add_mul 6 9 fun p => run_cost p.1 hfit p.2 + end Cslib.DataStructure.Queue.Memory diff --git a/Complexity/DataStructure/Queue/Spec.lean b/Complexity/DataStructure/Queue/Spec.lean index a420660819..0986e07b05 100644 --- a/Complexity/DataStructure/Queue/Spec.lean +++ b/Complexity/DataStructure/Queue/Spec.lean @@ -155,36 +155,6 @@ theorem stepFn_methods (front back : List α) (o : QueueOp α) : rw [runClient_dequeue_nil] simp -/-- A dequeue from an empty front stack asks for the rear size, transfers the rear, and pops the -front again. -/ -theorem runClient_dequeue_nil (back : List α) : - (stackPair α).runClient ([], back) (methods .dequeue) = - (back.reverse.head?, (back.reverse.tail, []), - .inl .pop :: .inr .size :: (transferTrace back ++ [.inl .pop])) := by - simp only [methods, bind, StateTransitionSpec.runClient_liftBind, stackPair, stack, - List.tail_nil, List.head?_nil] - rw [StateTransitionSpec.runClient_bind, transfer_run] - simp - -/-- Each method returns the FIFO answer, and the stacks then represent the FIFO successor of the -represented list. -/ -theorem stepFn_methods (front back : List α) (o : QueueOp α) : - (queue α).stepFn (front ++ back.reverse) o = - (((stackPair α).clientState (front, back) (methods o)).1 ++ - ((stackPair α).clientState (front, back) (methods o)).2.reverse, - (stackPair α).clientResult (front, back) (methods o)) := by - cases o with - | enqueue n => - change ((front ++ back.reverse) ++ [n], ()) = (front ++ (n :: back).reverse, ()) - simp [List.reverse_cons, List.append_assoc] - | dequeue => - cases front with - | cons a front => rfl - | nil => - simp only [StateTransitionSpec.clientState, StateTransitionSpec.clientResult] - rw [runClient_dequeue_nil] - simp - /-- Representation and behavior are proved once, before selecting a backend or cost model. -/ def implementation : Module (queue α) (stackPair α) := Module.ofFn diff --git a/Complexity/DataStructure/Stack/Memory.lean b/Complexity/DataStructure/Stack/Memory.lean index 8f5069fe44..cf79d42258 100644 --- a/Complexity/DataStructure/Stack/Memory.lean +++ b/Complexity/DataStructure/Stack/Memory.lean @@ -17,9 +17,9 @@ public import Complexity.DataStructure.Stack.WordRAM.Basic The bounded stack `Stack.WordRAM.spec w C` is implemented by three short programs over `Memory.spec w`. The storage is a `Layout` with two named fields: a one-cell `count` and an `items` array of `C` cells, bottom first. The abstraction function reads the first `count` items -and reverses them, so the top comes first. Each operation is one read, two writes at most, and a -single branch; its evaluation is definitional (`runClient_push`, `runClient_pop_of_pos`, …), and -the refinement obligations follow from those equations and the list view of the array. +and reverses them, so the top comes first. Each operation is at most three memory operations and +one branch. Its evaluation is definitional (`runClient_push`, `runClient_pop_of_pos`, …), and the +refinement obligations follow from those equations and the list view of the array. Every operation costs at most three memory units, with no potential (`amortized`). The methods write only the layout's cells and never allocate (`modifies_methods`), and the abstraction and @@ -27,8 +27,8 @@ invariant depend only on those cells (`abs_congr`, `Inv.congr`). Two stacks with layouts therefore implement the product of two stacks (`pairRefinement`). This is the same specification that `Stack.WordRAM` implements by a hand-stepped nine-register -dispatcher. Here no register, instruction count or word-fit chain appears; fits enter only -through the admissibility of `Memory.spec w`. +dispatcher. Here no register or instruction count appears. Word fit enters only through the +admissibility of `Memory.spec w` and one static fact of the layout, that the capacity fits. ## Main definitions @@ -46,9 +46,11 @@ through the admissibility of `Memory.spec w`. * `amortized`: every operation costs at most three memory units. * `pair_amortized`: so does every operation of the pair. * `pairRefinement_methods`: the pair's methods do not depend on the proofs about its layouts. -* `modifies_methods`, `abs_congr`, `Inv.congr`: the frame of a stack. -* `runClient_allocLayout_bind`, `inv_allocLayout`, `abs_allocLayout`: allocation yields an empty - stack in three memory units. +* `runClient_push`, `runClient_pop_of_eq_zero`, `runClient_pop_of_pos`, `runClient_size`: the + operations evaluate definitionally. +* `modifies_methods`, `abs_congr`, `Inv.congr`, `frame`: the frame of a stack. +* `runClient_allocLayout_bind`, `valid_allocLayout`, `inv_allocLayout`: allocation yields a + well-formed empty stack in three memory units. ## References diff --git a/ComplexityTests/DataStructureQueueMemory.lean b/ComplexityTests/DataStructureQueueMemory.lean new file mode 100644 index 0000000000..93a8793360 --- /dev/null +++ b/ComplexityTests/DataStructureQueueMemory.lean @@ -0,0 +1,79 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: local-only (test driver) + +public import Complexity.DataStructure.Queue.Memory + +/-! +# A queue over two array stacks, run on concrete memory + +A client enqueues three words and dequeues four times; the third dequeue empties the rear stack +into the front. The run allocates both stacks in empty memory at word size `8` with capacity `3`. +The tests check its answers against the queue specification, its exact memory cost against the +proved bound `6 + 9 m`, the admissibility of every memory operation, and that the general +theorems apply: their room and admissibility hypotheses hold here. +-/ + +@[expose] public section + +namespace ComplexityTests.DataStructureQueueMemory + +open Cslib.WordRAM Cslib.DataStructure Cslib.DataStructure.Memory Cslib.DataStructure.Queue + Cslib.DataStructure.Queue.Memory + +/-- Enqueue `1, 2`, dequeue, enqueue `3`, then dequeue three times. -/ +def client : (Queue.WordRAM.spec 8 3).Client (List (Option ℕ)) := do + (Queue.WordRAM.spec 8 3).call (.enqueue 1) + (Queue.WordRAM.spec 8 3).call (.enqueue 2) + let a ← (Queue.WordRAM.spec 8 3).call .dequeue + (Queue.WordRAM.spec 8 3).call (.enqueue 3) + let b ← (Queue.WordRAM.spec 8 3).call .dequeue + let c ← (Queue.WordRAM.spec 8 3).call .dequeue + let d ← (Queue.WordRAM.spec 8 3).call .dequeue + pure [a, b, c, d] + +/-- Empty memory with nothing allocated. -/ +def empty : State := ⟨fun _ => 0, 0⟩ + +/-- FIFO answers, and an empty queue at the end. -/ +example : (Queue.WordRAM.spec 8 3).clientResult [] client = [some 1, some 2, some 3, none] := by + decide + +/-- The run over memory gives the same answers. -/ +example : (spec 8).clientResult empty (run 8 3 client) = [some 1, some 2, some 3, none] := by + decide + +/-- Its exact memory cost: 6 for the allocations and 43 for the seven queue operations, against +the proved bound `6 + 9 · 7 = 69`. The dequeue that moves two elements costs 17 alone. -/ +example : (spec 8).totalCost charge empty ((spec 8).clientHistory empty (run 8 3 client)) = 49 := by + decide + +/-- Every memory operation of the run is admissible. -/ +example : (spec 8).ValidHistory empty ((spec 8).clientHistory empty (run 8 3 client)) := by + decide + +/-- The client is admissible for the bounded queue. -/ +theorem client_valid : + (Queue.WordRAM.spec 8 3).ValidHistory [] ((Queue.WordRAM.spec 8 3).clientHistory [] client) := + ⟨⟨by decide, by decide⟩, ⟨by decide, by decide⟩, trivial, ⟨by decide, by decide⟩, trivial, + trivial, trivial, trivial⟩ + +/-- The general theorems apply: empty memory has room for both stacks at this word size. -/ +theorem room : Fits 8 (empty.frontier + 2 * (3 + 1)) := by decide + +example : (spec 8).clientResult empty (run 8 3 client) = + (Queue.WordRAM.spec 8 3).clientResult [] client := + (run_correct client room client_valid).2 + +example : (spec 8).totalCost charge empty ((spec 8).clientHistory empty (run 8 3 client)) ≤ 69 := + run_cost client room client_valid + +/-- A memory with too little room: the second stack would end past the 4-bit word range. -/ +example : ¬Fits 4 (empty.frontier + 2 * (7 + 1)) := by decide + +end ComplexityTests.DataStructureQueueMemory From 4283b8fb714450e78ba170865bcb54ce8563bce3 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 17:51:55 -0500 Subject: [PATCH 12/27] feat(WordRAM): certify compositional register-word glue Add one framed register-word certificate over existing Code, with visible admissibility and fit envelopes, uniform costs, finite computed-argument substitution, and lazy selection. Keep exact-basis definedness explicit. Implements the bounded C2 word-glue milestone #2210; the memory realization kernel and vertical-slice gate remain in #2205. Validation: final staged tree a1b88e3fd1cc5e72a9a105865df17fa04295710e passed scripts/check.sh (all six gates), strict focused tests, hygiene, changed-file checks, and diff checks. No selected abstract/PDF impact. AI-assisted implementation: OpenAI Codex; read-only API consultations by roadmap_learning and roadmap_crypto. Independent outside review pending. --- Complexity.lean | 1 + Complexity/Computability/WordRAM/Glue.lean | 724 +++++++++++++++++++++ ComplexityTests/WordRAMGlue.lean | 172 +++++ ComplexityTests/WordRAMGlueNamed.lean | 43 ++ 4 files changed, 940 insertions(+) create mode 100644 Complexity/Computability/WordRAM/Glue.lean create mode 100644 ComplexityTests/WordRAMGlue.lean create mode 100644 ComplexityTests/WordRAMGlueNamed.lean diff --git a/Complexity.lean b/Complexity.lean index 7f9a77746d..53856ab81b 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2517,6 +2517,7 @@ public import Complexity.Computability.WordRAM.Dynamic public import Complexity.Computability.WordRAM.ExactNotation public import Complexity.Computability.WordRAM.Function public import Complexity.Computability.WordRAM.GhostLoop +public import Complexity.Computability.WordRAM.Glue public import Complexity.Computability.WordRAM.Heap public import Complexity.Computability.WordRAM.InputArray public import Complexity.Computability.WordRAM.Invariance diff --git a/Complexity/Computability/WordRAM/Glue.lean b/Complexity/Computability/WordRAM/Glue.lean new file mode 100644 index 0000000000..f022c95b6b --- /dev/null +++ b/Complexity/Computability/WordRAM/Glue.lean @@ -0,0 +1,724 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Structured +import Mathlib.Data.Fintype.EquivFin +import Mathlib.Data.List.FinRange + +/-! +# Compositional certificates for register-word glue + +This module certifies finite, memory-independent glue in the existing `WordRAM.Code` language. +An emitter takes possibly repeated input registers and a disjoint fresh scratch bank. Its code +and instruction budget are chosen before the input values and word width. This is the +emit-and-frame interface used by `Multiword.Circuit`, specialized to physical words; it does not +use that compiler's standard-basis wide-arithmetic semantics for exact-basis operations. + +The admissible-input predicate and one envelope for executed values remain explicit. In +particular, increasing the width does not justify exact subtraction below zero or a zero +divisor. Selection runs only its chosen alternative. Width reads, memory instructions, loops +and random sampling are excluded. This is the word-glue component of realization, not a +compiler for memory clients or algorithms. The certificate asserts successful execution on its +stated domain; it does not assert a converse about executions outside that domain. + +Inputs are already in the supplied registers. Their installation, scratch reservation and a +final halt are not charged here; the surrounding realization must charge them. Constants, +copies, ALU instructions and branch overhead inside the emitted code are charged normally. +The interface and closure results are original; the underlying model is `WordRAM.Structured`. + +## Main definitions + +* `Code.IsGlue`: the memory-independent, width-independent finite fragment of existing code. +* `GlueComputes`: a framed scalar computation with an explicit uniform instruction budget. +* `GlueComputable`: the same contract with only the instruction budget existentially hidden. + +## Main results + +* `GlueComputes.proj`, `GlueComputes.const`: register projections and constants. +* `GlueComputes.binary`, `GlueComputes.comp_many`, `GlueComputes.select`: compositional emission. +* `GlueComputes.realize`: instantiate the emitter with a concrete finite register layout. +* `GlueComputes.mono`: explicit weakening of the value envelope and instruction budget. +-/ + +public section + +namespace Cslib.WordRAM + +variable {B : Basis} {k r : ℕ} + +/-- The register-only finite fragment of `Code`. Conditions may select a branch, but no loop, +memory access, random choice or word-width read is permitted. This is a predicate on the +existing language, not a second program syntax. -/ +@[expose] def Code.IsGlue : Code B r → Prop + | .op (.const _ _) | .op (.copy _ _) => True + | .op (.alu f _ _) => ¬B.ReadsWidth f + | .seq a b | .branch _ a b => a.IsGlue ∧ b.IsGlue + | _ => False + +/-- Register glue does not read the word width, so existing execution monotonicity applies. -/ +theorem Code.IsGlue.not_readsWidth {c : Code B r} (h : c.IsGlue) : ¬c.ReadsWidth := by + induction c with + | op o => cases o <;> simp_all [Code.IsGlue, Code.ReadsWidth, Op.ReadsWidth] + | seq a b ha hb | branch cond a b ha hb => + exact fun hc => hc.elim (ha h.1) (hb h.2) + | loop | rand => exact h.elim + +/-- A scalar register function has a fixed emitter and instruction budget. `bound` is one +top-level value envelope, and `P` is the fixed admissibility predicate. The scratch bank is +fresh and injectively placed; input registers may repeat. Output is in scratch slot zero, +memory is unchanged, and registers outside scratch retain their initial contents. Neither the +emitter nor the budget may depend on run-time values or the word width. -/ +@[expose] def GlueComputes (B : Basis) (P : (Fin k → ℕ) → Prop) + (f bound : (Fin k → ℕ) → ℕ) + (time : ℕ) : Prop := + (∀ v, P v → f v ≤ bound v) ∧ + ∃ n, ∃ emit : ∀ {r : ℕ}, (Fin k → Fin r) → (Fin (n + 1) → Fin r) → Code B r, + ∀ {r : ℕ} (src : Fin k → Fin r) (tmp : Fin (n + 1) → Fin r), + Function.Injective tmp → (∀ i j, src i ≠ tmp j) → + (emit src tmp).IsGlue ∧ + ∀ (w : ℕ) (x : Data r), P (x.regs ∘ src) → Fits w (bound (x.regs ∘ src)) → + ∃ y, ExecWithin w (emit src tmp) x y time ∧ + y.regs (tmp 0) = f (x.regs ∘ src) ∧ y.mem = x.mem ∧ + ∀ i, (∀ j, tmp j ≠ i) → y.regs i = x.regs i + +/-- Syntax-directed automation hides only the uniform budget and emitter witness. The basis, +admissibility predicate, scalar function and value envelope remain visible parameters. -/ +@[expose, fun_prop] +def GlueComputable (B : Basis) (f : (Fin k → ℕ) → ℕ) (P : (Fin k → ℕ) → Prop) + (bound : (Fin k → ℕ) → ℕ) : Prop := + ∃ time, GlueComputes B P f bound time + +namespace GlueComputes + +variable {P : (Fin k → ℕ) → Prop} {f bound bound' : (Fin k → ℕ) → ℕ} {time time' : ℕ} + +/-- A checked explicit budget supplies the automation facade. -/ +theorem computable (h : GlueComputes B P f bound time) : GlueComputable B f P bound := + ⟨time, h⟩ + +/-- A canonical finite register layout puts inputs first and fresh scratch after them. The +same code and register count work for every valid input and width, and every initial memory +is framed. The budget excludes a final halt; a later flat-program export must charge it. -/ +theorem realize (h : GlueComputes B P f bound time) : + ∃ n, ∃ code : Code B (k + (n + 1)), code.IsGlue ∧ + ∀ (w : ℕ) (v : Fin k → ℕ) (mem : ℕ → ℕ), P v → Fits w (bound v) → + ∃ y, ExecWithin w code ⟨Fin.addCases v (fun _ => 0), mem⟩ y time ∧ + y.regs (Fin.natAdd k 0) = f v ∧ y.mem = mem ∧ + ∀ i, y.regs (Fin.castAdd (n + 1) i) = v i := by + obtain ⟨_, n, emit, he⟩ := h + have hi : Function.Injective (Fin.natAdd k : Fin (n + 1) → Fin (k + (n + 1))) := by + intro i j h + apply Fin.ext + have := congrArg Fin.val h + simp only [Fin.val_natAdd] at this + omega + have hd (i : Fin k) (j : Fin (n + 1)) : Fin.castAdd (n + 1) i ≠ Fin.natAdd k j := by + intro h + have := congrArg Fin.val h + simp only [Fin.val_castAdd, Fin.val_natAdd] at this + omega + obtain ⟨hg, hrun⟩ := he (Fin.castAdd (n + 1)) (Fin.natAdd k) hi hd + refine ⟨n, emit (Fin.castAdd (n + 1)) (Fin.natAdd k), hg, ?_⟩ + intro w v mem hv hw + let x : Data (k + (n + 1)) := ⟨Fin.addCases v (fun _ => 0), mem⟩ + have hxs : x.regs ∘ Fin.castAdd (n + 1) = v := by + funext i + change Fin.addCases v (fun _ : Fin (n + 1) => 0) (Fin.castAdd (n + 1) i) = v i + exact Fin.addCases_left i + obtain ⟨y, hy, hyo, hym, hyf⟩ := hrun w x (hxs.symm ▸ hv) (hxs.symm ▸ hw) + refine ⟨y, hy, hxs ▸ hyo, hym, ?_⟩ + intro i + exact (hyf _ (fun j => (hd i j).symm)).trans (congrFun hxs i) + +/-- Weaken the envelope and instruction budget explicitly, outside syntax-directed search. -/ +theorem mono (h : GlueComputes B P f bound time) + (hb : ∀ v, P v → bound v ≤ bound' v) (ht : time ≤ time') : + GlueComputes B P f bound' time' := by + obtain ⟨hf, n, emit, he⟩ := h + refine ⟨fun v hv => (hf v hv).trans (hb v hv), n, emit, ?_⟩ + intro r src tmp hi hd + obtain ⟨hg, hrun⟩ := he src tmp hi hd + refine ⟨hg, ?_⟩ + intro w x hx hw + obtain ⟨y, hy, ho, hm, hf⟩ := hrun w x hx (hw.of_le (hb _ hx)) + exact ⟨y, hy.mono ht, ho, hm, hf⟩ + +/-- Copy one input register. Its bound is checked against the common visible envelope. -/ +theorem proj (i : Fin k) (hb : ∀ v, P v → v i ≤ bound v) : + GlueComputes B P (fun v => v i) bound 1 := by + refine ⟨hb, 0, fun src tmp => .op (.copy (tmp 0) (src i)), ?_⟩ + intro r src tmp hi hd + refine ⟨trivial, ?_⟩ + intro w x hx hw + refine ⟨x.set (tmp 0) (x.regs (src i)), ExecWithin.copy, ?_, rfl, ?_⟩ + · simp [Data.set] + · intro j hj + exact Function.update_of_ne (hj 0).symm _ _ + +/-- Write a fitting constant. -/ +theorem const (z : ℕ) (hb : ∀ v, P v → z ≤ bound v) : + GlueComputes B P (fun _ => z) bound 1 := by + refine ⟨hb, 0, fun _ tmp => .op (.const (tmp 0) z), ?_⟩ + intro r src tmp hi hd + refine ⟨trivial, ?_⟩ + intro w x hx hw + refine ⟨x.set (tmp 0) z, ExecWithin.const (hw.of_le (hb _ hx)), ?_, rfl, ?_⟩ + · simp [Data.set] + · intro j hj + exact Function.update_of_ne (hj 0).symm _ _ + +private def left {a b : ℕ} (j : Fin (a + 1)) : Fin (a + b + 3) := + ⟨j.val + 1, by omega⟩ + +private def right {a b : ℕ} (j : Fin (b + 1)) : Fin (a + b + 3) := + ⟨j.val + a + 2, by omega⟩ + +private theorem left_injective {a b : ℕ} : Function.Injective (@left a b) := by + intro i j h + apply Fin.ext + have := congrArg Fin.val h + simp only [left] at this + omega + +private theorem right_injective {a b : ℕ} : Function.Injective (@right a b) := by + intro i j h + apply Fin.ext + have := congrArg Fin.val h + simp only [right] at this + omega + +private theorem left_ne_right {a b : ℕ} (i : Fin (a + 1)) (j : Fin (b + 1)) : + left i ≠ right j := by + intro h + have := congrArg Fin.val h + simp only [left, right] at this + omega + +/-- Apply a basis operation to two computed words. The operation may repeat or ignore either +word in its argument vector. Its interpretation and domain are checked explicitly, at every +width; no totalization of a partial basis operation is used. Intermediate executions share the +same envelope, and their instruction costs are added without bound search. -/ +theorem binary {f g h : (Fin k → ℕ) → ℕ} {tf tg : ℕ} + (hf : GlueComputes B P f bound tf) (hg : GlueComputes B P g bound tg) + (op : B.Op) (args : Fin (B.Arity op) → Fin 2) (hop : ¬B.ReadsWidth op) + (heval : ∀ v, P v → ∀ w, B.interp w op (fun i => ![f v, g v] (args i)) = some (h v)) + (hb : ∀ v, P v → h v ≤ bound v) : + GlueComputes B P h bound (tf + tg + 1) := by + obtain ⟨_, a, ea, ha⟩ := hf + obtain ⟨_, b, eb, hg⟩ := hg + let emit : ∀ {r : ℕ}, (Fin k → Fin r) → (Fin (a + b + 2 + 1) → Fin r) → Code B r := + fun src tmp => .seq (ea src (tmp ∘ left)) + (.seq (eb src (tmp ∘ right)) + (.op (.alu op (tmp 0) (fun i => if args i = 0 then tmp (left 0) else tmp (right 0))))) + refine ⟨hb, a + b + 2, emit, ?_⟩ + intro r src tmp hi hd + obtain ⟨hga, hra⟩ := ha src (tmp ∘ left) (hi.comp left_injective) + (fun i j => hd i (left j)) + obtain ⟨hgb, hrb⟩ := hg src (tmp ∘ right) (hi.comp right_injective) + (fun i j => hd i (right j)) + refine ⟨⟨hga, hgb, hop⟩, ?_⟩ + intro w x hx hw + obtain ⟨y, hy, hyo, hym, hyf⟩ := hra w x hx hw + have hys : y.regs ∘ src = x.regs ∘ src := by + funext i + exact hyf (src i) (fun j => (hd i (left j)).symm) + obtain ⟨z, hz, hzo, hzm, hzf⟩ := hrb w y (hys.symm ▸ hx) (hys.symm ▸ hw) + have hleft : z.regs (tmp (left 0)) = f (x.regs ∘ src) := + (hzf _ (fun j h => left_ne_right (0 : Fin (a + 1)) j (hi h).symm)).trans hyo + have hright : z.regs (tmp (right 0)) = g (x.regs ∘ src) := by + change z.regs (tmp (right 0)) = g (y.regs ∘ src) at hzo + exact hys ▸ hzo + have hargs : (z.regs ∘ (fun i => if args i = 0 then tmp (left 0) else tmp (right 0))) = + (fun i => ![f (x.regs ∘ src), g (x.regs ∘ src)] (args i)) := by + funext i + by_cases h : args i = 0 + · simp [h, hleft] + · have h' : args i = 1 := by apply Fin.ext; have := (args i).isLt; omega + simp [h', hright] + have he : ExecWithin w (.op (.alu op (tmp 0) + (fun i => if args i = 0 then tmp (left 0) else tmp (right 0)))) z + (z.set (tmp 0) (h (x.regs ∘ src))) 1 := by + apply ExecWithin.alu _ (hw.of_le (hb _ hx)) + rw [hargs] + exact heval _ hx w + refine ⟨z.set (tmp 0) (h (x.regs ∘ src)), hy.seq (hz.seq he), ?_, hzm.trans hym, ?_⟩ + · simp [Data.set] + · intro i hne + change Function.update z.regs (tmp 0) (h (x.regs ∘ src)) i = x.regs i + rw [Function.update_of_ne (hne 0).symm] + exact (hzf i (fun j => hne (right j))).trans (hyf i (fun j => hne (left j))) + +/-- Substitute a register map, including a map with repeated inputs. The emitter accepts +arbitrary input layouts, so this substitution needs no staging copies or injectivity premise. -/ +theorem reindex {m : ℕ} {Q : (Fin m → ℕ) → Prop} {b : (Fin m → ℕ) → ℕ} + (h : GlueComputes B P f bound time) (ρ : Fin k → Fin m) + (hP : ∀ v, Q v → P (v ∘ ρ)) (hb : ∀ v, Q v → bound (v ∘ ρ) ≤ b v) : + GlueComputes B Q (fun v => f (v ∘ ρ)) b time := by + obtain ⟨hf, n, emit, he⟩ := h + refine ⟨fun v hv => (hf _ (hP v hv)).trans (hb v hv), n, + fun src tmp => emit (src ∘ ρ) tmp, ?_⟩ + intro r src tmp hi hd + obtain ⟨hg, hrun⟩ := he (src ∘ ρ) tmp hi (fun i j => hd (ρ i) j) + refine ⟨hg, ?_⟩ + intro w x hx hw + exact hrun w x (hP _ hx) (hw.of_le (hb _ hx)) + +/-- Substitute any finite vector of computed arguments into a routine. All component emitters, +scratch banks and budgets are fixed before runtime inputs or widths. The forest runs in index +order, preserving inputs and earlier results, and then runs the outer routine in its own bank. +The one common envelope covers all components and dominates the outer envelope. -/ +theorem comp_many {m : ℕ} {Q : (Fin m → ℕ) → Prop} {h b : (Fin m → ℕ) → ℕ} + {g : (Fin k → ℕ) → Fin m → ℕ} {tf : ℕ} {t : Fin m → ℕ} + (hf : GlueComputes B Q h b tf) + (hg : ∀ i, GlueComputes B P (fun v => g v i) bound (t i)) + (hQ : ∀ v, P v → Q (g v)) (hb : ∀ v, P v → b (g v) ≤ bound v) : + GlueComputes B P (fun v => h (g v)) bound ((List.ofFn t).sum + tf) := by + classical + obtain ⟨hfo, a, ef, hef⟩ := hf + choose ns es hes using fun i => (hg i).2 + let S := (i : Fin m) × Fin (ns i + 1) + let N := Fintype.card S + let idx : S ≃ Fin N := Fintype.equivFin S + let inner (i : Fin m) (j : Fin (ns i + 1)) : Fin (N + a + 1) := + ⟨a + 1 + (idx ⟨i, j⟩).val, by have := (idx ⟨i, j⟩).isLt; omega⟩ + let outer (j : Fin (a + 1)) : Fin (N + a + 1) := ⟨j.val, by omega⟩ + have hindex {i j : Fin m} {p : Fin (ns i + 1)} {q : Fin (ns j + 1)} + (he : inner i p = inner j q) : (⟨i, p⟩ : S) = ⟨j, q⟩ := by + apply idx.injective + apply Fin.ext + have := congrArg Fin.val he + dsimp only [inner] at this + omega + have hinj (i : Fin m) : Function.Injective (inner i) := by + intro p q he + exact Fin.ext (congrArg (fun s : S => s.2.val) (hindex he)) + have hsep (i j : Fin m) (p : Fin (ns i + 1)) (q : Fin (ns j + 1)) + (he : inner i p = inner j q) : i = j := congrArg Sigma.fst (hindex he) + have hout : Function.Injective outer := by + intro p q he + apply Fin.ext + have hv := congrArg (fun j : Fin (N + a + 1) => j.val) he + exact hv + have hio (i : Fin m) (p : Fin (ns i + 1)) (q : Fin (a + 1)) : + inner i p ≠ outer q := by + intro he + have := congrArg Fin.val he + dsimp only [inner, outer] at this + omega + let emit : ∀ {r : ℕ}, (Fin k → Fin r) → (Fin (N + a + 1) → Fin r) → Code B r := + fun src tmp => (List.finRange m).foldr + (fun i rest => .seq (es i src (tmp ∘ inner i)) rest) + (ef (fun i => tmp (inner i 0)) (tmp ∘ outer)) + refine ⟨fun v hv => (hfo _ (hQ v hv)).trans (hb v hv), N + a, emit, ?_⟩ + intro r src tmp hi hd + have hc (i : Fin m) := hes i src (tmp ∘ inner i) (hi.comp (hinj i)) + (fun j q => hd j (inner i q)) + obtain ⟨hfg, hfrun⟩ := hef (fun i => tmp (inner i 0)) (tmp ∘ outer) + (hi.comp hout) (fun i q he => hio i 0 q (hi he)) + refine ⟨?_, ?_⟩ + · dsimp only [emit] + generalize List.finRange m = L + induction L with + | nil => exact hfg + | cons i L ih => exact ⟨(hc i).1, ih⟩ + intro w x hx hw + let v := x.regs ∘ src + have run (L : List (Fin m)) (z : Data r) (hz : z.regs ∘ src = v) + (hvals : ∀ i, i ∉ L → z.regs (tmp (inner i 0)) = g v i) : + ∃ y, ExecWithin w + (L.foldr (fun i rest => .seq (es i src (tmp ∘ inner i)) rest) + (ef (fun i => tmp (inner i 0)) (tmp ∘ outer))) z y ((L.map t).sum + tf) ∧ + y.regs (tmp 0) = h (g v) ∧ y.mem = z.mem ∧ + ∀ j, (∀ q, tmp q ≠ j) → y.regs j = z.regs j := by + induction L generalizing z with + | nil => + have hargs : z.regs ∘ (fun i => tmp (inner i 0)) = g v := by + funext i + exact hvals i (by simp) + obtain ⟨y, hy, ho, hm, hf⟩ := hfrun w z (hargs.symm ▸ hQ v hx) + (hargs.symm ▸ hw.of_le (hb v hx)) + refine ⟨y, ?_, ?_, hm, ?_⟩ + · simpa only [List.foldr_nil, List.map_nil, List.sum_nil, Nat.zero_add] using hy + · change y.regs (tmp 0) = h (z.regs ∘ (fun i => tmp (inner i 0))) at ho + exact hargs ▸ ho + · intro j hj + exact hf j (fun q => hj (outer q)) + | cons i L ih => + obtain ⟨z', he, ho, hm, hframe⟩ := (hc i).2 w z (hz.symm ▸ hx) (hz.symm ▸ hw) + have hz' : z'.regs ∘ src = v := by + funext j + exact (hframe (src j) (fun q => (hd j (inner i q)).symm)).trans (congrFun hz j) + have hvals' (j : Fin m) (hj : j ∉ L) : z'.regs (tmp (inner j 0)) = g v j := by + by_cases hji : j = i + · subst j + change z'.regs (tmp (inner i 0)) = g (z.regs ∘ src) i at ho + exact hz ▸ ho + · have hreg := hframe (tmp (inner j 0)) + (fun q heq => hji (hsep i j q 0 (hi heq)).symm) + exact hreg.trans (hvals j (by simpa only [List.mem_cons, not_or] using And.intro hji hj)) + obtain ⟨y, hy, hyo, hym, hyf⟩ := ih z' hz' hvals' + refine ⟨y, ?_, hyo, hym.trans hm, ?_⟩ + · simpa only [List.foldr_cons, List.map_cons, List.sum_cons, Nat.add_assoc] using he.seq hy + · intro j hj + exact (hyf j hj).trans (hframe j (fun q => hj (inner i q))) + obtain ⟨y, hy, ho, hm, hf⟩ := run (List.finRange m) x rfl + (fun i hi => False.elim (hi (by simp))) + refine ⟨y, ?_, ho, hm, hf⟩ + simpa only [List.ofFn_eq_map] using hy + +/-- Compose two scalar computations, passing the first result as the second computation's +single input. Each stage keeps its own explicit domain and envelope; the caller proves the +domain implication and envelope domination once. -/ +theorem comp {g b : (Fin 1 → ℕ) → ℕ} {Q : (Fin 1 → ℕ) → Prop} {tf tg : ℕ} + (hf : GlueComputes B P f bound tf) (hg : GlueComputes B Q g b tg) + (hQ : ∀ v, P v → Q (fun _ => f v)) + (hb : ∀ v, P v → b (fun _ => f v) ≤ bound v) : + GlueComputes B P (fun v => g (fun _ => f v)) bound (tf + tg + 1) := by + have hc := hg.comp_many (g := fun v _ => f v) (t := fun _ : Fin 1 => tf) + (fun _ => hf) hQ hb + have hc' : GlueComputes B P (fun v => g (fun _ => f v)) bound (tf + tg) := by + simpa [List.ofFn_succ] using hc + exact hc'.mono (fun _ _ => le_rfl) (by omega) + +/-- Select between two computations by an existing register condition. Only the selected +branch must satisfy its domain; the inactive branch may fault if executed. -/ +theorem select (cond : Cond k) {f g : (Fin k → ℕ) → ℕ} {tf tg : ℕ} + (hf : GlueComputes B (fun v => P v ∧ cond.Holds ⟨v, fun _ => 0⟩) f bound tf) + (hg : GlueComputes B (fun v => P v ∧ ¬cond.Holds ⟨v, fun _ => 0⟩) g bound tg) : + GlueComputes B P (fun v => if cond.Holds ⟨v, fun _ => 0⟩ then f v else g v) + bound (max tf tg + 3) := by + obtain ⟨hfo, a, ea, ha⟩ := hf + obtain ⟨hgo, b, eb, hg⟩ := hg + let emit : ∀ {r : ℕ}, (Fin k → Fin r) → (Fin (a + b + 2 + 1) → Fin r) → Code B r := + fun src tmp => .branch (cond.rename src) + (.seq (ea src (tmp ∘ left)) (.op (.copy (tmp 0) (tmp (left 0))))) + (.seq (eb src (tmp ∘ right)) (.op (.copy (tmp 0) (tmp (right 0))))) + refine ⟨?_, a + b + 2, emit, ?_⟩ + · intro v hv + dsimp only + split + · exact hfo v ⟨hv, by assumption⟩ + · exact hgo v ⟨hv, by assumption⟩ + intro r src tmp hi hd + obtain ⟨hga, hra⟩ := ha src (tmp ∘ left) (hi.comp left_injective) + (fun i j => hd i (left j)) + obtain ⟨hgb, hrb⟩ := hg src (tmp ∘ right) (hi.comp right_injective) + (fun i j => hd i (right j)) + refine ⟨⟨⟨hga, trivial⟩, hgb, trivial⟩, ?_⟩ + intro w x hx hw + have hc : (cond.rename src).Holds x ↔ cond.Holds ⟨x.regs ∘ src, fun _ => 0⟩ := by + cases cond <;> rfl + by_cases h : cond.Holds ⟨x.regs ∘ src, fun _ => 0⟩ + · obtain ⟨y, hy, hyo, hym, hyf⟩ := hra w x ⟨hx, h⟩ hw + refine ⟨y.set (tmp 0) (y.regs (tmp (left 0))), + ((hy.seq ExecWithin.copy).branch_pos (hc.mpr h)).mono (by omega), ?_, hym, ?_⟩ + · simpa [Data.set, h] using hyo + · intro i hne + change Function.update y.regs (tmp 0) _ i = x.regs i + rw [Function.update_of_ne (hne 0).symm] + exact hyf i (fun j => hne (left j)) + · obtain ⟨y, hy, hyo, hym, hyf⟩ := hrb w x ⟨hx, h⟩ hw + refine ⟨y.set (tmp 0) (y.regs (tmp (right 0))), + ((hy.seq ExecWithin.copy).branch_neg (fun h' => h (hc.mp h'))).mono (by omega), + ?_, hym, ?_⟩ + · simpa [Data.set, h] using hyo + · intro i hne + change Function.update y.regs (tmp 0) _ i = x.regs i + rw [Function.update_of_ne (hne 0).symm] + exact hyf i (fun j => hne (right j)) + +/-- Standard binary operations use the basis's documented total natural-number semantics. -/ +theorem bin_standard {f g : (Fin k → ℕ) → ℕ} {tf tg : ℕ} + (hf : GlueComputes .standard P f bound tf) (hg : GlueComputes .standard P g bound tg) + (op : BinOp) (hb : ∀ v, P v → op.eval (f v) (g v) ≤ bound v) : + GlueComputes .standard P (fun v => op.eval (f v) (g v)) bound (tf + tg + 1) := + binary hf hg (.bin op) id (by simp) (fun _ _ _ => rfl) hb + +/-- Exact binary operations require their interpretation to be defined on the fixed domain. +Output fit alone cannot discharge underflow or a zero divisor. -/ +theorem bin_exact {f g : (Fin k → ℕ) → ℕ} {tf tg : ℕ} + (hf : GlueComputes .exact P f bound tf) (hg : GlueComputes .exact P g bound tg) + (op : BinOp) (hv : ∀ v, P v → op.evalExact (f v) (g v) = some (op.eval (f v) (g v))) + (hb : ∀ v, P v → op.eval (f v) (g v) ≤ bound v) : + GlueComputes .exact P (fun v => op.eval (f v) (g v)) bound (tf + tg + 1) := + binary hf hg (.bin op) id (by simp) (fun v hp _ => hv v hp) hb + +end GlueComputes + +namespace GlueComputable + +variable {P : (Fin k → ℕ) → Prop} {bound : (Fin k → ℕ) → ℕ} + +/-- Register projections are found without searching for a budget. -/ +@[fun_prop] +theorem proj (i : Fin k) (hb : ∀ v, P v → v i ≤ bound v) : + GlueComputable B (fun v => v i) P bound := (GlueComputes.proj i hb).computable + +/-- Constants are found without searching for a budget. -/ +@[fun_prop] +theorem const (z : ℕ) (hb : ∀ v, P v → z ≤ bound v) : + GlueComputable B (fun _ => z) P bound := (GlueComputes.const z hb).computable + +variable {f g : (Fin k → ℕ) → ℕ} + +/-- Reuse a named scalar computation after another computation, with explicit domain and +envelope implications. This rule searches for the two functions, never for a budget. -/ +@[fun_prop] +theorem comp {h : ℕ → ℕ} {b : (Fin 1 → ℕ) → ℕ} {Q : (Fin 1 → ℕ) → Prop} + (hf : GlueComputable B f P bound) + (hh : GlueComputable B (fun v => h (v 0)) Q b) + (hQ : ∀ v, P v → Q (fun _ => f v)) + (hb : ∀ v, P v → b (fun _ => f v) ≤ bound v) : + GlueComputable B (fun v => h (f v)) P bound := by + obtain ⟨tf, hf⟩ := hf + obtain ⟨th, hh⟩ := hh + exact (hf.comp hh hQ hb).computable + +/-- Apply an opaque routine to any finite vector of computed local words. The component +budgets are chosen uniformly, before the input/width quantifiers, and then summed explicitly. -/ +@[fun_prop] +theorem comp_many {m : ℕ} {Q : (Fin m → ℕ) → Prop} {h b : (Fin m → ℕ) → ℕ} + {g : (Fin k → ℕ) → Fin m → ℕ} + (hh : GlueComputable B h Q b) + (hg : ∀ i, GlueComputable B (fun v => g v i) P bound) + (hQ : ∀ v, P v → Q (g v)) (hb : ∀ v, P v → b (g v) ≤ bound v) : + GlueComputable B (fun v => h (g v)) P bound := by + classical + obtain ⟨tf, hf⟩ := hh + choose t ht using hg + exact (hf.comp_many ht hQ hb).computable + +/-- Indexing a finite tuple is compile-time routing, so it adds no runtime instruction. -/ +@[fun_prop] +theorem vec_cons {m : ℕ} {g : (Fin k → ℕ) → Fin m → ℕ} + (hf : GlueComputable B f P bound) + (hg : ∀ i, GlueComputable B (fun v => g v i) P bound) (i : Fin (m + 1)) : + GlueComputable B (fun v => Matrix.vecCons (f v) (g v) i) P bound := by + refine Fin.cases ?_ (fun j => ?_) i + · exact hf + · exact hg j + +/-- A singleton tuple has just its computed head; there is no empty-tail obligation. -/ +@[fun_prop] +theorem vec_single (hf : GlueComputable B f P bound) (i : Fin 1) : + GlueComputable B (fun v => ![f v] i) P bound := by + have hi : i = 0 := Subsingleton.elim _ _ + subst i + exact hf + +/-- Register-condition selection retains branch-specific domains. -/ +theorem select (cond : Cond k) + (hf : GlueComputable B f (fun v => P v ∧ cond.Holds ⟨v, fun _ => 0⟩) bound) + (hg : GlueComputable B g (fun v => P v ∧ ¬cond.Holds ⟨v, fun _ => 0⟩) bound) : + GlueComputable B (fun v => if cond.Holds ⟨v, fun _ => 0⟩ then f v else g v) P bound := by + obtain ⟨tf, hf⟩ := hf + obtain ⟨tg, hg⟩ := hg + exact (GlueComputes.select cond hf hg).computable + +/-- Select by equality of input words, without evaluating the unchosen computation. -/ +@[fun_prop] +theorem ite_eq (i j : Fin k) + (hf : GlueComputable B f (fun v => P v ∧ v i = v j) bound) + (hg : GlueComputable B g (fun v => P v ∧ ¬v i = v j) bound) : + GlueComputable B (fun v => if v i = v j then f v else g v) P bound := + select (.eq i j) hf hg + +/-- Select by unsigned comparison of input words. -/ +@[fun_prop] +theorem ite_lt (i j : Fin k) + (hf : GlueComputable B f (fun v => P v ∧ v i < v j) bound) + (hg : GlueComputable B g (fun v => P v ∧ ¬v i < v j) bound) : + GlueComputable B (fun v => if v i < v j then f v else g v) P bound := + select (.lt i j) hf hg + +/-- The standard basis's binary operation rule. -/ +theorem bin_standard (hf : GlueComputable .standard f P bound) + (hg : GlueComputable .standard g P bound) (op : BinOp) + (hb : ∀ v, P v → op.eval (f v) (g v) ≤ bound v) : + GlueComputable .standard (fun v => op.eval (f v) (g v)) P bound := by + obtain ⟨tf, hf⟩ := hf + obtain ⟨tg, hg⟩ := hg + exact (hf.bin_standard hg op hb).computable + +/-- The exact basis's binary operation rule keeps its domain premise explicit. -/ +theorem bin_exact (hf : GlueComputable .exact f P bound) + (hg : GlueComputable .exact g P bound) (op : BinOp) + (hv : ∀ v, P v → op.evalExact (f v) (g v) = some (op.eval (f v) (g v))) + (hb : ∀ v, P v → op.eval (f v) (g v) ≤ bound v) : + GlueComputable .exact (fun v => op.eval (f v) (g v)) P bound := by + obtain ⟨tf, hf⟩ := hf + obtain ⟨tg, hg⟩ := hg + exact (hf.bin_exact hg op hv hb).computable + +/-- Closure under exact-basis `add`; the common envelope also bounds this result. -/ +@[fun_prop] +theorem add (hf : GlueComputable .exact f P bound) + (hg : GlueComputable .exact g P bound) + (hb : ∀ v, P v → f v + g v ≤ bound v) : + GlueComputable .exact (fun v => f v + g v) P bound := + bin_exact hf hg .add (fun _ _ => rfl) hb + +/-- Closure under exact-basis `sub`; the common envelope also bounds this result. -/ +@[fun_prop] +theorem sub (hf : GlueComputable .exact f P bound) + (hg : GlueComputable .exact g P bound) + (hv : ∀ v, P v → g v ≤ f v) + (hb : ∀ v, P v → f v - g v ≤ bound v) : + GlueComputable .exact (fun v => f v - g v) P bound := + bin_exact hf hg .sub (fun v hp => by simp [hv v hp]) hb + +/-- Closure under exact-basis `mul`; the common envelope also bounds this result. -/ +@[fun_prop] +theorem mul (hf : GlueComputable .exact f P bound) + (hg : GlueComputable .exact g P bound) + (hb : ∀ v, P v → f v * g v ≤ bound v) : + GlueComputable .exact (fun v => f v * g v) P bound := + bin_exact hf hg .mul (fun _ _ => rfl) hb + +/-- Closure under exact-basis `div`; the common envelope also bounds this result. -/ +@[fun_prop] +theorem div (hf : GlueComputable .exact f P bound) + (hg : GlueComputable .exact g P bound) + (hv : ∀ v, P v → g v ≠ 0) + (hb : ∀ v, P v → f v / g v ≤ bound v) : + GlueComputable .exact (fun v => f v / g v) P bound := + bin_exact hf hg .div (fun v hp => by simp [hv v hp]) hb + +/-- Closure under exact-basis `mod`; the common envelope also bounds this result. -/ +@[fun_prop] +theorem mod (hf : GlueComputable .exact f P bound) + (hg : GlueComputable .exact g P bound) + (hv : ∀ v, P v → g v ≠ 0) + (hb : ∀ v, P v → f v % g v ≤ bound v) : + GlueComputable .exact (fun v => f v % g v) P bound := + bin_exact hf hg .mod (fun v hp => by simp [hv v hp]) hb + +/-- Closure under exact-basis `and`; the common envelope also bounds this result. -/ +@[fun_prop] +theorem and (hf : GlueComputable .exact f P bound) + (hg : GlueComputable .exact g P bound) + (hb : ∀ v, P v → f v &&& g v ≤ bound v) : + GlueComputable .exact (fun v => f v &&& g v) P bound := + bin_exact hf hg .and (fun _ _ => rfl) hb + +/-- Closure under exact-basis `or`; the common envelope also bounds this result. -/ +@[fun_prop] +theorem or (hf : GlueComputable .exact f P bound) + (hg : GlueComputable .exact g P bound) + (hb : ∀ v, P v → f v ||| g v ≤ bound v) : + GlueComputable .exact (fun v => f v ||| g v) P bound := + bin_exact hf hg .or (fun _ _ => rfl) hb + +/-- Closure under exact-basis `xor`; the common envelope also bounds this result. -/ +@[fun_prop] +theorem xor (hf : GlueComputable .exact f P bound) + (hg : GlueComputable .exact g P bound) + (hb : ∀ v, P v → f v ^^^ g v ≤ bound v) : + GlueComputable .exact (fun v => f v ^^^ g v) P bound := + bin_exact hf hg .xor (fun _ _ => rfl) hb + +/-- Closure under exact-basis `shiftLeft`; the common envelope also bounds this result. -/ +@[fun_prop] +theorem shiftLeft (hf : GlueComputable .exact f P bound) + (hg : GlueComputable .exact g P bound) + (hb : ∀ v, P v → f v <<< g v ≤ bound v) : + GlueComputable .exact (fun v => f v <<< g v) P bound := + bin_exact hf hg .shiftLeft (fun _ _ => rfl) hb + +/-- Closure under exact-basis `shiftRight`; the common envelope also bounds this result. -/ +@[fun_prop] +theorem shiftRight (hf : GlueComputable .exact f P bound) + (hg : GlueComputable .exact g P bound) + (hb : ∀ v, P v → f v >>> g v ≤ bound v) : + GlueComputable .exact (fun v => f v >>> g v) P bound := + bin_exact hf hg .shiftRight (fun _ _ => rfl) hb + +/-- Closure under standard-basis `add`; the common envelope also bounds this result. -/ +@[fun_prop] +theorem add_standard (hf : GlueComputable .standard f P bound) + (hg : GlueComputable .standard g P bound) + (hb : ∀ v, P v → f v + g v ≤ bound v) : + GlueComputable .standard (fun v => f v + g v) P bound := + bin_standard hf hg .add hb + +/-- Closure under standard-basis `sub`; the common envelope also bounds this result. -/ +@[fun_prop] +theorem sub_standard (hf : GlueComputable .standard f P bound) + (hg : GlueComputable .standard g P bound) + (hb : ∀ v, P v → f v - g v ≤ bound v) : + GlueComputable .standard (fun v => f v - g v) P bound := + bin_standard hf hg .sub hb + +/-- Closure under standard-basis `mul`; the common envelope also bounds this result. -/ +@[fun_prop] +theorem mul_standard (hf : GlueComputable .standard f P bound) + (hg : GlueComputable .standard g P bound) + (hb : ∀ v, P v → f v * g v ≤ bound v) : + GlueComputable .standard (fun v => f v * g v) P bound := + bin_standard hf hg .mul hb + +/-- Closure under standard-basis `div`; the common envelope also bounds this result. -/ +@[fun_prop] +theorem div_standard (hf : GlueComputable .standard f P bound) + (hg : GlueComputable .standard g P bound) + (hb : ∀ v, P v → f v / g v ≤ bound v) : + GlueComputable .standard (fun v => f v / g v) P bound := + bin_standard hf hg .div hb + +/-- Closure under standard-basis `mod`; the common envelope also bounds this result. -/ +@[fun_prop] +theorem mod_standard (hf : GlueComputable .standard f P bound) + (hg : GlueComputable .standard g P bound) + (hb : ∀ v, P v → f v % g v ≤ bound v) : + GlueComputable .standard (fun v => f v % g v) P bound := + bin_standard hf hg .mod hb + +/-- Closure under standard-basis `and`; the common envelope also bounds this result. -/ +@[fun_prop] +theorem and_standard (hf : GlueComputable .standard f P bound) + (hg : GlueComputable .standard g P bound) + (hb : ∀ v, P v → f v &&& g v ≤ bound v) : + GlueComputable .standard (fun v => f v &&& g v) P bound := + bin_standard hf hg .and hb + +/-- Closure under standard-basis `or`; the common envelope also bounds this result. -/ +@[fun_prop] +theorem or_standard (hf : GlueComputable .standard f P bound) + (hg : GlueComputable .standard g P bound) + (hb : ∀ v, P v → f v ||| g v ≤ bound v) : + GlueComputable .standard (fun v => f v ||| g v) P bound := + bin_standard hf hg .or hb + +/-- Closure under standard-basis `xor`; the common envelope also bounds this result. -/ +@[fun_prop] +theorem xor_standard (hf : GlueComputable .standard f P bound) + (hg : GlueComputable .standard g P bound) + (hb : ∀ v, P v → f v ^^^ g v ≤ bound v) : + GlueComputable .standard (fun v => f v ^^^ g v) P bound := + bin_standard hf hg .xor hb + +/-- Closure under standard-basis `shiftLeft`; the common envelope also bounds this result. -/ +@[fun_prop] +theorem shiftLeft_standard (hf : GlueComputable .standard f P bound) + (hg : GlueComputable .standard g P bound) + (hb : ∀ v, P v → f v <<< g v ≤ bound v) : + GlueComputable .standard (fun v => f v <<< g v) P bound := + bin_standard hf hg .shiftLeft hb + +/-- Closure under standard-basis `shiftRight`; the common envelope also bounds this result. -/ +@[fun_prop] +theorem shiftRight_standard (hf : GlueComputable .standard f P bound) + (hg : GlueComputable .standard g P bound) + (hb : ∀ v, P v → f v >>> g v ≤ bound v) : + GlueComputable .standard (fun v => f v >>> g v) P bound := + bin_standard hf hg .shiftRight hb + +end GlueComputable + +end Cslib.WordRAM diff --git a/ComplexityTests/WordRAMGlue.lean b/ComplexityTests/WordRAMGlue.lean new file mode 100644 index 0000000000..e1b2663e7a --- /dev/null +++ b/ComplexityTests/WordRAMGlue.lean @@ -0,0 +1,172 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: local-only (tests) + +public import Complexity.Computability.WordRAM.Glue +import ComplexityTests.WordRAMGlueNamed +meta import Mathlib.Tactic.FunProp.Decl + +/-! Concrete word-glue clients. `WordRAMBasis` separately pins the underlying basis semantics. -/ + +public section + +namespace ComplexityTests.WordRAMGlue + +open Cslib.WordRAM + +-- The property must be indexed by the scalar function, not the admissibility predicate. +run_cmd Lean.Elab.Command.liftTermElabM do + let goal ← Lean.Elab.Term.elabTerm (← `(GlueComputable .exact + (fun v : Fin 1 → ℕ => v 0 + 1) (fun v => v 0 ≤ 10) (fun _ => 11))) none + let some decl ← Mathlib.Meta.FunProp.getFunPropDecl? goal + | throwError "GlueComputable was not registered" + unless decl.funArgId == 2 do throwError "wrong GlueComputable function index" + let some f ← Mathlib.Meta.FunProp.getFunPropFun? goal + | throwError "GlueComputable function was not found" + let expected ← Lean.Elab.Term.elabTerm (← `(fun v : Fin 1 → ℕ => v 0 + 1)) none + unless ← Lean.Meta.isDefEq f expected do throwError "indexed P or the bound instead of f" + +-- Costs are explicit and separate from the automation facade: two copies and one addition. +example : GlueComputes .exact (fun v : Fin 2 → ℕ => v 0 + v 1 ≤ 100) + (fun v => v 0 + v 1) (fun _ => 100) 3 := by + apply GlueComputes.bin_exact (GlueComputes.proj 0 ?_) (GlueComputes.proj 1 ?_) .add + · intros; rfl + all_goals intros; (try dsimp [BinOp.eval, Function.comp_def] at *); omega + +-- A larger advertised bound is discharged once, outside fun_prop's rule search. +example : GlueComputes .exact (fun v : Fin 2 → ℕ => v 0 + v 1 ≤ 100) + (fun v => v 0 + v 1) (fun _ => 200) 10 := by + have h : GlueComputes .exact (fun v : Fin 2 → ℕ => v 0 + v 1 ≤ 100) + (fun v => v 0 + v 1) (fun _ => 100) 3 := by + apply GlueComputes.bin_exact (GlueComputes.proj 0 ?_) (GlueComputes.proj 1 ?_) .add + · intros; rfl + all_goals intros; (try dsimp [BinOp.eval, Function.comp_def] at *); omega + exact h.mono (by intros; omega) (by omega) + +-- Ordinary arithmetic closure needs no intermediate machine assertions or cost metavariables. +example : GlueComputable .exact (fun v => (v 0 + v 1) + v 2) + (fun v : Fin 3 → ℕ => v 0 + v 1 + v 2 ≤ 100) + (fun _ => 100) := by + fun_prop (disch := intros; (try dsimp [BinOp.eval, Function.comp_def] at *); omega) + +example : GlueComputable .standard (fun v => v 0 + v 1) + (fun v : Fin 2 → ℕ => v 0 + v 1 ≤ 100) + (fun _ => 100) := by + fun_prop (disch := intros; (try dsimp [BinOp.eval, Function.comp_def] at *); omega) + +-- Exact subtraction uses the domain, rather than totalizing an underflow. +example : GlueComputable .exact (fun v => (v 0 - v 1) / 2) + (fun v : Fin 2 → ℕ => v 1 ≤ v 0 ∧ v 0 ≤ 100) + (fun _ => 100) := by + fun_prop (disch := intros; (try dsimp [BinOp.eval, Function.comp_def] at *); omega) + +-- The nonselected branch is allowed to be invalid on the other branch's input domain. +example : GlueComputable .exact (fun v => if v 0 < v 1 then v 1 - v 0 else v 0 - v 1) + (fun v : Fin 2 → ℕ => v 0 ≤ 100 ∧ v 1 ≤ 100) + (fun _ => 100) := by + fun_prop (disch := intros; (try dsimp [BinOp.eval, Function.comp_def] at *); omega) + +-- At zero, division is not evaluated. The other branch's domain supplies a nonzero divisor. +example : GlueComputable .exact (fun v => if v 0 = v 1 then 0 else 7 / v 0) + (fun v : Fin 2 → ℕ => v 0 ≤ 100 ∧ v 1 = 0) (fun _ => 100) := by + fun_prop (disch := intro v hv; have := Nat.div_le_self 7 (v 0); omega) + +-- Repeated source registers are supported by layout substitution, without injectivity of ρ. +example : GlueComputes .exact (fun v : Fin 1 → ℕ => 2 * v 0 ≤ 100) + (fun v => v 0 + v 0) (fun _ => 100) 3 := by + have h : GlueComputes .exact (fun v : Fin 2 → ℕ => v 0 + v 1 ≤ 100) + (fun v => v 0 + v 1) (fun _ => 100) 3 := by + apply GlueComputes.bin_exact (GlueComputes.proj 0 ?_) (GlueComputes.proj 1 ?_) .add + · intros; rfl + all_goals intros; (try dsimp [BinOp.eval, Function.comp_def] at *); omega + exact h.reindex (fun _ => 0) + (by intros; (try dsimp [BinOp.eval, Function.comp_def] at *); omega) (by intros; rfl) + +-- This function's body is opaque in this importing module. Composition reuses its certificate. +example : GlueComputable .exact (fun v => WordRAMGlueNamed.twice (v 0 + v 1)) + (fun v : Fin 2 → ℕ => 2 * (v 0 + v 1) ≤ 100) + (fun _ => 100) := by + have ht := WordRAMGlueNamed.twice_glue + fun_prop (disch := intros; omega) + +-- Both arguments are computed locals. The outer two-input routine is opaque here; the +-- forest rule must stage both values and preserve the input/frame automatically. +example : GlueComputable .exact + (fun v => WordRAMGlueNamed.weighted ![v 0 + 1, v 1 + v 1]) + (fun v : Fin 2 → ℕ => v 0 + 1 + 4 * v 1 ≤ 100) (fun _ => 100) := by + have hw := WordRAMGlueNamed.weighted_glue + fun_prop (disch := intros; (try dsimp at *); omega) + +example : WordRAMGlueNamed.weighted ![(2 : ℕ) + 1, 3 + 3] = 15 := + WordRAMGlueNamed.weighted_three_six + +-- Empty substitution executes only the outer constant routine, with no staging overhead. +example : GlueComputes .exact (fun _ : Fin 1 → ℕ => True) + (fun _ => 7) (fun _ => 7) 1 := by + have hf : GlueComputes .exact (fun _ : Fin 0 → ℕ => True) + (fun _ => 7) (fun _ => 7) 1 := GlueComputes.const 7 (by intros; rfl) + have hg : ∀ i : Fin 0, GlueComputes .exact (fun _ : Fin 1 → ℕ => True) + (fun _ => Fin.elim0 i) (fun _ => 7) (Fin.elim0 i) := fun i => Fin.elim0 i + exact hf.comp_many hg (by intros; trivial) (by intros; rfl) + +/-- The certificate supplies one actual finite code before the initial memory is chosen. -/ +theorem concrete_code : ∃ n, ∃ code : Code .exact (2 + (n + 1)), code.IsGlue ∧ + ∀ mem : ℕ → ℕ, ∃ y, + ExecWithin 4 code ⟨Fin.addCases ![2, 3] (fun _ => 0), mem⟩ y 3 ∧ + y.regs (Fin.natAdd 2 0) = 5 ∧ y.mem = mem ∧ + y.regs (Fin.castAdd (n + 1) 0) = 2 ∧ y.regs (Fin.castAdd (n + 1) 1) = 3 := by + have h : GlueComputes .exact (fun v : Fin 2 → ℕ => v 0 + v 1 ≤ 10) + (fun v => v 0 + v 1) (fun _ => 10) 3 := by + apply GlueComputes.bin_exact (GlueComputes.proj 0 ?_) (GlueComputes.proj 1 ?_) .add + · intros; rfl + all_goals intros; (try dsimp [BinOp.eval] at *); omega + obtain ⟨n, code, hg, hrun⟩ := h.realize + refine ⟨n, code, hg, ?_⟩ + intro mem + obtain ⟨y, hy, ho, hm, hi⟩ := hrun 4 ![2, 3] mem (by decide) (by decide) + exact ⟨y, hy, ho, hm, hi 0, hi 1⟩ + +-- A zero envelope cannot hide a positive returned word, including at width zero. +example : ¬GlueComputable .exact (fun _ => 1) (fun _ : Fin 0 → ℕ => True) (fun _ => 0) := by + rintro ⟨_, h⟩ + have hh := h.1 Fin.elim0 trivial + exact Nat.not_succ_le_zero 0 hh + +-- Existing Code.eval makes failure independent of fuel exhaustion for this loop-free code. +private def sample (a b : ℕ) : Data 4 := ⟨![a, b, 0, 0], fun _ => 17⟩ + +example : (Code.op (Op.const (B := .exact) (2 : Fin 4) 0)).eval 0 0 (sample 0 0) = + some ((sample 0 0).set 2 0, 1) := rfl + +example : (Code.op (Op.const (B := .exact) (2 : Fin 4) 1)).eval 0 0 (sample 0 0) = + none := by decide + +example : (Code.op (Op.exactBin .sub (2 : Fin 4) 0 1)).eval 4 0 (sample 3 5) = + none := by decide + +example : (Code.op (Op.exactBin .div (2 : Fin 4) 0 1)).eval 4 0 (sample 7 0) = + none := by decide + +example : (Code.op (Op.exactBin .mod (2 : Fin 4) 0 1)).eval 4 0 (sample 7 0) = + none := by decide + +-- Fitting inputs and fitting final mathematical value do not imply intermediate fit. +example : (Code.seq (.op (.exactBin .add (2 : Fin 4) 0 1)) + (.op (.exactBin .sub 2 2 1))).eval 3 0 (sample 7 1) = none := by decide + +-- A lazy branch avoids an exact zero-divisor fault and charges the actual branch overhead. +private def lazyDivide : Code .exact 4 := + .branch (.eq 0 1) (.op (.const 2 0)) (.op (.exactBin .div 2 3 0)) + +example : lazyDivide.eval 3 0 ⟨![0, 0, 0, 7], fun _ => 17⟩ = + some ((⟨![0, 0, 0, 7], fun _ => 17⟩ : Data 4).set 2 0, 2) := rfl + +example : lazyDivide.eval 3 0 ⟨![2, 0, 0, 7], fun _ => 17⟩ = + some ((⟨![2, 0, 0, 7], fun _ => 17⟩ : Data 4).set 2 3, 3) := rfl + +end ComplexityTests.WordRAMGlue diff --git a/ComplexityTests/WordRAMGlueNamed.lean b/ComplexityTests/WordRAMGlueNamed.lean new file mode 100644 index 0000000000..3f09a068d3 --- /dev/null +++ b/ComplexityTests/WordRAMGlueNamed.lean @@ -0,0 +1,43 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: local-only (tests) + +public import Complexity.Computability.WordRAM.Glue + +/-! An imported, unexposed named routine for word-glue composition regressions. -/ + +public section + +namespace ComplexityTests.WordRAMGlueNamed + +open Cslib.WordRAM + +/-- The body is deliberately not exposed to importing clients. -/ +def twice (n : ℕ) : ℕ := n + n + +/-- A named certificate can be reused without unfolding its function. -/ +theorem twice_glue : GlueComputable .exact (fun v => twice (v 0)) + (fun v : Fin 1 → ℕ => 2 * v 0 ≤ 100) (fun _ => 100) := by + unfold twice + fun_prop (disch := intros; omega) + +/-- A two-input routine whose definition is also hidden across this module boundary. -/ +def weighted (v : Fin 2 → ℕ) : ℕ := v 0 + 2 * v 1 + +/-- The fixed domain and envelope make the named routine safe independently of its callers. -/ +theorem weighted_glue : GlueComputable .exact weighted + (fun v => v 0 + 2 * v 1 ≤ 100) (fun _ => 100) := by + unfold weighted + fun_prop (disch := intros; omega) + +/-- A concrete value pins the opaque routine's ordinary natural-number meaning. -/ +theorem weighted_three_six : weighted ![3, 6] = 15 := by + unfold weighted + decide + +end ComplexityTests.WordRAMGlueNamed From 45dd18424e29ae6a3136f0d0944fbc60354f1b44 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 18:22:17 -0500 Subject: [PATCH 13/27] feat(WordRAM): realize memory queries with uniform bind and glue Add one graded certificate over existing Memory.Prog and native Code. Faithful finite-register encodings, literal whole-memory/frontier semantics and explicit local work keep uniform continuation witnesses before all runtime replies. This is the foundational milestone of #2213 under #2205. Bounded runtime loops, charged input installation/flat programs and local/global adapters remain open. No curated abstract or headline scope changes. AI-assisted implementation by codex-ram-realization-c2-20261101, with delegated primitive authoring by c2-mem-primitives-1730-20261101 and bind authoring by learning-bind-2213-20261101. Memory5909 and Glue4283 prerequisites are preserved. --- Complexity.lean | 5 + .../WordRAM/Realization/Basic.lean | 182 +++++++++++++++ .../WordRAM/Realization/Bind.lean | 189 +++++++++++++++ .../WordRAM/Realization/Branch.lean | 129 +++++++++++ .../WordRAM/Realization/Glue.lean | 70 ++++++ .../WordRAM/Realization/Primitive.lean | 206 +++++++++++++++++ ComplexityTests/WordRAMRealizationBind.lean | 61 +++++ ComplexityTests/WordRAMRealizationBranch.lean | 52 +++++ .../WordRAMRealizationPrimitive.lean | 215 ++++++++++++++++++ ROADMAP.md | 10 + UPSTREAM.md | 6 + 11 files changed, 1125 insertions(+) create mode 100644 Complexity/Computability/WordRAM/Realization/Basic.lean create mode 100644 Complexity/Computability/WordRAM/Realization/Bind.lean create mode 100644 Complexity/Computability/WordRAM/Realization/Branch.lean create mode 100644 Complexity/Computability/WordRAM/Realization/Glue.lean create mode 100644 Complexity/Computability/WordRAM/Realization/Primitive.lean create mode 100644 ComplexityTests/WordRAMRealizationBind.lean create mode 100644 ComplexityTests/WordRAMRealizationBranch.lean create mode 100644 ComplexityTests/WordRAMRealizationPrimitive.lean diff --git a/Complexity.lean b/Complexity.lean index b1759ffebf..b58e9959ef 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2556,6 +2556,11 @@ public import Complexity.Computability.WordRAM.Randomized.Law public import Complexity.Computability.WordRAM.Randomized.LoopSegment public import Complexity.Computability.WordRAM.Randomized.Structured public import Complexity.Computability.WordRAM.Randomized.UniformLoopExit +public import Complexity.Computability.WordRAM.Realization.Basic +public import Complexity.Computability.WordRAM.Realization.Bind +public import Complexity.Computability.WordRAM.Realization.Branch +public import Complexity.Computability.WordRAM.Realization.Glue +public import Complexity.Computability.WordRAM.Realization.Primitive public import Complexity.Computability.WordRAM.RegisterFrame public import Complexity.Computability.WordRAM.Sequence public import Complexity.Computability.WordRAM.Signed.Arithmetic diff --git a/Complexity/Computability/WordRAM/Realization/Basic.lean b/Complexity/Computability/WordRAM/Realization/Basic.lean new file mode 100644 index 0000000000..12c4ef7608 --- /dev/null +++ b/Complexity/Computability/WordRAM/Realization/Basic.lean @@ -0,0 +1,182 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Controller +public import Complexity.Computability.WordRAM.MapOp +public import Complexity.DataStructure.Memory.Spec + +/-! +# Graded realization of memory-oracle clients + +`Realization.Realizes` certifies one uniform native emitter for an existing `Memory.Prog` +family. Arguments and results have explicit faithful finite-register encodings. Its memory +abstraction is the entire memory function together with one frontier register; scratch consists +only of fresh registers. The entry predicate and local-work charge are visible parameters. + +The charged currency adds certified local computation and control work to `Memory.charge`. +In particular, a query-free loop cannot acquire a constant instruction bound merely because its +memory history is empty. The emitter, scratch size and exchange constant precede every runtime +argument, word width and initial memory. `Refines` is a semantic projection of this certificate, +not a competing execution semantics or a second client language. + +## Main definitions + +* `memoryState`: the functional whole-memory/frontier abstraction. +* `memoryCost`: the memory-level charge of the client's actual deterministic history. +* `Realizes`: uniform native realization with a combined instruction currency and register frame. + +## Main results + +* `Realizes.exists_refines`: forget the fresh-register frame to the existing `Refines` API. + +The realization certificate and its grading are original infrastructure. The memory interface, +free-monad interpreter and native execution rules are reused unchanged. +-/ + +@[expose] public section + +namespace Cslib.WordRAM.Realization + +open Cslib.DataStructure + +variable {B : Basis} {r k l : ℕ} {δ α : Type} + +/-- The abstraction retains every cell, including unallocated and inaccessible cells. -/ +def memoryState (frontier : Fin r) (x : Data r) : Memory.State := + ⟨x.mem, x.regs frontier⟩ + +/-- Bounded native data supplies the memory invariant, independently of any scratch layout. -/ +theorem wellFormed_memoryState {w : ℕ} {frontier : Fin r} {x : Data r} (hx : x.Bounded w) : + Memory.WellFormed w (memoryState frontier x) := + ⟨hx.1 frontier, hx.2⟩ + +/-- The memory-level instruction currency uses the existing deterministic interpreter. Its +transition function does not depend on the word width; admissibility is a separate premise. -/ +def memoryCost (p : Memory.Prog α) (s : Memory.State) : ℕ := + (Memory.spec 0).totalCost Memory.charge s ((Memory.spec 0).clientHistory s p) + +/-- Uniformity of the mathematical interpreter across widths: only `valid` depends on `w`. -/ +theorem runClient_width (w : ℕ) (p : Memory.Prog α) (s : Memory.State) : + (Memory.spec w).runClient s p = (Memory.spec 0).runClient s p := by + induction p generalizing s with + | pure a => rfl + | liftBind o cont ih => + exact congrArg (fun t : α × Memory.State × List Memory.Op => (t.1, t.2.1, o :: t.2.2)) + (ih (Memory.step s o).2 (Memory.step s o).1) + +/-- A semantic return issues no memory operation. Its native glue is charged separately. -/ +@[simp] theorem memoryCost_pure (a : α) (s : Memory.State) : + memoryCost (.pure a) s = 0 := rfl + +/-- Memory charges add at the actual intermediate state; the local bind charge is separate. -/ +theorem memoryCost_bind {β : Type} (p : Memory.Prog α) (q : α → Memory.Prog β) + (s : Memory.State) : + memoryCost (p.bind q) s = memoryCost p s + + memoryCost (q ((Memory.spec 0).clientResult s p)) ((Memory.spec 0).clientState s p) := by + unfold memoryCost + simp only [StateTransitionSpec.clientHistory, StateTransitionSpec.runClient_bind] + rw [StateTransitionSpec.totalCost_append, StateTransitionSpec.run_runClient] + +/-- Existing `Memory.Prog` clients have a fixed emitter and a combined charge. The explicit +`safe` predicate may contain glue domains and fit envelopes; it is never existentially chosen. +The existing valid-history premise supplies memory admissibility. The extra one pays uniform +entry/exit overhead, so the native bound is `C * charged + C` with one fixed `C`. + +Input registers may alias each other. The injective scratch bank is disjoint from all inputs and +the frontier; its first `l` registers hold the faithfully encoded result. Every other register, +except the frontier, is framed. All native registers and memory are bounded at entry, separately +from the functional representation. -/ +def Realizes (B : Basis) (p : δ → Memory.Prog α) + (input : δ ↪ (Fin k → ℕ)) (output : α ↪ (Fin l → ℕ)) + (safe : ℕ → δ → Memory.State → Prop) (work : δ → Memory.State → ℕ) : Prop := + ∃ n C : ℕ, + ∃ emit : ∀ {r : ℕ}, (frontier : Fin r) → (Fin k → Fin r) → + (Fin (l + n + 1) → Fin r) → Code B r, + ∀ {r : ℕ} (frontier : Fin r) (src : Fin k → Fin r) + (tmp : Fin (l + n + 1) → Fin r), + Function.Injective tmp → (∀ i j, src i ≠ tmp j) → + (∀ j, tmp j ≠ frontier) → (∀ i, src i ≠ frontier) → + ¬(emit frontier src tmp).ReadsWidth ∧ + ∀ (w : ℕ) (d : δ) (x : Data r), x.Bounded w → x.regs ∘ src = input d → + safe w d (memoryState frontier x) → + (Memory.spec w).ValidHistory (memoryState frontier x) + ((Memory.spec w).clientHistory (memoryState frontier x) (p d)) → + ∃ y, ExecWithin w (emit frontier src tmp) x y + (C * (memoryCost (p d) (memoryState frontier x) + + work d (memoryState frontier x) + 1)) ∧ + memoryState frontier y = (Memory.spec w).clientState (memoryState frontier x) (p d) ∧ + (∀ i : Fin l, y.regs (tmp (Fin.castAdd (n + 1) i)) = + output ((Memory.spec w).clientResult (memoryState frontier x) (p d)) i) ∧ + ∀ i, i ≠ frontier → (∀ j, tmp j ≠ i) → y.regs i = x.regs i + +namespace Realizes + +variable {p : δ → Memory.Prog α} {input : δ ↪ (Fin k → ℕ)} + {output : α ↪ (Fin l → ℕ)} {safe : ℕ → δ → Memory.State → Prop} + {work : δ → Memory.State → ℕ} + +/-- Strengthen the visible entry domain or enlarge the local-work charge. The entry word and +memory bounds may discharge fit conditions; no executable code or domain is hidden by this rule. -/ +theorem consequence {safe' : ℕ → δ → Memory.State → Prop} {work' : δ → Memory.State → ℕ} + (h : Realizes B p input output safe work) + (hsafe : ∀ w d s, (∀ i, Fits w (input d i)) → Memory.WellFormed w s → + safe' w d s → safe w d s) + (hwork : ∀ d s, work d s ≤ work' d s) : Realizes B p input output safe' work' := by + obtain ⟨n, C, emit, he⟩ := h + refine ⟨n, C, emit, ?_⟩ + intro r frontier src tmp hi hd hf hs + obtain ⟨hnw, hrun⟩ := he frontier src tmp hi hd hf hs + refine ⟨hnw, ?_⟩ + intro w d x hx hin hsafe' hv + have hwords (i : Fin k) : Fits w (input d i) := congrFun hin i ▸ hx.1 (src i) + obtain ⟨y, hy, hm, hout, hframe⟩ := + hrun w d x hx hin (hsafe w d _ hwords (wellFormed_memoryState hx) hsafe') hv + exact ⟨y, hy.mono (Nat.mul_le_mul_left C (by have := hwork d (memoryState frontier x); omega)), + hm, hout, hframe⟩ + +/-- Existing defined-evaluation basis simulation preserves the complete realization certificate. -/ +theorem mapSimulates {B' : Basis} (h : Realizes B p input output safe work) + (φ : B.Simulates B') : Realizes B' p input output safe work := by + obtain ⟨n, C, emit, he⟩ := h + refine ⟨n, C, fun frontier src tmp => (emit frontier src tmp).mapSimulates φ, ?_⟩ + intro r frontier src tmp hi hd hf hs + obtain ⟨hnw, hrun⟩ := he frontier src tmp hi hd hf hs + refine ⟨fun h => hnw (Code.readsWidth_of_mapSimulates φ h), ?_⟩ + intro w d x hx hin hsafe hv + obtain ⟨y, hy, hm, hout, hframe⟩ := hrun w d x hx hin hsafe hv + exact ⟨y, hy.mapSimulates φ, hm, hout, hframe⟩ + +/-- The same witness derives the existing semantic `Refines` judgment. The intermediate +representation is fixed by the functional memory abstraction and argument/result registers. -/ +theorem exists_refines (h : Realizes B p input output safe work) : + ∃ n C : ℕ, + ∃ emit : ∀ {r : ℕ}, (frontier : Fin r) → (Fin k → Fin r) → + (Fin (l + n + 1) → Fin r) → Code B r, + ∀ {r : ℕ} (frontier : Fin r) (src : Fin k → Fin r) + (tmp : Fin (l + n + 1) → Fin r), + Function.Injective tmp → (∀ i j, src i ≠ tmp j) → + (∀ j, tmp j ≠ frontier) → (∀ i, src i ≠ frontier) → + ∀ (w : ℕ) (d : δ), + Refines (D := Memory.spec w) w + (fun s x => s = memoryState frontier x ∧ x.Bounded w ∧ x.regs ∘ src = input d ∧ + safe w d s ∧ (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s (p d))) + (p d) + (fun a s y => memoryState frontier y = s ∧ + ∀ i : Fin l, y.regs (tmp (Fin.castAdd (n + 1) i)) = output a i) + (fun s _ => C * (memoryCost (p d) s + work d s + 1)) (emit frontier src tmp) := by + obtain ⟨n, C, emit, he⟩ := h + refine ⟨n, C, emit, fun frontier src tmp hi hd hf hs w d => ?_⟩ + apply Refines.ofFn + rintro s x ⟨rfl, hx, hin, hsafe, hv⟩ + obtain ⟨y, hy, hm, hout, _⟩ := (he frontier src tmp hi hd hf hs).2 w d x hx hin hsafe hv + exact ⟨hv, y, hy, hm, hout⟩ + +end Realizes + +end Cslib.WordRAM.Realization diff --git a/Complexity/Computability/WordRAM/Realization/Bind.lean b/Complexity/Computability/WordRAM/Realization/Bind.lean new file mode 100644 index 0000000000..51d75154ea --- /dev/null +++ b/Complexity/Computability/WordRAM/Realization/Bind.lean @@ -0,0 +1,189 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Realization.Basic +public import Mathlib.Logic.Equiv.Fin.Basic + +/-! +# Uniform sequential realization of memory clients + +The continuation has one realization for the environment paired with its runtime answer. +Its source registers append the first computation's answer slots to the original inputs. +Two disjoint static scratch banks preserve that environment without client-written intermediate +assertions. The continuation's bank already contains the final output slots, so no copy is needed. + +## Main results + +* `Realizes.bind`: sequential realization with additive memory and local-work charges. +-/ + +@[expose] public section + +namespace Cslib.WordRAM.Realization + +open Cslib.DataStructure + +private def bindLeft {m n₂ l n₁ : ℕ} (i : Fin (m + n₂ + 1)) : + Fin (m + (n₂ + l + n₁ + 1) + 1) := ⟨i.val, by omega⟩ + +private def bindRight {m n₂ l n₁ : ℕ} (i : Fin (l + n₁ + 1)) : + Fin (m + (n₂ + l + n₁ + 1) + 1) := ⟨m + n₂ + 1 + i.val, by omega⟩ + +private theorem validHistory_bind {α β : Type} (w : ℕ) (s : Memory.State) + (p : Memory.Prog α) (q : α → Memory.Prog β) : + (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s (p.bind q)) ↔ + (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s p) ∧ + (Memory.spec w).ValidHistory ((Memory.spec w).clientState s p) + ((Memory.spec w).clientHistory ((Memory.spec w).clientState s p) + (q ((Memory.spec w).clientResult s p))) := by + have hh : (Memory.spec w).clientHistory s (p.bind q) = + (Memory.spec w).clientHistory s p ++ + (Memory.spec w).clientHistory ((Memory.spec w).clientState s p) + (q ((Memory.spec w).clientResult s p)) := by + simp only [StateTransitionSpec.clientHistory, StateTransitionSpec.clientState, + StateTransitionSpec.clientResult, StateTransitionSpec.runClient_bind] + rw [hh, StateTransitionSpec.validHistory_append, StateTransitionSpec.run_runClient] + +namespace Realizes + +variable {B : Basis} {δ α β : Type} {k l m : ℕ} + {p : δ → Memory.Prog α} {q : δ × α → Memory.Prog β} + {input : δ ↪ (Fin k → ℕ)} {answer : α ↪ (Fin l → ℕ)} + {output : β ↪ (Fin m → ℕ)} + {safeP : ℕ → δ → Memory.State → Prop} {safeQ : ℕ → δ × α → Memory.State → Prop} + {workP : δ → Memory.State → ℕ} {workQ : δ × α → Memory.State → ℕ} + +/-- One uniform continuation emitter consumes the original environment and the first runtime +answer. Its domain and work charge are evaluated at the deterministic intermediate state. +All emitted code, register banks and the exchange constant precede runtime answers. -/ +theorem bind (hP : Realizes B p input answer safeP workP) + (hQ : Realizes B q ((input.prodMap answer).trans (Fin.appendEquiv k l).toEmbedding) + output safeQ workQ) : + Realizes B (fun d => (p d).bind (fun a => q (d, a))) input output + (fun w d s => safeP w d s ∧ + safeQ w (d, (Memory.spec 0).clientResult s (p d)) + ((Memory.spec 0).clientState s (p d))) + (fun d s => workP d s + + workQ (d, (Memory.spec 0).clientResult s (p d)) + ((Memory.spec 0).clientState s (p d)) + 1) := by + obtain ⟨n₁, C₁, emitP, heP⟩ := hP + obtain ⟨n₂, C₂, emitQ, heQ⟩ := hQ + let N := n₂ + l + n₁ + 1 + let C := max C₁ C₂ + let left : Fin (m + n₂ + 1) → Fin (m + N + 1) := bindLeft + let right : Fin (l + n₁ + 1) → Fin (m + N + 1) := bindRight + let emit : ∀ {r : ℕ}, (frontier : Fin r) → (Fin k → Fin r) → + (Fin (m + N + 1) → Fin r) → Code B r := fun frontier src tmp => + .seq (emitP frontier src (tmp ∘ right)) + (emitQ frontier + (Fin.append src (fun i => tmp (right (Fin.castAdd (n₁ + 1) i)))) (tmp ∘ left)) + refine ⟨N, C, emit, ?_⟩ + intro r frontier src tmp hi hd hf hs + have hleft : Function.Injective (tmp ∘ left) := by + intro a b hab + apply Fin.ext + have h := congrArg Fin.val (hi hab) + exact h + have hright : Function.Injective (tmp ∘ right) := by + intro a b hab + apply Fin.ext + have := congrArg Fin.val (hi hab) + change m + n₂ + 1 + a.val = m + n₂ + 1 + b.val at this + omega + have hdisjoint (a : Fin (l + n₁ + 1)) (b : Fin (m + n₂ + 1)) : + tmp (right a) ≠ tmp (left b) := by + intro hab + have := congrArg Fin.val (hi hab) + change m + n₂ + 1 + a.val = b.val at this + omega + let nextSrc : Fin (k + l) → Fin r := + Fin.append src (fun i => tmp (right (Fin.castAdd (n₁ + 1) i))) + have hnextDisjoint (i : Fin (k + l)) (j : Fin (m + n₂ + 1)) : + nextSrc i ≠ (tmp ∘ left) j := by + refine Fin.addCases (fun a => ?_) (fun b => ?_) i + · simpa only [nextSrc, Fin.append_left, Function.comp_apply] using hd a (left j) + · simpa only [nextSrc, Fin.append_right, Function.comp_apply] using + hdisjoint (Fin.castAdd (n₁ + 1) b) j + have hnextFrontier (i : Fin (k + l)) : nextSrc i ≠ frontier := by + refine Fin.addCases (fun a => ?_) (fun b => ?_) i + · simpa only [nextSrc, Fin.append_left] using hs a + · simpa only [nextSrc, Fin.append_right] using hf (right (Fin.castAdd (n₁ + 1) b)) + obtain ⟨hnwP, hrunP⟩ := heP frontier src (tmp ∘ right) hright + (fun i j => hd i (right j)) (fun j => hf (right j)) hs + obtain ⟨hnwQ, hrunQ⟩ := heQ frontier nextSrc (tmp ∘ left) hleft hnextDisjoint + (fun j => hf (left j)) hnextFrontier + refine ⟨fun h => h.elim hnwP hnwQ, ?_⟩ + intro w d x hx hin hsafe hv + let s := memoryState frontier x + let a := (Memory.spec 0).clientResult s (p d) + let s' := (Memory.spec 0).clientState s (p d) + have hresult : (Memory.spec w).clientResult s (p d) = a := + congrArg Prod.fst (runClient_width w (p d) s) + have hstate : (Memory.spec w).clientState s (p d) = s' := + congrArg (fun z => z.2.1) (runClient_width w (p d) s) + obtain ⟨hvP, hvQ⟩ := (validHistory_bind w s (p d) (fun a => q (d, a))).mp hv + obtain ⟨z, hz, hmP, houtP, hframeP⟩ := hrunP w d x hx hin hsafe.1 hvP + change memoryState frontier z = (Memory.spec w).clientState s (p d) at hmP + rw [hstate] at hmP + change ∀ i : Fin l, z.regs ((tmp ∘ right) (Fin.castAdd (n₁ + 1) i)) = + answer ((Memory.spec w).clientResult s (p d)) i at houtP + rw [hresult] at houtP + have hinQ : z.regs ∘ nextSrc = + ((input.prodMap answer).trans (Fin.appendEquiv k l).toEmbedding) (d, a) := by + funext i + refine Fin.addCases (fun j => ?_) (fun j => ?_) i + · change z.regs (nextSrc (Fin.castAdd l j)) = + Fin.append (input d) (answer a) (Fin.castAdd l j) + simp only [nextSrc, Fin.append_left] + exact (hframeP (src j) (hs j) (fun t => (hd j (right t)).symm)).trans (congrFun hin j) + · change z.regs (nextSrc (Fin.natAdd k j)) = + Fin.append (input d) (answer a) (Fin.natAdd k j) + simpa only [nextSrc, Fin.append_right, Function.comp_apply] using houtP j + have hsafeQ : safeQ w (d, a) (memoryState frontier z) := by + rw [hmP] + exact hsafe.2 + have hvalidQ : (Memory.spec w).ValidHistory (memoryState frontier z) + ((Memory.spec w).clientHistory (memoryState frontier z) (q (d, a))) := by + rw [hmP] + simpa only [hstate, hresult] using hvQ + obtain ⟨y, hy, hmQ, houtQ, hframeQ⟩ := + hrunQ w (d, a) z (hz.bounded hx) hinQ hsafeQ hvalidQ + refine ⟨y, (hz.seq hy).mono ?_, ?_, ?_, ?_⟩ + · change C₁ * (memoryCost (p d) s + workP d s + 1) + + C₂ * (memoryCost (q (d, a)) (memoryState frontier z) + + workQ (d, a) (memoryState frontier z) + 1) ≤ + C * (memoryCost ((p d).bind (fun a => q (d, a))) s + + (workP d s + workQ (d, a) s' + 1) + 1) + rw [hmP, memoryCost_bind] + calc + _ ≤ C * (memoryCost (p d) s + workP d s + 1) + + C * (memoryCost (q (d, a)) s' + workQ (d, a) s' + 1) := + Nat.add_le_add (Nat.mul_le_mul_right _ (Nat.le_max_left _ _)) + (Nat.mul_le_mul_right _ (Nat.le_max_right _ _)) + _ = _ := by + rw [← Nat.mul_add] + congr 1 + dsimp only [a, s'] + omega + · rw [hmP] at hmQ + exact hmQ.trans (by + simp only [StateTransitionSpec.clientState, StateTransitionSpec.runClient_bind] + rw [runClient_width w (p d) s]) + · intro i + have hslot : left (Fin.castAdd (n₂ + 1) i) = Fin.castAdd (N + 1) i := rfl + rw [hmP] at houtQ + simpa only [hslot, Function.comp_apply, StateTransitionSpec.clientResult, + StateTransitionSpec.runClient_bind, runClient_width] using houtQ i + · intro i hif htmp + exact (hframeQ i hif (fun j => htmp (left j))).trans + (hframeP i hif (fun j => htmp (right j))) + +end Realizes + +end Cslib.WordRAM.Realization diff --git a/Complexity/Computability/WordRAM/Realization/Branch.lean b/Complexity/Computability/WordRAM/Realization/Branch.lean new file mode 100644 index 0000000000..3d55738f1e --- /dev/null +++ b/Complexity/Computability/WordRAM/Realization/Branch.lean @@ -0,0 +1,129 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Realization.Basic + +/-! +# Native branching in memory-client realizations + +The branch test reads only the encoded live words. Both alternatives have uniform emitters; +only the selected alternative's domain, fit obligations and local work are required at runtime. +The two-instruction allowance includes the jump on the negative path. + +## Main definitions + +* `test`: interpret an existing native condition on a word-vector environment. + +## Main results + +* `Realizes.branch`: compose two realizations using an actual native conditional. +-/ + +@[expose] public section + +namespace Cslib.WordRAM.Realization + +open Cslib.DataStructure + +variable {B : Basis} {δ α : Type} {k l : ℕ} + +/-- The existing native condition observes the encoded words, without inspecting memory. -/ +def test (c : Cond k) (v : Fin k → ℕ) : Prop := c.Holds ⟨v, fun _ => 0⟩ + +instance (c : Cond k) (v : Fin k → ℕ) : Decidable (test c v) := by + unfold test + infer_instance + +private theorem test_rename {r : ℕ} (c : Cond k) (src : Fin k → Fin r) + (x : Data r) (v : Fin k → ℕ) (h : x.regs ∘ src = v) : + (c.rename src).Holds x ↔ test c v := by + cases c <;> simp only [Cond.rename, Cond.Holds, test] <;> + simp only [← h, Function.comp_apply] + +namespace Realizes + +/-- Both branch emitters precede all runtime values. Shared result slots and disjointness from +live inputs are synthesized internally; the entry domain and work charge select the actual path. -/ +theorem branch {pos neg : δ → Memory.Prog α} + {input : δ ↪ (Fin k → ℕ)} {output : α ↪ (Fin l → ℕ)} + {safePos safeNeg : ℕ → δ → Memory.State → Prop} + {workPos workNeg : δ → Memory.State → ℕ} (c : Cond k) + (hp : Realizes B pos input output safePos workPos) + (hn : Realizes B neg input output safeNeg workNeg) : + Realizes B (fun d => if test c (input d) then pos d else neg d) input output + (fun w d s => if test c (input d) then safePos w d s else safeNeg w d s) + (fun d s => (if test c (input d) then workPos d s else workNeg d s) + 2) := by + obtain ⟨np, Cp, ep, hep⟩ := hp + obtain ⟨nn, Cn, en, hen⟩ := hn + let ip : Fin (l + np + 1) → Fin (l + (np + nn) + 1) := Fin.castLE (by omega) + let ineg : Fin (l + nn + 1) → Fin (l + (np + nn) + 1) := Fin.castLE (by omega) + refine ⟨np + nn, max Cp Cn + 2, fun frontier src tmp => + .branch (c.rename src) (ep frontier src (tmp ∘ ip)) + (en frontier src (tmp ∘ ineg)), ?_⟩ + intro r frontier src tmp hi hd hf hs + have hip : Function.Injective (tmp ∘ ip) := by + intro a b hab + apply Fin.ext + exact congrArg (fun z : Fin (l + (np + nn) + 1) => z.val) (hi hab) + have hin : Function.Injective (tmp ∘ ineg) := by + intro a b hab + apply Fin.ext + exact congrArg (fun z : Fin (l + (np + nn) + 1) => z.val) (hi hab) + obtain ⟨hnp, hrp⟩ := hep frontier src (tmp ∘ ip) hip + (fun i j => hd i (ip j)) (fun j => hf (ip j)) hs + obtain ⟨hnn, hrn⟩ := hen frontier src (tmp ∘ ineg) hin + (fun i j => hd i (ineg j)) (fun j => hf (ineg j)) hs + refine ⟨fun h => h.elim hnp hnn, ?_⟩ + intro w d x hx hin hsafe hv + have hc := test_rename c src x (input d) hin + by_cases ht : test c (input d) + · simp only [ite_eq_left ht] at hsafe hv ⊢ + obtain ⟨y, hy, hm, hout, hframe⟩ := hrp w d x hx hin hsafe hv + refine ⟨y, (hy.branch_pos (hc.mpr ht)).mono ?_, hm, ?_, ?_⟩ + · have hC : Cp ≤ max Cp Cn + 2 := by omega + have hmul := Nat.mul_le_mul_right + (memoryCost (pos d) (memoryState frontier x) + workPos d (memoryState frontier x) + 1) hC + calc + _ ≤ (max Cp Cn + 2) * + (memoryCost (pos d) (memoryState frontier x) + + workPos d (memoryState frontier x) + 1) + 2 := by omega + _ ≤ (max Cp Cn + 2) * + (memoryCost (pos d) (memoryState frontier x) + workPos d (memoryState frontier x) + 1) + + (max Cp Cn + 2) * 2 := by omega + _ = _ := by ring + · intro i + have heq : Fin.castAdd (np + nn + 1) i = ip (Fin.castAdd (np + 1) i) := Fin.ext rfl + rw [heq] + exact hout i + · intro i hif htmp + exact hframe i hif (fun j => htmp (ip j)) + · simp only [ite_eq_right ht] at hsafe hv ⊢ + obtain ⟨y, hy, hm, hout, hframe⟩ := hrn w d x hx hin hsafe hv + refine ⟨y, (hy.branch_neg (fun h => ht (hc.mp h))).mono ?_, hm, ?_, ?_⟩ + · have hC : Cn ≤ max Cp Cn + 2 := by omega + have hmul := Nat.mul_le_mul_right + (memoryCost (neg d) (memoryState frontier x) + workNeg d (memoryState frontier x) + 1) hC + calc + _ ≤ (max Cp Cn + 2) * + (memoryCost (neg d) (memoryState frontier x) + + workNeg d (memoryState frontier x) + 1) + 2 := by omega + _ ≤ (max Cp Cn + 2) * + (memoryCost (neg d) (memoryState frontier x) + workNeg d (memoryState frontier x) + 1) + + (max Cp Cn + 2) * 2 := by omega + _ = _ := by ring + · intro i + have heq : Fin.castAdd (np + nn + 1) i = ineg (Fin.castAdd (nn + 1) i) := Fin.ext rfl + rw [heq] + exact hout i + · intro i hif htmp + exact hframe i hif (fun j => htmp (ineg j)) + +end Realizes + +end Cslib.WordRAM.Realization diff --git a/Complexity/Computability/WordRAM/Realization/Glue.lean b/Complexity/Computability/WordRAM/Realization/Glue.lean new file mode 100644 index 0000000000..faac0fa3e3 --- /dev/null +++ b/Complexity/Computability/WordRAM/Realization/Glue.lean @@ -0,0 +1,70 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Realization.Basic +public import Complexity.Computability.WordRAM.Glue + +/-! +# Certified local computation in memory clients + +The source computation is an ordinary `pure` in `Memory.Prog`, but its native cost is the +certified word-glue budget. The input domain and top-level fit envelope remain visible. +Uniform emitter witnesses are chosen before the runtime environment, width or memory, including +when this rule supplies the continuation of a previous memory read. + +## Main results + +* `Realizes.glue`: reuse a `GlueComputes` emitter, preserving the entire memory and frontier. +-/ + +@[expose] public section + +namespace Cslib.WordRAM.Realization.Realizes + +open Cslib.DataStructure + +variable {B : Basis} {δ : Type} {k : ℕ} {P : (Fin k → ℕ) → Prop} + {f bound : (Fin k → ℕ) → ℕ} {time : ℕ} + +private def glueSlot {n : ℕ} (j : Fin (n + 1)) : Fin (1 + n + 1) := ⟨j.val, by omega⟩ + +/-- Certified scalar computation returns its word, frames memory/frontier/live registers, and +charges its instruction budget even though its source memory-operation history is empty. -/ +theorem glue (input : δ ↪ (Fin k → ℕ)) (h : GlueComputes B P f bound time) : + Realizes B (fun d => pure (f (input d))) input + (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + (fun w d _ => P (input d) ∧ Fits w (bound (input d))) (fun _ _ => time) := by + obtain ⟨_, n, emit, he⟩ := h + refine ⟨n, 1, fun _ src tmp => emit src (fun j => tmp (glueSlot j)), ?_⟩ + intro r frontier src tmp hi hd hf hs + have htmp : Function.Injective (fun j : Fin (n + 1) => tmp (glueSlot j)) := by + intro a b h + apply Fin.ext + exact congrArg (fun z : Fin (1 + n + 1) => z.val) (hi h) + obtain ⟨hg, hrun⟩ := he src (fun j => tmp (glueSlot j)) htmp + (fun i j => hd i (glueSlot j)) + refine ⟨hg.not_readsWidth, ?_⟩ + intro w d x hx hin hsafe hv + obtain ⟨y, hy, hout, hm, hframe⟩ := hrun w x (hin.symm ▸ hsafe.1) (hin.symm ▸ hsafe.2) + refine ⟨y, hy.mono (by change time ≤ 1 * (0 + time + 1); omega), ?_, ?_, ?_⟩ + · apply Memory.State.ext + · exact hm + · exact hframe frontier (fun j => hf (glueSlot j)) + · intro i + have hi : i = 0 := Fin.eq_zero i + subst i + change y.regs (tmp 0) = f (input d) + have hzero : glueSlot (0 : Fin (n + 1)) = 0 := by + apply Fin.ext + simp [glueSlot] + simpa only [hzero, hin] using hout + · intro i hif htmp + exact hframe i (fun j => htmp (glueSlot j)) + +end Cslib.WordRAM.Realization.Realizes diff --git a/Complexity/Computability/WordRAM/Realization/Primitive.lean b/Complexity/Computability/WordRAM/Realization/Primitive.lean new file mode 100644 index 0000000000..cc15b5c64c --- /dev/null +++ b/Complexity/Computability/WordRAM/Realization/Primitive.lean @@ -0,0 +1,206 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Realization.Basic +public import Complexity.Computability.WordRAM.Subroutine + +/-! +# Native memory-query realizations + +The native query arguments are already in the caller's live register environment. Loads and +stores reuse the basis-generic execution rules. Allocation uses exact arithmetic and the +existing generic zero-fill subroutine; it changes the full memory abstraction exactly as the +memory oracle specifies. Argument computation, binds and the final halt are charged by the +surrounding realization rules rather than hidden in these primitives. + +## Main results + +* `Realizes.read`, `Realizes.write`: one-instruction memory operations with live-register frames. +* `Realizes.allocDirty`: exact two-instruction bump allocation, including zero-width empty blocks. +* `Realizes.alloc`: zeroed allocation via the existing basis-generic zero-fill code. +-/ + +@[expose] public section + +namespace Cslib.WordRAM.Realization.Realizes + +open Cslib.DataStructure + +variable {B : Basis} {δ : Type} {k : ℕ} + +local notation "word" => Equiv.toEmbedding (Equiv.symm (Equiv.funUnique (Fin 1) ℕ)) +local notation "unitWords" => Equiv.toEmbedding (Equiv.ofUnique Unit (Fin 0 → ℕ)) + +/-- A read returns a word without changing the frontier or any memory cell. Its address is a +live word; memory admissibility additionally states that the address is allocated. -/ +theorem read (input : δ ↪ (Fin k → ℕ)) (i : Fin k) : + Realizes B (fun d => Memory.read (input d i)) input word + (fun _ _ _ => True) (fun _ _ => 0) := by + refine ⟨0, 1, fun _ src tmp => .op (.load (tmp 0) (src i)), ?_⟩ + intro r frontier src tmp hi hd hf hs + refine ⟨by simp [Code.ReadsWidth, Op.ReadsWidth], ?_⟩ + intro w d x hx hin hsafe hv + have harg : x.regs (src i) = input d i := congrFun hin i + let y := x.set (tmp 0) (x.mem (input d i)) + refine ⟨y, ?_, ?_, ?_, ?_⟩ + · have he := ExecWithin.load (B := B) (w := w) (d := tmp 0) (a := src i) (x := x) + rw [harg] at he + exact he.mono (by change 1 ≤ 1 * (1 + 0 + 1); omega) + · apply Memory.State.ext + · rfl + · change (x.set (tmp 0) _).regs frontier = x.regs frontier + simp [Data.set, Function.update_of_ne (hf 0).symm] + · intro j + have hj : j = 0 := Fin.eq_zero j + subst j + change (x.set (tmp 0) _).regs (tmp 0) = x.mem (input d i) + simp [Data.set] + · intro j hj htmp + exact Function.update_of_ne (htmp 0).symm _ _ + +/-- A store returns unit, updates precisely the selected memory cell and preserves all registers. +The value and allocated-address hypotheses remain in the existing memory valid-history premise. -/ +theorem write (input : δ ↪ (Fin k → ℕ)) (i j : Fin k) : + Realizes B (fun d => Memory.write (input d i) (input d j)) input unitWords + (fun _ _ _ => True) (fun _ _ => 0) := by + refine ⟨0, 1, fun _ src _ => .op (.store (src i) (src j)), ?_⟩ + intro r frontier src tmp hi hd hf hs + refine ⟨by simp [Code.ReadsWidth, Op.ReadsWidth], ?_⟩ + intro w d x hx hin hsafe hv + have hi' : x.regs (src i) = input d i := congrFun hin i + have hj' : x.regs (src j) = input d j := congrFun hin j + let y := x.write (input d i) (input d j) + refine ⟨y, ?_, ?_, ?_, fun _ _ _ => rfl⟩ + · have he := ExecWithin.store (B := B) (w := w) (a := src i) (s := src j) (x := x) + rw [hi', hj'] at he + exact he.mono (by change 1 ≤ 1 * (1 + 0 + 1); omega) + · rfl + · exact fun i => Fin.elim0 i + +/-- Dirty allocation returns the old frontier and adds the count exactly, without touching memory. +No constant `1` is materialized, so a zero-width empty allocation is included. -/ +theorem allocDirty (input : δ ↪ (Fin k → ℕ)) (i : Fin k) : + Realizes .exact (fun d => Memory.allocDirty (input d i)) input word + (fun _ _ _ => True) (fun _ _ => 0) := by + refine ⟨0, 1, fun frontier src tmp => + .seq (.op (.copy (tmp 0) frontier)) (.op (.exactBin .add frontier frontier (src i))), ?_⟩ + intro r frontier src tmp hi hd hf hs + refine ⟨by simp [Code.ReadsWidth, Op.ReadsWidth, Op.exactBin, Basis.exact], ?_⟩ + intro w d x hx hin hsafe hv + have harg : x.regs (src i) = input d i := congrFun hin i + have hfit : Fits w (x.regs frontier + input d i) := hv.1 + let a := x.set (tmp 0) (x.regs frontier) + let y := a.set frontier (x.regs frontier + input d i) + have ha : a.regs frontier = x.regs frontier := + Function.update_of_ne (hf 0).symm _ _ + have hai : a.regs (src i) = input d i := + (Function.update_of_ne (hd i 0) _ _).trans harg + have hcopy : ExecWithin (B := .exact) w (.op (.copy (tmp 0) frontier)) x a 1 := + ExecWithin.copy + have hadd : ExecWithin w (.op (.exactBin .add frontier frontier (src i))) a y 1 := by + apply ExecWithin.op + rw [Op.eval_exactBin, ha, hai] + exact Data.put_of_fits hfit + refine ⟨y, (hcopy.seq hadd).mono (by change 1 + 1 ≤ 1 * (1 + 0 + 1); omega), ?_, ?_, ?_⟩ + · apply Memory.State.ext + · rfl + · change (a.set frontier _).regs frontier = x.regs frontier + input d i + simp [Data.set] + · intro j + have hj : j = 0 := Fin.eq_zero j + subst j + change y.regs (tmp 0) = x.regs frontier + simp [y, a, Data.set, Function.update_of_ne (hf 0)] + · intro j hj htmp + simp [y, a, Data.set, Function.update_of_ne hj, + Function.update_of_ne (htmp 0).symm] + +private def clearPackage {r : ℕ} (frontier base cur zero one : Fin r) : + SubroutineCode .exact r 0 where + allocSpan := .op (.copy base base) + initSetup := .op (.copy base base) + code := .op (.copy base base) + frontierReg := frontier + baseReg := base + initParams := Fin.elim0 + curReg := cur + zeroReg := zero + oneReg := one + incCur := .exactBin .add cur cur one + input := fun _ => false + output := base + +/-- Zeroed allocation realizes the full oracle memory update on arbitrary old scratch contents. +The reused clear code materializes `1` only in the positive-size branch. -/ +theorem alloc (input : δ ↪ (Fin k → ℕ)) (i : Fin k) : + Realizes .exact (fun d => Memory.alloc (input d i)) input word + (fun _ _ _ => True) (fun _ _ => 0) := by + refine ⟨2, 4, fun frontier src tmp => + .seq (.op (.copy (tmp 0) frontier)) <| + .seq (.op (.exactBin .add frontier frontier (src i))) + (clearPackage frontier (tmp 0) (tmp 1) (tmp 2) (tmp 3)).zeroFillCode, ?_⟩ + intro r frontier src tmp hi hd hf hs + refine ⟨by simp [Code.ReadsWidth, Op.ReadsWidth, Op.exactBin, Basis.exact, + clearPackage, SubroutineCode.zeroFillCode], ?_⟩ + intro w d x hx hin hsafe hv + have harg : x.regs (src i) = input d i := congrFun hin i + have hfit : Fits w (x.regs frontier + input d i) := hv.1 + let a := x.set (tmp 0) (x.regs frontier) + let b := a.set frontier (x.regs frontier + input d i) + let P := clearPackage frontier (tmp 0) (tmp 1) (tmp 2) (tmp 3) + have ha : a.regs frontier = x.regs frontier := Function.update_of_ne (hf 0).symm _ _ + have hai : a.regs (src i) = input d i := + (Function.update_of_ne (hd i 0) _ _).trans harg + have hcopy : ExecWithin (B := .exact) w (.op (.copy (tmp 0) frontier)) x a 1 := + ExecWithin.copy + have hadd : ExecWithin w (.op (.exactBin .add frontier frontier (src i))) a b 1 := by + apply ExecWithin.op + rw [Op.eval_exactBin, ha, hai] + exact Data.put_of_fits hfit + have hclear : [P.curReg, P.zeroReg, P.oneReg, P.baseReg, P.frontierReg].Nodup := by + simp [P, clearPackage, hi.eq_iff, hf] + have hinc {z : Data r} (hz : z.regs P.oneReg = 1) (hfit : Fits w (z.regs P.curReg + 1)) : + P.incCur.eval w z = some (z.set P.curReg (z.regs P.curReg + 1)) := by + change z.regs (tmp 3) = 1 at hz + change Fits w (z.regs (tmp 1) + 1) at hfit + change (Op.exactBin .add (tmp 1) (tmp 1) (tmp 3)).eval w z = + some (z.set (tmp 1) (z.regs (tmp 1) + 1)) + rw [Op.eval_exactBin, hz] + exact Data.put_of_fits hfit + have hbbase : b.regs P.baseReg = x.regs frontier := by + simp [P, clearPackage, b, a, Data.set, Function.update_of_ne (hf 0)] + have hbfront : b.regs P.frontierReg = x.regs frontier + input d i := by + simp [P, clearPackage, b, Data.set] + obtain ⟨y, hy, hby, hbase, hfront, _, hzero, hmod⟩ := + P.zeroFillCode_exec (params := Fin.elim0) hclear (fun j => Fin.elim0 j) hinc + ((hcopy.seq hadd).bounded hx) + hbbase hbfront (fun j => Fin.elim0 j) + have hmem : y.mem = clear x.mem (x.regs frontier) (input d i) := by + funext addr + by_cases ha : (Block.mk (x.regs frontier) (input d i)).Owns addr + · rw [clear_of_owns ha] + exact hzero addr (Block.mem_footprint.mpr ha) + · rw [clear_of_not_owns ha] + exact hmod addr (fun h => ha (Block.mem_footprint.mp h)) + have hwhole := hcopy.seq (hadd.seq hy) + refine ⟨y, hwhole.mono (by change 1 + (1 + (4 * input d i + 6)) ≤ + 4 * ((input d i + 1) + 0 + 1); omega), ?_, ?_, ?_⟩ + · apply Memory.State.ext + · exact hmem + · exact hfront + · intro j + have hj : j = 0 := Fin.eq_zero j + subst j + exact hbase + · intro j hj htmp + apply hwhole.regs_eq + simp [Code.Preserves, Op.dest, Op.exactBin, P, clearPackage, + SubroutineCode.zeroFillCode, Ne.symm hj, htmp] + +end Cslib.WordRAM.Realization.Realizes diff --git a/ComplexityTests/WordRAMRealizationBind.lean b/ComplexityTests/WordRAMRealizationBind.lean new file mode 100644 index 0000000000..8667ae3a17 --- /dev/null +++ b/ComplexityTests/WordRAMRealizationBind.lean @@ -0,0 +1,61 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.WordRAM.Realization.Bind +import Complexity.Computability.WordRAM.Realization.Primitive + +/-! A uniform continuation reads the address returned by allocation, not a statically fixed cell. -/ + +namespace ComplexityTests.WordRAMRealizationBind + +open Cslib.DataStructure Cslib.WordRAM Cslib.WordRAM.Realization + +private def word : ℕ ↪ (Fin 1 → ℕ) := (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + +private def environment : ℕ × ℕ ↪ (Fin 2 → ℕ) := + (word.prodMap word).trans (Fin.appendEquiv 1 1).toEmbedding + +private def allocatedRead (count : ℕ) : Memory.Prog ℕ := + (Memory.alloc count).bind Memory.read + +-- The two source emitters are uniform over all initial counts and all returned addresses. +-- Only their existing primitive certificates are supplied: no intermediate assertion or +-- manually chosen register layout is needed by the bind client. +example : Realizes .exact allocatedRead word word + (fun _ _ _ => True) (fun _ _ => 1) := by + have hp : Realizes .exact (fun count => Memory.alloc count) word word + (fun _ _ _ => True) (fun _ _ => 0) := Realizes.alloc word 0 + have hq : Realizes .exact (fun da : ℕ × ℕ => Memory.read da.2) environment word + (fun _ _ _ => True) (fun _ _ => 0) := Realizes.read environment 1 + have h := Realizes.bind hp hq + exact h.consequence (fun _ _ _ _ _ _ => ⟨trivial, trivial⟩) (fun _ _ => le_rfl) + +private def initial : Memory.State := ⟨fun _ => 7, 3⟩ + +-- This is a nonempty admissible instance; the allocated base is 3 while the input count is 2. +example : Memory.WellFormed 4 initial := by + constructor + · decide + · intro address + change Fits 4 7 + decide + +example : (Memory.spec 4).ValidHistory initial + ((Memory.spec 4).clientHistory initial (allocatedRead 2)) := by decide + +example : (Memory.spec 4).clientResult initial (allocatedRead 2) = 0 := by decide + +example : ((Memory.spec 4).clientState initial (allocatedRead 2)).frontier = 5 := by decide + +-- The abstraction retains cells outside the allocated block, including inaccessible cells. +example : ((Memory.spec 4).clientState initial (allocatedRead 2)).mem 10 = 7 := by decide + +-- Empty allocation cannot justify reading its returned frontier: the existing memory domain +-- remains a genuine obligation of the composed realization. +example : ¬(Memory.spec 4).ValidHistory initial + ((Memory.spec 4).clientHistory initial (allocatedRead 0)) := by decide + +end ComplexityTests.WordRAMRealizationBind diff --git a/ComplexityTests/WordRAMRealizationBranch.lean b/ComplexityTests/WordRAMRealizationBranch.lean new file mode 100644 index 0000000000..9b027819f9 --- /dev/null +++ b/ComplexityTests/WordRAMRealizationBranch.lean @@ -0,0 +1,52 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: local-only (tests of realization branching) + +public import Complexity.Computability.WordRAM.Realization.Branch +public import Complexity.Computability.WordRAM.Realization.Glue + +/-! The branch that needs `1` is taken only when an input word is positive. Thus the same fixed +program also works at width zero on two zero inputs, without requiring `Fits 0 1`. -/ + +public section + +open Cslib.WordRAM Cslib.WordRAM.Realization + +namespace WordRAMRealizationBranchTests + +@[expose] def input : (Fin 2 → ℕ) ↪ (Fin 2 → ℕ) := Function.Embedding.refl _ + +@[expose] def output : ℕ ↪ (Fin 1 → ℕ) := (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + +theorem branch_fits_on_selected_path : Realizes .exact + (fun v => if v 0 < v 1 then pure 1 else pure 0) input output + (fun _ _ _ => True) (fun _ _ => 3) := by + have hp := Realizes.glue input + (GlueComputes.const (B := .exact) (P := fun _ : Fin 2 → ℕ => True) + (bound := fun _ => 1) 1 (fun _ _ => le_rfl)) + have hn := Realizes.glue input + (GlueComputes.const (B := .exact) (P := fun _ : Fin 2 → ℕ => True) + (bound := fun _ => 0) 0 (fun _ _ => le_rfl)) + have h := Realizes.branch (.lt 0 1) hp hn + apply h.consequence + · intro w d s hwords hmem hs + by_cases hd : d 0 < d 1 + · simp only [test, Cond.Holds, input, Function.Embedding.refl_apply, + ite_eq_left hd] + exact ⟨trivial, (hwords 1).of_le (by change 1 ≤ d 1; omega)⟩ + · simp only [test, Cond.Holds, input, Function.Embedding.refl_apply, + ite_eq_right hd] + exact ⟨trivial, fits_zero w⟩ + · intro d s + split <;> exact le_rfl + +example : ¬ Fits 0 1 := by decide + +example : ¬ test (.lt 0 1) (fun _ : Fin 2 => 0) := by decide + +end WordRAMRealizationBranchTests diff --git a/ComplexityTests/WordRAMRealizationPrimitive.lean b/ComplexityTests/WordRAMRealizationPrimitive.lean new file mode 100644 index 0000000000..fd02aca415 --- /dev/null +++ b/ComplexityTests/WordRAMRealizationPrimitive.lean @@ -0,0 +1,215 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.WordRAM.Realization.Primitive + +/-! +# Concrete clients of native memory-query realization + +The clients instantiate the emitted native code on dirty memory, retain an unrelated live word, +and inspect the returned word, frontier and entire resulting memory. Empty zeroed allocation is +also instantiated at word width zero, where the constant `1` cannot be written. +-/ + +namespace ComplexityTests.WordRAMRealizationPrimitive + +open Cslib.WordRAM Cslib.WordRAM.Realization Cslib.DataStructure + +private abbrev wordWords : ℕ ↪ (Fin 1 → ℕ) := + Equiv.toEmbedding (Equiv.symm (Equiv.funUnique (Fin 1) ℕ)) + +private def initial {r : ℕ} (frontier arg marker : ℕ) (mem : ℕ → ℕ) : Data r := + ⟨fun j => if j.val = 0 then frontier else if j.val = 1 then arg + else if j.val = 2 then marker else 0, mem⟩ + +/-- A canonical layout keeps the frontier, argument and a live marker before the fresh bank. -/ +private theorem word_query {B : Basis} (p : ℕ → Memory.Prog ℕ) + (h : Realizes B p wordWords wordWords (fun _ _ _ => True) (fun _ _ => 0)) + (w frontier arg marker : ℕ) (mem : ℕ → ℕ) + (hf : Fits w frontier) (ha : Fits w arg) (hm : ∀ a, Fits w (mem a)) + (hmarker : Fits w marker) + (hv : (Memory.spec w).ValidHistory ⟨mem, frontier⟩ + ((Memory.spec w).clientHistory ⟨mem, frontier⟩ (p arg))) : + ∃ n C, ∃ code : Code B (3 + (1 + n + 1)), ∃ y : Data (3 + (1 + n + 1)), + ExecWithin w code (initial frontier arg marker mem) y + (C * (memoryCost (p arg) ⟨mem, frontier⟩ + 1)) ∧ + memoryState ⟨0, by omega⟩ y = (Memory.spec w).clientState ⟨mem, frontier⟩ (p arg) ∧ + y.regs ⟨3, by omega⟩ = (Memory.spec w).clientResult ⟨mem, frontier⟩ (p arg) ∧ + y.regs ⟨1, by omega⟩ = arg ∧ y.regs ⟨2, by omega⟩ = marker := by + obtain ⟨n, C, emit, he⟩ := h + let f : Fin (3 + (1 + n + 1)) := ⟨0, by omega⟩ + let src : Fin 1 → Fin (3 + (1 + n + 1)) := fun _ => ⟨1, by omega⟩ + let tmp : Fin (1 + n + 1) → Fin (3 + (1 + n + 1)) := Fin.natAdd 3 + have hi : Function.Injective tmp := by + intro a b hab + apply Fin.ext + have hval := congrArg Fin.val hab + change 3 + a.val = 3 + b.val at hval + omega + have hd (i : Fin 1) (j : Fin (1 + n + 1)) : src i ≠ tmp j := by + intro hij + have hval := congrArg Fin.val hij + change 1 = 3 + j.val at hval + omega + have htf (j : Fin (1 + n + 1)) : tmp j ≠ f := by + intro hj + have hval := congrArg Fin.val hj + change 3 + j.val = 0 at hval + omega + have hsf (i : Fin 1) : src i ≠ f := by + intro hi + have hval := congrArg Fin.val hi + change 1 = 0 at hval + omega + let x : Data (3 + (1 + n + 1)) := initial frontier arg marker mem + have hx : x.Bounded w := by + refine ⟨fun j => ?_, hm⟩ + change Fits w (if j.val = 0 then frontier else if j.val = 1 then arg + else if j.val = 2 then marker else 0) + split_ifs <;> first | exact hf | exact ha | exact hmarker | exact fits_zero w + have hin : x.regs ∘ src = wordWords arg := by + funext i + rfl + obtain ⟨y, hy, hstate, hout, hframe⟩ := (he f src tmp hi hd htf hsf).2 w arg x hx hin + trivial hv + have hxs : memoryState f x = ⟨mem, frontier⟩ := rfl + rw [hxs] at hy hstate hout + have ho := hout 0 + change y.regs ⟨3, by omega⟩ = (Memory.spec w).clientResult ⟨mem, frontier⟩ (p arg) at ho + have harg := hframe ⟨1, by omega⟩ + (by intro h; have hval := congrArg Fin.val h; change 1 = 0 at hval; omega) + (fun j h => by have hval := congrArg Fin.val h; change 3 + j.val = 1 at hval; omega) + have hmark := hframe ⟨2, by omega⟩ + (by intro h; have hval := congrArg Fin.val h; change 2 = 0 at hval; omega) + (fun j h => by have hval := congrArg Fin.val h; change 3 + j.val = 2 at hval; omega) + refine ⟨n, C, emit f src tmp, y, ?_, hstate, ho, harg, hmark⟩ + simpa only [Nat.add_zero] using hy + +-- Clearing a dirty block preserves both the allocated prefix and inaccessible high cells. +example : + ∃ n C, ∃ code : Code .exact (3 + (1 + n + 1)), ∃ y : Data (3 + (1 + n + 1)), + ExecWithin 4 code (initial 3 2 9 (fun _ => 7)) y (C * 4) ∧ + y.regs ⟨0, by omega⟩ = 5 ∧ y.regs ⟨3, by omega⟩ = 3 ∧ + y.regs ⟨1, by omega⟩ = 2 ∧ y.regs ⟨2, by omega⟩ = 9 ∧ + y.mem 3 = 0 ∧ y.mem 4 = 0 ∧ y.mem 2 = 7 ∧ y.mem 100 = 7 := by + obtain ⟨n, C, code, y, he, hs, ho, ha, hl⟩ := + word_query Memory.alloc (Realizes.alloc wordWords 0) 4 3 2 9 (fun _ => 7) + (by norm_num [Fits]) (by norm_num [Fits]) (fun _ => by norm_num [Fits]) + (by norm_num [Fits]) (by change Fits 4 (3 + 2) ∧ True; norm_num [Fits]) + have hf := congrArg Memory.State.frontier hs + have hm := congrArg Memory.State.mem hs + change y.mem = clear (fun _ => 7) 3 2 at hm + refine ⟨n, C, code, y, he, hf, ho, ha, hl, ?_⟩ + rw [hm] + norm_num [clear] + +-- At width zero only zero fits, but allocating an empty block still has a native realization. +example : + ∃ n C, ∃ code : Code .exact (3 + (1 + n + 1)), ∃ y : Data (3 + (1 + n + 1)), + ExecWithin 0 code (initial 0 0 0 (fun _ => 0)) y (C * 2) ∧ + y.regs ⟨0, by omega⟩ = 0 ∧ y.regs ⟨3, by omega⟩ = 0 ∧ + y.mem = (fun _ => 0) ∧ y.regs ⟨2, by omega⟩ = 0 := by + obtain ⟨n, C, code, y, he, hs, ho, _, hl⟩ := + word_query Memory.alloc (Realizes.alloc wordWords 0) 0 0 0 0 (fun _ => 0) + (fits_zero 0) (fits_zero 0) (fun _ => fits_zero 0) (fits_zero 0) + (by change Fits 0 (0 + 0) ∧ True; exact ⟨fits_zero 0, trivial⟩) + have hf := congrArg Memory.State.frontier hs + have hm := congrArg Memory.State.mem hs + change y.mem = clear (fun _ => 0) 0 0 at hm + rw [clear_zero] at hm + exact ⟨n, C, code, y, he, hf, ho, hm, hl⟩ + +-- Dirty allocation retains every old memory value while returning the old frontier. +example : + ∃ n C, ∃ code : Code .exact (3 + (1 + n + 1)), ∃ y : Data (3 + (1 + n + 1)), + ExecWithin 4 code (initial 3 2 9 (fun _ => 7)) y (C * 2) ∧ + y.regs ⟨0, by omega⟩ = 5 ∧ y.regs ⟨3, by omega⟩ = 3 ∧ + y.mem = (fun _ => 7) ∧ y.regs ⟨2, by omega⟩ = 9 := by + obtain ⟨n, C, code, y, he, hs, ho, _, hl⟩ := + word_query Memory.allocDirty (Realizes.allocDirty wordWords 0) 4 3 2 9 (fun _ => 7) + (by norm_num [Fits]) (by norm_num [Fits]) (fun _ => by norm_num [Fits]) + (by norm_num [Fits]) (by change Fits 4 (3 + 2) ∧ True; norm_num [Fits]) + exact ⟨n, C, code, y, he, congrArg Memory.State.frontier hs, ho, + congrArg Memory.State.mem hs, hl⟩ + +-- Reading an allocated word keeps the entire dirty memory and an unrelated live register. +example : + ∃ n C, ∃ code : Code .exact (3 + (1 + n + 1)), ∃ y : Data (3 + (1 + n + 1)), + ExecWithin 4 code (initial 3 2 9 (fun _ => 7)) y (C * 2) ∧ + y.regs ⟨0, by omega⟩ = 3 ∧ y.regs ⟨3, by omega⟩ = 7 ∧ + y.mem = (fun _ => 7) ∧ y.regs ⟨2, by omega⟩ = 9 := by + obtain ⟨n, C, code, y, he, hs, ho, _, hl⟩ := + word_query Memory.read (Realizes.read (B := .exact) wordWords 0) 4 3 2 9 (fun _ => 7) + (by norm_num [Fits]) (by norm_num [Fits]) (fun _ => by norm_num [Fits]) + (by norm_num [Fits]) (by change 2 < 3 ∧ True; norm_num) + exact ⟨n, C, code, y, he, congrArg Memory.State.frontier hs, ho, + congrArg Memory.State.mem hs, hl⟩ + +private def pairWords : ℕ × ℕ ↪ (Fin 2 → ℕ) where + toFun x := ![x.1, x.2] + inj' := by + intro a b h + apply Prod.ext + · exact congrFun h 0 + · exact congrFun h 1 + +private def writeInitial {r : ℕ} : Data r := + ⟨fun j => if j.val = 0 then 3 else if j.val = 1 then 2 else if j.val = 2 then 12 else 9, + fun _ => 7⟩ + +-- A store changes its allocated cell while preserving low/high memory and a live register. +example : + ∃ n C, ∃ code : Code .exact (4 + (0 + n + 1)), ∃ y : Data (4 + (0 + n + 1)), + ExecWithin 4 code writeInitial y (C * 2) ∧ + y.regs ⟨0, by omega⟩ = 3 ∧ y.mem 2 = 12 ∧ y.mem 0 = 7 ∧ y.mem 100 = 7 ∧ + y.regs ⟨3, by omega⟩ = 9 := by + obtain ⟨n, C, emit, he⟩ := Realizes.write (B := .exact) pairWords 0 1 + let f : Fin (4 + (0 + n + 1)) := ⟨0, by omega⟩ + let src : Fin 2 → Fin (4 + (0 + n + 1)) := fun i => ⟨1 + i.val, by omega⟩ + let tmp : Fin (0 + n + 1) → Fin (4 + (0 + n + 1)) := Fin.natAdd 4 + have hi : Function.Injective tmp := by + intro a b hab + apply Fin.ext + have hval := congrArg Fin.val hab + change 4 + a.val = 4 + b.val at hval + omega + have hd (i : Fin 2) (j : Fin (0 + n + 1)) : src i ≠ tmp j := by + intro hij + have hval := congrArg Fin.val hij + change 1 + i.val = 4 + j.val at hval + omega + have htf (j : Fin (0 + n + 1)) : tmp j ≠ f := by + intro hj + have hval := congrArg Fin.val hj + change 4 + j.val = 0 at hval + omega + have hsf (i : Fin 2) : src i ≠ f := by + intro hi + have hval := congrArg Fin.val hi + change 1 + i.val = 0 at hval + omega + let x : Data (4 + (0 + n + 1)) := writeInitial + have hx : x.Bounded 4 := by + refine ⟨fun j => ?_, fun _ => by change Fits 4 7; norm_num [Fits]⟩ + change Fits 4 (if j.val = 0 then 3 else if j.val = 1 then 2 else if j.val = 2 then 12 else 9) + split_ifs <;> norm_num [Fits] + have hin : x.regs ∘ src = pairWords (2, 12) := by + funext i + fin_cases i <;> rfl + have hv : (Memory.spec 4).ValidHistory (memoryState f x) + ((Memory.spec 4).clientHistory (memoryState f x) (Memory.write 2 12)) := by + change (2 < 3 ∧ Fits 4 12) ∧ True + norm_num [Fits] + obtain ⟨y, hy, hs, _, hframe⟩ := (he f src tmp hi hd htf hsf).2 4 (2, 12) x hx hin trivial hv + have hm := congrArg Memory.State.mem hs + change y.mem = Function.update (fun _ => 7) 2 12 at hm + have hf := congrArg Memory.State.frontier hs + have hl := hframe ⟨3, by omega⟩ + (by intro h; have hval := congrArg Fin.val h; change 3 = 0 at hval; omega) + (fun j h => by have hval := congrArg Fin.val h; change 4 + j.val = 3 at hval; omega) + refine ⟨n, C, emit f src tmp, y, hy, hf, ?_, ?_, ?_, hl⟩ <;> rw [hm] <;> norm_num + +end ComplexityTests.WordRAMRealizationPrimitive diff --git a/ROADMAP.md b/ROADMAP.md index 11d44c8c06..678d8e30d8 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -631,6 +631,14 @@ reusing graph and matroid foundations and the shared data-structure interfaces. `3704*(p+1)²*(n+1)^2.81` instructions including halt. Its source-regime admission and width-independent prefix bounds are proved; selecting the actual minimum-count prime and the global endpoint remain separate. +- **Memory-oracle realization** ([#2205], [#2213]): the foundational graded kernel certifies + all four memory queries, register-word glue, native branches and binds with one fixed + continuation emitter for all runtime replies. Its functional representation retains the + complete memory and frontier, and its instruction currency adds local work to memory charges. + Remaining: bounded runtime loops, charged input installation and flat programs, operation-level + memory implementation and framed upper-ADT adapters. The full C2/M1b and queue transfer + objectives remain open; this foundation does not turn arbitrary host continuations into + machine-time certificates. - **Stateful algorithms and data structures:** Word-RAM controllers for larger adaptive and iterative clients, including the two-stack queue and timeline-editing controllers; reusable allocation and deallocation, shrinking, and live and peak space accounting; ownership and @@ -774,3 +782,5 @@ not planned, not as proved. [#2019]: https://github.com/cslib-community/complexitylib/issues/2019 [#2062]: https://github.com/cslib-community/complexitylib/issues/2062 [#2063]: https://github.com/cslib-community/complexitylib/issues/2063 +[#2205]: https://github.com/cslib-community/complexitylib/issues/2205 +[#2213]: https://github.com/cslib-community/complexitylib/issues/2213 diff --git a/UPSTREAM.md b/UPSTREAM.md index 0aebe288d0..7a8ebc36cb 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -510,6 +510,12 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he representation from the standard input. `WordRAM/Function` gives separate nonuniform and uniform RAM function-time predicates, with named-certificate reuse by `polytime`. Arbitrary continuation synthesis, iterative clients and a Turing-machine simulation remain open. + The bounded `WordRAM/Realization` foundation (#2213, under #2205) reuses the memory + oracle and the same native `Code`/`ExecWithin` semantics. Its explicit faithful register + encodings support all four memory queries, certified word glue, branches and uniform + reply-dependent bind, charging local work as well as memory operations. Query arguments are + already in live registers; whole-input installation, final halt, bounded-loop and local-ADT + adapter endpoints remain separate work. This adds no abstract-catalogue headline. `DataStructure/OrderedSet` and `DataStructure/OrderedSet/*` provide neutral optional-minimum contracts, a typed finite-set interface including deletion, and adaptive minimum extraction, with no tree or cost dependency. Bounded-key implementations may specialize From d8f6bea19d10e824d0045e4ed832fbc70d182177 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 18:27:47 -0500 Subject: [PATCH 14/27] fix(WordRAM): normalize the memory return simp rule --- Complexity/Computability/WordRAM/Realization/Basic.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Complexity/Computability/WordRAM/Realization/Basic.lean b/Complexity/Computability/WordRAM/Realization/Basic.lean index 12c4ef7608..07f59ad67b 100644 --- a/Complexity/Computability/WordRAM/Realization/Basic.lean +++ b/Complexity/Computability/WordRAM/Realization/Basic.lean @@ -72,7 +72,7 @@ theorem runClient_width (w : ℕ) (p : Memory.Prog α) (s : Memory.State) : /-- A semantic return issues no memory operation. Its native glue is charged separately. -/ @[simp] theorem memoryCost_pure (a : α) (s : Memory.State) : - memoryCost (.pure a) s = 0 := rfl + memoryCost (pure a) s = 0 := rfl /-- Memory charges add at the actual intermediate state; the local bind charge is separate. -/ theorem memoryCost_bind {β : Type} (p : Memory.Prog α) (q : α → Memory.Prog β) From 4977e9bf3c417ebb33a2d39a73f17d169a463863 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 18:46:48 -0500 Subject: [PATCH 15/27] feat(WordRAM): realize bounded loops and initialized client programs Reuse one body certificate for runtime fuel, charging every iteration and all local work even without memory queries. Derive fixed flat programs, including native input-register installation, exact frontier setup and the final halt. Unit observations execute an additional real zero instruction. This is the loop/whole-program milestone under #2213 and #2205. Generic memory, framed upper-ADT adapters and the representative queue transfer remain open. Existing selected abstracts and headline scope are unchanged. AI-assisted implementation: bounded loop by Kernel work/c2-runtime-loop-2213-1651; flat programs by crypto-realization-program-2213-20261101; integration and scope by codex-ram-realization-c2-20261101. Prior foundational source remains unchanged. --- Complexity.lean | 2 + .../WordRAM/Realization/Loop.lean | 509 ++++++++++++++++++ .../WordRAM/Realization/Program.lean | 345 ++++++++++++ ComplexityTests/WordRAMRealizationLoop.lean | 193 +++++++ .../WordRAMRealizationProgram.lean | 294 ++++++++++ ROADMAP.md | 9 +- UPSTREAM.md | 9 +- 7 files changed, 1354 insertions(+), 7 deletions(-) create mode 100644 Complexity/Computability/WordRAM/Realization/Loop.lean create mode 100644 Complexity/Computability/WordRAM/Realization/Program.lean create mode 100644 ComplexityTests/WordRAMRealizationLoop.lean create mode 100644 ComplexityTests/WordRAMRealizationProgram.lean diff --git a/Complexity.lean b/Complexity.lean index b58e9959ef..25721a3ac3 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2560,7 +2560,9 @@ public import Complexity.Computability.WordRAM.Realization.Basic public import Complexity.Computability.WordRAM.Realization.Bind public import Complexity.Computability.WordRAM.Realization.Branch public import Complexity.Computability.WordRAM.Realization.Glue +public import Complexity.Computability.WordRAM.Realization.Loop public import Complexity.Computability.WordRAM.Realization.Primitive +public import Complexity.Computability.WordRAM.Realization.Program public import Complexity.Computability.WordRAM.RegisterFrame public import Complexity.Computability.WordRAM.Sequence public import Complexity.Computability.WordRAM.Signed.Arithmetic diff --git a/Complexity/Computability/WordRAM/Realization/Loop.lean b/Complexity/Computability/WordRAM/Realization/Loop.lean new file mode 100644 index 0000000000..9081953bdc --- /dev/null +++ b/Complexity/Computability/WordRAM/Realization/Loop.lean @@ -0,0 +1,509 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Realization.Basic + +/-! +# Bounded memory-client loops + +Bounded iteration remains an ordinary `Memory.Prog` term. The local-work currency counts body +work and every iteration, including iterations issuing no memory query. The native realization +uses one fixed body emitter and static registers for the mutable state and countdown counter. + +## Main definitions + +* `iterate`: bounded iteration in the existing memory-client monad. +* `iterationWork`, `iterationSafe`: local work and domains along the actual semantic states. + +## Main results + +* `copyBank_exec`: a static register-bank copy with memory and live-register frames. +* `loop_invariant_state_charged`: bounded native loops charge local work and control. +* `Realizes.iterateRuntime`: one uniform body certificate realizes a runtime-bounded loop. + +The iteration grading and realization are original infrastructure over the existing free monad +and structured Word-RAM code. No additional program syntax or memory model is introduced. +-/ + +@[expose] public section + +namespace Cslib.WordRAM.Realization + +open Cslib.DataStructure + +variable {δ σ : Type} {B : Basis} {r k l : ℕ} + +/-- Execute one fixed body family a runtime-bounded number of times. -/ +def iterate (body : δ × σ → Memory.Prog σ) (d : δ) : ℕ → σ → Memory.Prog σ + | 0, s => pure s + | n + 1, s => do + let s' ← body (d, s) + iterate body d n s' + +/-- Local computation and one control-work unit per iteration, along actual semantic states. -/ +def iterationWork (body : δ × σ → Memory.Prog σ) (work : (δ × σ) → Memory.State → ℕ) + (d : δ) : ℕ → σ → Memory.State → ℕ + | 0, _, _ => 0 + | n + 1, a, s => + work (d, a) s + iterationWork body work d n + ((Memory.spec 0).clientResult s (body (d, a))) + ((Memory.spec 0).clientState s (body (d, a))) + 1 + +/-- Every executed body must satisfy its explicit domain at the reached state. -/ +def iterationSafe (body : δ × σ → Memory.Prog σ) + (safe : ℕ → (δ × σ) → Memory.State → Prop) (w : ℕ) (d : δ) : + ℕ → σ → Memory.State → Prop + | 0, _, _ => True + | n + 1, a, s => safe w (d, a) s ∧ iterationSafe body safe w d n + ((Memory.spec 0).clientResult s (body (d, a))) + ((Memory.spec 0).clientState s (body (d, a))) + +/-- Native register copies need no word-size or arithmetic side condition. -/ +def copyBank : {k : ℕ} → (Fin k → Fin r) → (Fin k → Fin r) → Fin r → Code B r + | 0, _, _, idle => .op (.copy idle idle) + | n + 1, src, dst, idle => .seq (.op (.copy (dst 0) (src 0))) + (copyBank (fun i : Fin n => src i.succ) (fun i => dst i.succ) idle) + +/-- Copying a finite bank does not inspect the word width. -/ +theorem copyBank_not_readsWidth (src dst : Fin k → Fin r) (idle : Fin r) : + ¬(copyBank (B := B) src dst idle).ReadsWidth := by + induction k with + | zero => simp [copyBank, Code.ReadsWidth, Op.ReadsWidth] + | succ n ih => simp [copyBank, Code.ReadsWidth, Op.ReadsWidth, ih] + +/-- Copy disjoint source words into an injective destination bank, leaving the entire memory and +all other registers unchanged. A neutral copy supplies the zero-length code case. -/ +theorem copyBank_exec (src dst : Fin k → Fin r) (idle : Fin r) + (hi : Function.Injective dst) (hd : ∀ i j, src i ≠ dst j) (w : ℕ) (x : Data r) : + ∃ y, ExecWithin w (copyBank (B := B) src dst idle) x y (k + 1) ∧ + y.mem = x.mem ∧ (∀ i, y.regs (dst i) = x.regs (src i)) ∧ + ∀ j, (∀ i, dst i ≠ j) → y.regs j = x.regs j := by + induction k generalizing x with + | zero => + have he : ExecWithin (B := B) w (.op (.copy idle idle)) x x 1 := by + convert (ExecWithin.copy (B := B) (w := w) (d := idle) (s := idle) (x := x)) using 1 + apply Data.ext <;> simp [Data.set] + exact ⟨x, he, rfl, fun i => Fin.elim0 i, fun _ _ => rfl⟩ + | succ n ih => + let a := x.set (dst 0) (x.regs (src 0)) + obtain ⟨y, hy, hm, hout, hframe⟩ := ih + (fun i : Fin n => src i.succ) (fun i => dst i.succ) + (fun i j h => Fin.succ_injective _ (hi h)) + (fun i j => hd i.succ j.succ) a + have he : ExecWithin (B := B) w (.op (.copy (dst 0) (src 0))) x a 1 := + ExecWithin.copy + refine ⟨y, ?_, hm, ?_, ?_⟩ + · simpa only [copyBank, Nat.add_assoc, Nat.add_left_comm, Nat.add_comm] using he.seq hy + · intro i + refine Fin.cases ?_ (fun j => ?_) i + · rw [hframe (dst 0) (fun j h => Fin.succ_ne_zero _ (hi h))] + simp [a, Data.set] + · rw [hout] + exact Function.update_of_ne (hd j.succ 0) _ _ + · intro j hj + rw [hframe j (fun i => hj i.succ)] + exact Function.update_of_ne (hj 0).symm _ _ + +/-- The result of the memory interpreter is independent of the admissibility width. -/ +theorem clientResult_width (w : ℕ) (p : Memory.Prog σ) (s : Memory.State) : + (Memory.spec w).clientResult s p = (Memory.spec 0).clientResult s p := + congrArg Prod.fst (runClient_width w p s) + +/-- The final memory/frontier state is independent of the admissibility width. -/ +theorem clientState_width (w : ℕ) (p : Memory.Prog σ) (s : Memory.State) : + (Memory.spec w).clientState s p = (Memory.spec 0).clientState s p := + congrArg (fun t => t.2.1) (runClient_width w p s) + +/-- Admissible sequential histories split at the actual returned value and memory state. -/ +theorem validHistory_bind (w : ℕ) {α β : Type} (p : Memory.Prog α) + (q : α → Memory.Prog β) (s : Memory.State) : + (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s (p.bind q)) ↔ + (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s p) ∧ + (Memory.spec w).ValidHistory ((Memory.spec w).clientState s p) + ((Memory.spec w).clientHistory ((Memory.spec w).clientState s p) + (q ((Memory.spec w).clientResult s p))) := by + simp only [StateTransitionSpec.clientHistory, StateTransitionSpec.runClient_bind] + rw [StateTransitionSpec.validHistory_append, StateTransitionSpec.run_runClient] + +/-- Memory cost and certified local work decompose at each actual iteration. -/ +theorem iteration_charge_succ (body : δ × σ → Memory.Prog σ) + (work : (δ × σ) → Memory.State → ℕ) (d : δ) (n : ℕ) (a : σ) (s : Memory.State) : + memoryCost (iterate body d (n + 1) a) s + iterationWork body work d (n + 1) a s = + memoryCost (body (d, a)) s + work (d, a) s + + (memoryCost (iterate body d n ((Memory.spec 0).clientResult s (body (d, a)))) + ((Memory.spec 0).clientState s (body (d, a))) + + iterationWork body work d n ((Memory.spec 0).clientResult s (body (d, a))) + ((Memory.spec 0).clientState s (body (d, a)))) + 1 := by + rw [show iterate body d (n + 1) a = + (body (d, a)).bind (iterate body d n) from rfl, memoryCost_bind] + simp only [iterationWork] + omega + +/-- Sum a state-dependent charge along a fixed semantic transition. -/ +def iterationSum (next : σ → σ) (charge : σ → ℕ) : ℕ → σ → ℕ + | 0, _ => 0 + | n + 1, s => charge s + iterationSum next charge n (next s) + +/-- A bounded native loop may charge variable body work and one unit per iteration without +issuing a memory query. Its body, register bank and exchange constants are fixed before fuel. +The remaining-fuel relation also carries arbitrary semantic proof state. -/ +theorem loop_invariant_state_charged {cond : Cond r} {code : Code B r} {w C D : ℕ} + (I : ℕ → σ → Data r → Prop) (next : σ → σ) (charge : σ → ℕ) + (hexit : ∀ s x, I 0 s x → ¬cond.Holds x) + (hbody : ∀ n s x, I (n + 1) s x → + cond.Holds x ∧ ∃ y, ExecWithin w code x y (C * (charge s + 1) + D) ∧ + I n (next s) y) + {n : ℕ} {s : σ} {x : Data r} (hx : I n s x) : + ∃ y, ExecWithin w (.loop cond code) x y + ((C + D + 2) * (iterationSum next charge n s + n + 1)) ∧ + I 0 ((next^[n]) s) y := by + induction n generalizing s x with + | zero => + refine ⟨x, (ExecWithin.loop_exit (hexit s x hx)).mono ?_, hx⟩ + simp only [iterationSum, Nat.zero_add, Nat.mul_one] + omega + | succ n ih => + obtain ⟨hc, a, ha, hI⟩ := hbody n s x hx + obtain ⟨y, hy, hIy⟩ := ih hI + refine ⟨y, (ExecWithin.loop_iter hc ha hy).mono ?_, ?_⟩ + · simp only [iterationSum] + nlinarith + · simpa only [Function.iterate_succ_apply] using hIy + +/-- One semantic iteration updates both the mutable value and the complete memory state. -/ +def iterationStep (body : δ × σ → Memory.Prog σ) (d : δ) (a : σ × Memory.State) : + σ × Memory.State := + ((Memory.spec 0).clientResult a.2 (body (d, a.1)), + (Memory.spec 0).clientState a.2 (body (d, a.1))) + +/-- The interpreter follows precisely the bounded semantic transition, not an arbitrary state. -/ +theorem iterate_result_state (body : δ × σ → Memory.Prog σ) (d : δ) (n : ℕ) (a : σ) + (s : Memory.State) : + ((Memory.spec 0).clientResult s (iterate body d n a), + (Memory.spec 0).clientState s (iterate body d n a)) = + (iterationStep body d)^[n] (a, s) := by + induction n generalizing a s with + | zero => rfl + | succ n ih => + rw [show iterate body d (n + 1) a = + (body (d, a)).bind (iterate body d n) from rfl] + simpa only [StateTransitionSpec.clientResult, StateTransitionSpec.clientState, + StateTransitionSpec.runClient_bind, Function.iterate_succ_apply, iterationStep] using + ih ((Memory.spec 0).clientResult s (body (d, a))) + ((Memory.spec 0).clientState s (body (d, a))) + +/-- The native grading rule's semantic sum is exactly memory charge plus local iteration work. -/ +theorem iterationSum_eq (body : δ × σ → Memory.Prog σ) + (work : (δ × σ) → Memory.State → ℕ) (d : δ) (n : ℕ) (a : σ) (s : Memory.State) : + iterationSum (iterationStep body d) + (fun q => memoryCost (body (d, q.1)) q.2 + work (d, q.1) q.2) n (a, s) + n = + memoryCost (iterate body d n a) s + iterationWork body work d n a s := by + induction n generalizing a s with + | zero => rfl + | succ n ih => + rw [iterationSum, iteration_charge_succ] + have h := ih ((Memory.spec 0).clientResult s (body (d, a))) + ((Memory.spec 0).clientState s (body (d, a))) + dsimp only [iterationStep] at * + omega + +private def stateSlot {l n : ℕ} (i : Fin l) : Fin (l + (l + n + 3) + 1) := + ⟨i.val, by omega⟩ + +private def counterSlot (l n : ℕ) : Fin (l + (l + n + 3) + 1) := ⟨l, by omega⟩ +private def zeroSlot (l n : ℕ) : Fin (l + (l + n + 3) + 1) := ⟨l + 1, by omega⟩ +private def oneSlot (l n : ℕ) : Fin (l + (l + n + 3) + 1) := ⟨l + 2, by omega⟩ + +private def bodySlot {l n : ℕ} (i : Fin (l + n + 1)) : Fin (l + (l + n + 3) + 1) := + ⟨l + 3 + i.val, by omega⟩ + +private def loopBody {n : ℕ} (emit : ∀ {r : ℕ}, Fin r → (Fin (k + l) → Fin r) → + (Fin (l + n + 1) → Fin r) → Code .exact r) + (frontier : Fin r) (src : Fin (k + l + 1) → Fin r) + (tmp : Fin (l + (l + n + 3) + 1) → Fin r) : Code .exact r := + .seq (emit frontier (Fin.append (fun i : Fin k => src ⟨i.val, by omega⟩) + (fun i => tmp (stateSlot i))) (fun i => tmp (bodySlot i))) <| + .seq (copyBank (fun i : Fin l => tmp (bodySlot (Fin.castAdd (n + 1) i))) + (fun i => tmp (stateSlot i)) (tmp (zeroSlot l n))) <| + .seq (.op (.const (tmp (oneSlot l n)) 1)) + (.op (.exactBin .sub (tmp (counterSlot l n)) + (tmp (counterSlot l n)) (tmp (oneSlot l n)))) + +private def loopEmit {n : ℕ} (emit : ∀ {r : ℕ}, Fin r → (Fin (k + l) → Fin r) → + (Fin (l + n + 1) → Fin r) → Code .exact r) + (frontier : Fin r) (src : Fin (k + l + 1) → Fin r) + (tmp : Fin (l + (l + n + 3) + 1) → Fin r) : Code .exact r := + .seq (copyBank (fun i : Fin l => src ⟨k + i.val, by omega⟩) + (fun i => tmp (stateSlot i)) (tmp (zeroSlot l n))) <| + .seq (.op (.copy (tmp (counterSlot l n)) (src ⟨k + l, by omega⟩))) <| + .seq (.op (.const (tmp (zeroSlot l n)) 0)) + (.loop (.lt (tmp (zeroSlot l n)) (tmp (counterSlot l n))) + (loopBody emit frontier src tmp)) + +namespace Realizes + +/-- A runtime-bounded loop reuses one fixed body emitter, including iterations without queries. +The visible domain is precisely the body domains along the actual semantic states. -/ +theorem iterateRuntime (input : δ ↪ (Fin k → ℕ)) (output : σ ↪ (Fin l → ℕ)) + {body : δ × σ → Memory.Prog σ} + {safe : ℕ → (δ × σ) → Memory.State → Prop} + {work : (δ × σ) → Memory.State → ℕ} + (h : Realizes .exact body + ((input.prodMap output).trans (Fin.appendEquiv k l).toEmbedding) output safe work) : + Realizes .exact (fun d : (δ × σ) × ℕ => iterate body d.1.1 d.2 d.1.2) + ((((input.prodMap output).trans (Fin.appendEquiv k l).toEmbedding).prodMap + (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding).trans + (Fin.appendEquiv (k + l) 1).toEmbedding) output + (fun w d s => iterationSafe body safe w d.1.1 d.2 d.1.2 s) + (fun d s => iterationWork body work d.1.1 d.2 d.1.2 s) := by + obtain ⟨n, C, emit, he⟩ := h + refine ⟨l + n + 3, C + 2 * l + 8, loopEmit emit, ?_⟩ + intro r frontier src tmp hi hd hf hs + let st : Fin l → Fin r := fun i => tmp (stateSlot i) + let ctr := tmp (counterSlot l n) + let zero := tmp (zeroSlot l n) + let one := tmp (oneSlot l n) + let bt : Fin (l + n + 1) → Fin r := fun i => tmp (bodySlot i) + let env : Fin k → Fin r := fun i => src ⟨i.val, by omega⟩ + let bs := Fin.append env st + have hst : Function.Injective st := by + intro i j hij + apply Fin.ext + exact congrArg (fun j : Fin (l + (l + n + 3) + 1) => j.val) (hi hij) + have hbt : Function.Injective bt := by + intro i j hij + apply Fin.ext + have hh := congrArg Fin.val (hi hij) + dsimp only [bodySlot] at hh + omega + have hbdis : ∀ i j, bs i ≠ bt j := by + intro i j + refine Fin.addCases (fun i => ?_) (fun i => ?_) i + · simpa only [bs, Fin.append_left] using hd (⟨i.val, by omega⟩ : Fin (k + l + 1)) (bodySlot j) + · simp only [bs, Fin.append_right] + intro hij + have hh := congrArg Fin.val (hi hij) + dsimp only [stateSlot, bodySlot] at hh + omega + have hbf : ∀ j, bt j ≠ frontier := fun j => hf _ + have hbs : ∀ i, bs i ≠ frontier := by + intro i + refine Fin.addCases (fun i => ?_) (fun i => ?_) i + · simpa only [bs, Fin.append_left] using hs (⟨i.val, by omega⟩ : Fin (k + l + 1)) + · simpa only [bs, Fin.append_right] using hf (stateSlot i) + obtain ⟨hnw, hrun⟩ := he frontier bs bt hbt hbdis hbf hbs + have houtdis : ∀ i j : Fin l, bt (Fin.castAdd (n + 1) i) ≠ st j := by + intro i j hij + have hh := congrArg Fin.val (hi hij) + dsimp only [bodySlot, stateSlot] at hh + omega + have hct : ctr ≠ zero := by + intro hij + have := congrArg Fin.val (hi hij) + simp [counterSlot, zeroSlot] at this + have hco : ctr ≠ one := by + intro hij + have := congrArg Fin.val (hi hij) + simp [counterSlot, oneSlot] at this + have hzo : zero ≠ one := by + intro hij + have := congrArg Fin.val (hi hij) + simp [zeroSlot, oneSlot] at this + have hregbt (a : Fin (l + (l + n + 3) + 1)) (ha : a.val < l + 3) : + ∀ j, bt j ≠ tmp a := by + intro j hij + have hh := congrArg Fin.val (hi hij) + dsimp only [bodySlot] at hh + omega + have hregst (a : Fin (l + (l + n + 3) + 1)) (ha : l ≤ a.val) : + ∀ i, st i ≠ tmp a := by + intro i hij + have hh := congrArg Fin.val (hi hij) + dsimp only [stateSlot] at hh + omega + refine ⟨?_, ?_⟩ + · change ¬(loopEmit emit frontier src tmp).ReadsWidth + simpa [loopEmit, loopBody, Code.ReadsWidth, Op.ReadsWidth, Op.exactBin, + copyBank_not_readsWidth] using hnw + · intro w d x hx hin hsafe hv + let a₀ := d.1.2 + let d₀ := d.1.1 + let fuel := d.2 + let s₀ := memoryState frontier x + have henv (i : Fin k) : x.regs (env i) = input d₀ i := by + have hh : x.regs (src (Fin.castAdd 1 (Fin.castAdd l i))) = input d₀ i := by + simpa [d₀, Fin.appendEquiv] using congrFun hin (Fin.castAdd 1 (Fin.castAdd l i)) + have hidx : Fin.castAdd 1 (Fin.castAdd l i) = (⟨i.val, by omega⟩ : Fin (k + l + 1)) := by + apply Fin.ext + rfl + rw [hidx] at hh + exact hh + have hstate (i : Fin l) : x.regs (src ⟨k + i.val, by omega⟩) = output a₀ i := by + have hh : x.regs (src (Fin.castAdd 1 (Fin.natAdd k i))) = output a₀ i := by + simpa [a₀, Fin.appendEquiv] using congrFun hin (Fin.castAdd 1 (Fin.natAdd k i)) + have hidx : Fin.castAdd 1 (Fin.natAdd k i) = + (⟨k + i.val, by omega⟩ : Fin (k + l + 1)) := by + apply Fin.ext + rfl + rw [hidx] at hh + exact hh + have hfuel : x.regs (src ⟨k + l, by omega⟩) = fuel := by + have hh : x.regs (src (Fin.natAdd (k + l) 0)) = fuel := by + simpa [fuel, Fin.appendEquiv] using congrFun hin (Fin.natAdd (k + l) 0) + have hidx : Fin.natAdd (k + l) 0 = (⟨k + l, by omega⟩ : Fin (k + l + 1)) := by + apply Fin.ext + simp + rw [hidx] at hh + exact hh + obtain ⟨a, ha, ham, has, haf⟩ := copyBank_exec + (fun i : Fin l => src ⟨k + i.val, by omega⟩) st zero hst + (fun _ _ => hd _ _) w x + let b := a.set ctr fuel + let z := b.set zero 0 + have hafuel : a.regs (src ⟨k + l, by omega⟩) = fuel := by + rw [haf _ (fun _ => (hd _ _).symm)] + exact hfuel + have hc : ExecWithin (B := .exact) w (.op (.copy ctr (src ⟨k + l, by omega⟩))) a b 1 := by + simpa only [hafuel] using + (ExecWithin.copy (B := .exact) (w := w) (d := ctr) + (s := src ⟨k + l, by omega⟩) (x := a)) + have hz : ExecWithin (B := .exact) w (.op (.const zero 0)) b z 1 := + ExecWithin.const (fits_zero w) + let I : ℕ → (σ × Memory.State) → Data r → Prop := fun j q y => + y.Bounded w ∧ memoryState frontier y = q.2 ∧ + (∀ i, y.regs (env i) = input d₀ i) ∧ (∀ i, y.regs (st i) = output q.1 i) ∧ + y.regs ctr = j ∧ y.regs zero = 0 ∧ iterationSafe body safe w d₀ j q.1 q.2 ∧ + (Memory.spec w).ValidHistory q.2 + ((Memory.spec w).clientHistory q.2 (iterate body d₀ j q.1)) ∧ + ∀ i, i ≠ frontier → (∀ j, tmp j ≠ i) → y.regs i = x.regs i + have hIz : I fuel (a₀, s₀) z := by + refine ⟨(ha.seq (hc.seq hz)).bounded hx, ?_, ?_, ?_, ?_, ?_, hsafe, hv, ?_⟩ + · apply Memory.State.ext + · exact ham + · change (b.set zero 0).regs frontier = x.regs frontier + rw [show (b.set zero 0).regs frontier = b.regs frontier from + Function.update_of_ne (hf (zeroSlot l n)).symm _ _, + show b.regs frontier = a.regs frontier from + Function.update_of_ne (hf (counterSlot l n)).symm _ _, + haf frontier (fun i => hf _)] + · intro i + simp only [z, b, Data.set] + rw [Function.update_of_ne (hd _ _), Function.update_of_ne (hd _ _), + haf _ (fun _ => (hd _ _).symm)] + exact henv i + · intro i + simp only [z, b, Data.set] + rw [Function.update_of_ne (hregst _ (by simp [zeroSlot]) i), + Function.update_of_ne (hregst _ (by simp [counterSlot]) i), has] + exact hstate i + · simp [z, b, Data.set, hct] + · simp [z, Data.set] + · intro i hif htmp + simp only [z, b, Data.set] + rw [Function.update_of_ne (htmp _).symm, + Function.update_of_ne (htmp _).symm, haf _ (fun _ => htmp _)] + have hbody : ∀ j q y, I (j + 1) q y → + (Cond.lt zero ctr).Holds y ∧ ∃ y', + ExecWithin w (loopBody emit frontier src tmp) y y' + (C * (memoryCost (body (d₀, q.1)) q.2 + work (d₀, q.1) q.2 + 1) + (l + 3)) ∧ + I j (iterationStep body d₀ q) y' := by + intro j q y hIy + obtain ⟨hyb, hym, hye, hys, hyc, hyz, hysafe, hyv, hyframe⟩ := hIy + refine ⟨by simp [Cond.Holds, hyz, hyc], ?_⟩ + have hvparts := (validHistory_bind w (body (d₀, q.1)) + (iterate body d₀ j) q.2).mp hyv + have hinbody : y.regs ∘ bs = + ((input.prodMap output).trans (Fin.appendEquiv k l).toEmbedding) (d₀, q.1) := by + funext i + refine Fin.addCases (fun i => ?_) (fun i => ?_) i + · simpa [bs, Fin.appendEquiv] using hye i + · simpa [bs, Fin.appendEquiv] using hys i + obtain ⟨u, hu, hum, huo, huf⟩ := hrun w (d₀, q.1) y hyb hinbody + (hym ▸ hysafe.1) (hym ▸ hvparts.1) + rw [hym] at hu hum huo + rw [clientState_width] at hum + simp only [clientResult_width] at huo + obtain ⟨v, hv', hvm, hvs, hvf⟩ := copyBank_exec + (fun i : Fin l => bt (Fin.castAdd (n + 1) i)) st zero hst houtdis w u + have hvctr : v.regs ctr = j + 1 := by + rw [hvf _ (hregst _ (by simp [counterSlot])), + huf _ (hf _) (hregbt _ (by simp [counterSlot]))] + exact hyc + have hvzero : v.regs zero = 0 := by + rw [hvf _ (hregst _ (by simp [zeroSlot])), + huf _ (hf _) (hregbt _ (by simp [zeroSlot]))] + exact hyz + have h1 : Fits w 1 := (hyb.1 ctr).of_le (by omega) + let t := v.set one 1 + let y' := t.set ctr j + have ht : ExecWithin (B := .exact) w (.op (.const one 1)) v t 1 := + ExecWithin.const h1 + have htc : t.regs ctr = j + 1 := by + rw [show t.regs ctr = v.regs ctr from Function.update_of_ne hco _ _] + exact hvctr + have htone : t.regs one = 1 := by simp [t, Data.set] + have hjfit : Fits w j := (hyb.1 ctr).of_le (by omega) + have hdec : ExecWithin w (.op (.exactBin .sub ctr ctr one)) t y' 1 := by + apply ExecWithin.op + rw [Op.eval_exactBin, htc, htone] + simp only [BinOp.evalExact, Nat.le_add_left, ↓reduceIte, Nat.add_sub_cancel, + Option.bind_some] + exact Data.put_of_fits hjfit + refine ⟨y', ?_, ?_⟩ + · exact (hu.seq (hv'.seq (ht.seq hdec))).mono (by omega) + · refine ⟨(hu.seq (hv'.seq (ht.seq hdec))).bounded hyb, ?_, ?_, ?_, ?_, ?_, + hysafe.2, ?_, ?_⟩ + · apply Memory.State.ext + · exact hvm.trans (congrArg Memory.State.mem hum) + · change (t.set ctr j).regs frontier = _ + rw [show (t.set ctr j).regs frontier = t.regs frontier from + Function.update_of_ne (hf (counterSlot l n)).symm _ _, + show t.regs frontier = v.regs frontier from + Function.update_of_ne (hf (oneSlot l n)).symm _ _, + hvf frontier (fun i => hf _)] + exact congrArg Memory.State.frontier hum + · intro i + simp only [y', t, Data.set] + rw [Function.update_of_ne (hd _ _), Function.update_of_ne (hd _ _), + hvf _ (fun _ => (hd _ _).symm), huf _ (hs _) (fun _ => (hd _ _).symm)] + exact hye i + · intro i + simp only [y', t, Data.set] + rw [Function.update_of_ne (hregst _ (by simp [counterSlot]) i), + Function.update_of_ne (hregst _ (by simp [oneSlot]) i), hvs, huo] + rfl + · simp [y', Data.set] + · simp [y', t, Data.set, hct.symm, hzo, hvzero] + · have hrest := hvparts.2 + rw [clientState_width w, clientResult_width w] at hrest + exact hrest + · intro i hif htmp + simp only [y', t, Data.set] + rw [Function.update_of_ne (htmp _).symm, Function.update_of_ne (htmp _).symm, + hvf _ (fun _ => htmp _), huf _ hif (fun _ => htmp _)] + exact hyframe i hif htmp + obtain ⟨y, hy, hIy⟩ := loop_invariant_state_charged I (iterationStep body d₀) + (fun q => memoryCost (body (d₀, q.1)) q.2 + work (d₀, q.1) q.2) + (fun q y hI => by simp [Cond.Holds, hI.2.2.2.2.1, hI.2.2.2.2.2.1]) hbody hIz + rw [iterationSum_eq] at hy + have hfinal := iterate_result_state body d₀ fuel a₀ s₀ + rw [← hfinal] at hIy + obtain ⟨_, hym, _, hys, _, _, _, _, hyf⟩ := hIy + refine ⟨y, (ha.seq (hc.seq (hz.seq hy))).mono ?_, ?_, ?_, hyf⟩ + · nlinarith + · change memoryState frontier y = (Memory.spec w).clientState s₀ (iterate body d₀ fuel a₀) + rw [clientState_width w] + exact hym + · intro i + change y.regs (st i) = output ((Memory.spec w).clientResult s₀ (iterate body d₀ fuel a₀)) i + rw [clientResult_width w] + exact hys i + +end Realizes +end Cslib.WordRAM.Realization diff --git a/Complexity/Computability/WordRAM/Realization/Program.lean b/Complexity/Computability/WordRAM/Realization/Program.lean new file mode 100644 index 0000000000..5bd3f46df6 --- /dev/null +++ b/Complexity/Computability/WordRAM/Realization/Program.lean @@ -0,0 +1,345 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Realization.Basic + +/-! +# Flat programs from graded memory-client realizations + +`Realizes.exists_program` projects the existing realization certificate through +`Refines.executes`. One emitter, scratch size and exchange constant precede every runtime +argument, word width and initial memory. A statically selected coordinate of the faithful result +encoding is observed by the existing `Code.program`; its final halt costs one instruction. +The emitted body is shared by all coordinate observations. + +`Realizes.exists_program` takes already installed arguments and frontier. +`Realizes.exists_init_program` instead executes `inputPrefix` from `Data.init`, charging +`2 * k + 5` instructions to install the first `k` input words and frontier. Its explicit layout, +input-word and frontier-fit premises exclude free preparation and address overflow. The initial +memory is retained in full. The whole-memory postcondition and register frame remain available +through `Realizes.exists_refines`; no new execution judgment is introduced here. + +## Main definitions + +* `inputLoads`: a fixed sequence loading consecutive cells into source registers. +* `inputPrefix`: the native register and frontier installation prefix. +* `inputState`: the functional memory state of the native list-input convention. + +## Main results + +* `Realizes.exists_program`: a uniform flat program for every static output coordinate, with + bound `C * (memoryCost + work + 1) + 1`, including the trailing halt. +* `inputPrefix_exec`: a charged, memory-preserving native list-input prefix. +* `Realizes.exists_init_program`: the same uniform flat-program bound from `Data.init`, + absorbing the actual prefix into its exchange constant. +* `Realizes.exists_unit_program`, `Realizes.exists_init_unit_program`: zero-output clients + execute a zero write and halt, charging both instructions at every word width. + +## References + +This semantic projection is original infrastructure over the existing flat compiler. +-/ + +@[expose] public section + +namespace Cslib.WordRAM.Realization + +open Cslib.DataStructure + +/-- Install consecutive input cells in the given source registers. The terminal self-copy +provides the empty case without adding a skip instruction to the native language. -/ +def inputLoads {r : ℕ} (ptr : Fin r) : + {k : ℕ} → (Fin k → Fin r) → ℕ → Code .exact r + | 0, _, _ => .op (.copy ptr ptr) + | _ + 1, src, offset => + .seq (.op (.const ptr (offset + 1))) + (.seq (.op (.load (src 0) ptr)) (inputLoads ptr (fun i => src i.succ) (offset + 1))) + +private theorem inputLoads_exec {r k w : ℕ} (ptr : Fin r) (src : Fin k → Fin r) + (offset : ℕ) (x : Data r) (hi : Function.Injective src) + (hd : ∀ i, src i ≠ ptr) (hfit : ∀ i : Fin k, Fits w (offset + i.val + 1)) : + ∃ y, ExecWithin w (inputLoads ptr src offset) x y (2 * k + 1) ∧ + y.mem = x.mem ∧ (∀ i, y.regs (src i) = x.mem (offset + i.val + 1)) ∧ + ∀ j, j ≠ ptr → (∀ i, src i ≠ j) → y.regs j = x.regs j := by + induction k generalizing offset x with + | zero => + refine ⟨x, ?_, rfl, (fun i => Fin.elim0 i), ?_⟩ + · simpa [inputLoads, Data.set] using + (ExecWithin.copy (B := .exact) (w := w) (x := x) (d := ptr) (s := ptr)) + · intro j _ _ + rfl + | succ k ih => + let x₁ := x.set ptr (offset + 1) + let x₂ := x₁.set (src 0) (x.mem (offset + 1)) + have e₁ : ExecWithin (B := .exact) w (.op (.const ptr (offset + 1))) x x₁ 1 := + ExecWithin.const (by simpa using hfit 0) + have e₂ : ExecWithin (B := .exact) w (.op (.load (src 0) ptr)) x₁ x₂ 1 := by + simpa [x₂, x₁] using + (ExecWithin.load (B := .exact) (w := w) (x := x₁) (d := src 0) (a := ptr)) + obtain ⟨y, ey, hm, ho, hf⟩ := ih (fun i => src i.succ) (offset + 1) x₂ + (hi.comp (Fin.succ_injective _)) (fun i => hd i.succ) + (fun i => by simpa [Nat.add_assoc, Nat.add_comm, Nat.add_left_comm] using hfit i.succ) + refine ⟨y, ?_, hm, ?_, ?_⟩ + · exact (e₁.seq (e₂.seq ey)).mono (by omega) + · intro i + refine Fin.cases ?_ (fun j => ?_) i + · rw [hf (src 0) (hd 0) (fun j h => by + have := hi h + exact Fin.succ_ne_zero j this)] + simp [x₂] + · simpa [x₂, x₁, Nat.add_assoc, Nat.add_comm, Nat.add_left_comm] using ho j + · intro j hj hs + rw [hf j hj (fun i => hs i.succ)] + simp [x₂, x₁, (hs 0).symm, hj] + +/-- A native entry prefix installs the frontier and the first `k` input words. All data starts +from the existing `Data.init` convention; register installation is charged. -/ +def inputPrefix {r k : ℕ} (frontier ptr : Fin r) (src : Fin k → Fin r) : Code .exact r := + .seq (.op (.const ptr 0)) + (.seq (.op (.load frontier ptr)) + (.seq (.op (.const ptr 1)) + (.seq (.op (.exactBin .add frontier frontier ptr)) (inputLoads ptr src 0)))) + +/-- The whole input memory, with the first free address immediately after its last word. -/ +def inputState (words : List ℕ) : Memory.State := + ⟨(Data.init words : Data 0).mem, words.length + 1⟩ + +/-- Argument installation takes at most `2 * k + 5` native instructions. Its memory is unchanged; +only the frontier, pointer and source registers can change. The frontier-fit premise also pays +for the constant one and every input address. -/ +theorem inputPrefix_exec {r k w : ℕ} (frontier ptr : Fin r) (src : Fin k → Fin r) + (words : List ℕ) (hi : Function.Injective src) (hp : ptr ≠ frontier) + (hd : ∀ i, src i ≠ ptr) (hf : ∀ i, src i ≠ frontier) + (hk : k ≤ words.length) (hw : Fits w (words.length + 1)) (hin : InputFits w words) : + ∃ y, ExecWithin w (inputPrefix frontier ptr src) (Data.init words) y (2 * k + 5) ∧ + y.Bounded w ∧ memoryState frontier y = inputState words ∧ + (∀ i, y.regs (src i) = words.getD i.val 0) ∧ + ∀ j, j ≠ frontier → j ≠ ptr → (∀ i, src i ≠ j) → y.regs j = 0 := by + let x : Data r := Data.init words + let x₁ := x.set ptr 0 + let x₂ := x₁.set frontier words.length + let x₃ := x₂.set ptr 1 + let x₄ := x₃.set frontier (words.length + 1) + have h1 : Fits w 1 := hw.of_le (by omega) + have e₁ : ExecWithin (B := .exact) w (.op (.const ptr 0)) x x₁ 1 := ExecWithin.const (fits_zero w) + have e₂ : ExecWithin (B := .exact) w (.op (.load frontier ptr)) x₁ x₂ 1 := by + simpa [x₂, x₁, x] using + (ExecWithin.load (B := .exact) (w := w) (x := x₁) (d := frontier) (a := ptr)) + have e₃ : ExecWithin (B := .exact) w (.op (.const ptr 1)) x₂ x₃ 1 := ExecWithin.const h1 + have e₄ : ExecWithin w (.op (.exactBin .add frontier frontier ptr)) x₃ x₄ 1 := by + apply ExecWithin.op + simp [Op.eval_exactBin, BinOp.evalExact, x₄, x₃, x₂, hp.symm, Data.put_of_fits hw] + obtain ⟨y, ey, hm, ho, hframe⟩ := inputLoads_exec ptr src 0 x₄ hi hd + (fun i => hw.of_le (by omega)) + have he : ExecWithin w (inputPrefix frontier ptr src) x y (2 * k + 5) := by + exact (e₁.seq (e₂.seq (e₃.seq (e₄.seq ey)))).mono (by omega) + refine ⟨y, he, he.bounded (Data.Bounded.init hin), ?_, ?_, ?_⟩ + · apply Memory.State.ext + · exact hm + · change y.regs frontier = words.length + 1 + rw [hframe frontier hp.symm hf] + simp [x₄] + · intro i + simpa [x₄, x₃, x₂, x₁, x] using ho i + · intro j hj hptr hs + rw [hframe j hptr hs] + simp [x₄, x₃, x₂, x₁, x, hj, hptr] + +end Cslib.WordRAM.Realization + +namespace Cslib.WordRAM.Realization.Realizes + +open Cslib.DataStructure + +variable {B : Basis} {k l : ℕ} {δ α : Type} + {p : δ → Memory.Prog α} {input : δ ↪ (Fin k → ℕ)} + {output : α ↪ (Fin l → ℕ)} {safe : ℕ → δ → Memory.State → Prop} + {work : δ → Memory.State → ℕ} + +/-- Each static coordinate of a faithfully encoded result has one flat program, independent of +the runtime input, width and memory. The selected halt observes an output register of the same +emitted body; the clock includes that halt. Arguments and frontier are explicit entry premises. +For an empty result register vector this makes no scalar observation. -/ +theorem exists_program (h : Realizes B p input output safe work) : + ∃ n C : ℕ, + ∃ emit : ∀ {r : ℕ}, (frontier : Fin r) → (Fin k → Fin r) → + (Fin (l + n + 1) → Fin r) → Code B r, + ∀ {r : ℕ} (frontier : Fin r) (src : Fin k → Fin r) + (tmp : Fin (l + n + 1) → Fin r), + Function.Injective tmp → (∀ i j, src i ≠ tmp j) → + (∀ j, tmp j ≠ frontier) → (∀ i, src i ≠ frontier) → + ∀ (i : Fin l) (w : ℕ) (d : δ) (x : Data r), + x.Bounded w → x.regs ∘ src = input d → safe w d (memoryState frontier x) → + (Memory.spec w).ValidHistory (memoryState frontier x) + ((Memory.spec w).clientHistory (memoryState frontier x) (p d)) → + ∃ t, Executes w ((emit frontier src tmp).program (tmp (Fin.castAdd (n + 1) i))) + ⟨0, x⟩ (output ((Memory.spec w).clientResult (memoryState frontier x) (p d)) i) t ∧ + t ≤ C * (memoryCost (p d) (memoryState frontier x) + + work d (memoryState frontier x) + 1) + 1 := by + obtain ⟨n, C, emit, he⟩ := h.exists_refines + refine ⟨n, C, emit, ?_⟩ + intro r frontier src tmp hi hd hf hs i w d x hx hin hsafe hv + exact (he frontier src tmp hi hd hf hs w d).executes + (tmp (Fin.castAdd (n + 1) i)) (fun a => output a i) (fun _ _ _ hout => hout.2 i) + ⟨rfl, hx, hin, hsafe, hv⟩ + +/-- A flat exact-basis program starts from the native list-input convention and charges both +argument installation and its trailing halt. The first `k` cells encode the argument; the whole +input memory and its frontier are passed to the client. The fixed installation cost is absorbed +into one exchange constant before every width and runtime argument. -/ +theorem exists_init_program (h : Realizes .exact p input output safe work) + (raw : δ → List ℕ) (hlayout : ∀ d (i : Fin k), (raw d).getD i.val 0 = input d i) : + ∃ n C : ℕ, + ∃ emit : ∀ {r : ℕ}, (frontier : Fin r) → (Fin k → Fin r) → + (Fin (l + n + 1) → Fin r) → Code .exact r, + ∀ {r : ℕ} (frontier : Fin r) (src : Fin k → Fin r) + (tmp : Fin (l + n + 1) → Fin r), + Function.Injective src → Function.Injective tmp → (∀ i j, src i ≠ tmp j) → + (∀ j, tmp j ≠ frontier) → (∀ i, src i ≠ frontier) → + ∀ (i : Fin l) (w : ℕ) (d : δ), + k ≤ (raw d).length → Fits w ((raw d).length + 1) → InputFits w (raw d) → + safe w d (inputState (raw d)) → + (Memory.spec w).ValidHistory (inputState (raw d)) + ((Memory.spec w).clientHistory (inputState (raw d)) (p d)) → + ∃ t, Executes w + ((Code.seq (inputPrefix frontier (tmp (Fin.last (l + n))) src) + (emit frontier src tmp)).program (tmp (Fin.castAdd (n + 1) i))) + (State.init (raw d)) + (output ((Memory.spec w).clientResult (inputState (raw d)) (p d)) i) t ∧ + t ≤ C * (memoryCost (p d) (inputState (raw d)) + + work d (inputState (raw d)) + 1) + 1 := by + obtain ⟨n, C, emit, he⟩ := h + refine ⟨n, C + (2 * k + 5), emit, ?_⟩ + intro r frontier src tmp hsrc htmp hd hf hs i w d hk hw hin hsafe hv + obtain ⟨x, ex, hx, hm, hwords, _⟩ := + inputPrefix_exec frontier (tmp (Fin.last (l + n))) src (raw d) hsrc + (hf _) (fun j => hd j _) hs hk hw hin + have harg : x.regs ∘ src = input d := by + funext j + exact (hwords j).trans (hlayout d j) + obtain ⟨y, ey, _, hout, _⟩ := (he frontier src tmp htmp hd hf hs).2 w d x hx harg + (hm.symm ▸ hsafe) (hm.symm ▸ hv) + rw [hm] at ey hout + obtain ⟨t, ht, et⟩ := ex.seq ey + refine ⟨t + 1, ?_, ?_⟩ + · simpa only [State.init, hout i] using et.executes (tmp (Fin.castAdd (n + 1) i)) + · have hpos : 1 ≤ memoryCost (p d) (inputState (raw d)) + + work d (inputState (raw d)) + 1 := by omega + have hover := Nat.mul_le_mul_left (2 * k + 5) hpos + simp only [Nat.mul_one] at hover + rw [Nat.add_mul] + omega + +/-- A Unit-returning client needs no output coordinate. Execute an actual zero constant after +its body to obtain a scalar observation, preserving the entire represented client state. +The extra instruction fits every width, including zero. -/ +theorem exists_unit_refines {p : δ → Memory.Prog Unit} {output : Unit ↪ (Fin 0 → ℕ)} + (h : Realizes B p input output safe work) : + ∃ n C : ℕ, + ∃ emit : ∀ {r : ℕ}, (frontier : Fin r) → (Fin k → Fin r) → + (Fin (0 + n + 1) → Fin r) → Code B r, + ∀ {r : ℕ} (frontier : Fin r) (src : Fin k → Fin r) + (tmp : Fin (0 + n + 1) → Fin r), + Function.Injective tmp → (∀ i j, src i ≠ tmp j) → + (∀ j, tmp j ≠ frontier) → (∀ i, src i ≠ frontier) → + ∀ (w : ℕ) (d : δ), + Refines (D := Memory.spec w) w + (fun s x => s = memoryState frontier x ∧ x.Bounded w ∧ x.regs ∘ src = input d ∧ + safe w d s ∧ (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s (p d))) + (p d) + (fun _ s y => memoryState frontier y = s ∧ y.regs (tmp 0) = 0) + (fun s _ => C * (memoryCost (p d) s + work d s + 1) + 1) + (.seq (emit frontier src tmp) (.op (.const (tmp 0) 0))) := by + obtain ⟨n, C, emit, he⟩ := h.exists_refines + refine ⟨n, C, emit, ?_⟩ + intro r frontier src tmp hi hd hf hs w d + apply Refines.ofFn + intro s x hx + obtain ⟨hv, y, ey, hm, _⟩ := (he frontier src tmp hi hd hf hs w d).toOfFn hx + refine ⟨hv, y.set (tmp 0) 0, ey.seq (ExecWithin.const (fits_zero w)), ?_, ?_⟩ + · apply Eq.trans ?_ hm + apply Memory.State.ext + · rfl + · exact Function.update_of_ne (hf 0).symm _ _ + · simp + +/-- A zero-output Unit client has a uniform flat scalar observation after a charged zero write +and halt. Both instructions are included, without a positive-width premise. -/ +theorem exists_unit_program {p : δ → Memory.Prog Unit} {output : Unit ↪ (Fin 0 → ℕ)} + (h : Realizes B p input output safe work) : + ∃ n C : ℕ, + ∃ emit : ∀ {r : ℕ}, (frontier : Fin r) → (Fin k → Fin r) → + (Fin (0 + n + 1) → Fin r) → Code B r, + ∀ {r : ℕ} (frontier : Fin r) (src : Fin k → Fin r) + (tmp : Fin (0 + n + 1) → Fin r), + Function.Injective tmp → (∀ i j, src i ≠ tmp j) → + (∀ j, tmp j ≠ frontier) → (∀ i, src i ≠ frontier) → + ∀ (w : ℕ) (d : δ) (x : Data r), + x.Bounded w → x.regs ∘ src = input d → safe w d (memoryState frontier x) → + (Memory.spec w).ValidHistory (memoryState frontier x) + ((Memory.spec w).clientHistory (memoryState frontier x) (p d)) → + ∃ t, Executes w + ((Code.seq (emit frontier src tmp) (.op (.const (tmp 0) 0))).program (tmp 0)) + ⟨0, x⟩ 0 t ∧ + t ≤ C * (memoryCost (p d) (memoryState frontier x) + + work d (memoryState frontier x) + 1) + 2 := by + obtain ⟨n, C, emit, he⟩ := h.exists_unit_refines + refine ⟨n, C, emit, ?_⟩ + intro r frontier src tmp hi hd hf hs w d x hx hin hsafe hv + simpa only [Nat.add_assoc] using + (he frontier src tmp hi hd hf hs w d).executes (tmp 0) (fun _ => 0) + (fun _ _ _ hout => hout.2) ⟨rfl, hx, hin, hsafe, hv⟩ + +/-- Unit-returning clients can start from `Data.init` too. Argument installation is charged and +absorbed into the uniform exchange constant; the real zero write and trailing halt cost two +further instructions. -/ +theorem exists_init_unit_program {p : δ → Memory.Prog Unit} {output : Unit ↪ (Fin 0 → ℕ)} + (h : Realizes .exact p input output safe work) + (raw : δ → List ℕ) (hlayout : ∀ d (i : Fin k), (raw d).getD i.val 0 = input d i) : + ∃ n C : ℕ, + ∃ emit : ∀ {r : ℕ}, (frontier : Fin r) → (Fin k → Fin r) → + (Fin (0 + n + 1) → Fin r) → Code .exact r, + ∀ {r : ℕ} (frontier : Fin r) (src : Fin k → Fin r) + (tmp : Fin (0 + n + 1) → Fin r), + Function.Injective src → Function.Injective tmp → (∀ i j, src i ≠ tmp j) → + (∀ j, tmp j ≠ frontier) → (∀ i, src i ≠ frontier) → + ∀ (w : ℕ) (d : δ), + k ≤ (raw d).length → Fits w ((raw d).length + 1) → InputFits w (raw d) → + safe w d (inputState (raw d)) → + (Memory.spec w).ValidHistory (inputState (raw d)) + ((Memory.spec w).clientHistory (inputState (raw d)) (p d)) → + ∃ t, Executes w + ((Code.seq (inputPrefix frontier (tmp (Fin.last (0 + n))) src) + (.seq (emit frontier src tmp) (.op (.const (tmp 0) 0)))).program (tmp 0)) + (State.init (raw d)) 0 t ∧ + t ≤ C * (memoryCost (p d) (inputState (raw d)) + + work d (inputState (raw d)) + 1) + 2 := by + obtain ⟨n, C, emit, he⟩ := h.exists_unit_refines + refine ⟨n, C + (2 * k + 5), emit, ?_⟩ + intro r frontier src tmp hsrc htmp hd hf hs w d hk hw hin hsafe hv + obtain ⟨x, ex, hx, hm, hwords, _⟩ := + inputPrefix_exec frontier (tmp (Fin.last (0 + n))) src (raw d) hsrc + (hf _) (fun j => hd j _) hs hk hw hin + have harg : x.regs ∘ src = input d := by + funext j + exact (hwords j).trans (hlayout d j) + obtain ⟨_, y, ey, _, hout⟩ := (he frontier src tmp htmp hd hf hs w d).toOfFn + ⟨rfl, hx, harg, hm.symm ▸ hsafe, hm.symm ▸ hv⟩ + rw [hm] at ey + obtain ⟨t, ht, et⟩ := ex.seq ey + refine ⟨t + 1, ?_, ?_⟩ + · simpa only [State.init, hout] using et.executes (tmp 0) + · have hpos : 1 ≤ memoryCost (p d) (inputState (raw d)) + + work d (inputState (raw d)) + 1 := by omega + have hover := Nat.mul_le_mul_left (2 * k + 5) hpos + simp only [Nat.mul_one] at hover + rw [Nat.add_mul] + omega + +end Cslib.WordRAM.Realization.Realizes diff --git a/ComplexityTests/WordRAMRealizationLoop.lean b/ComplexityTests/WordRAMRealizationLoop.lean new file mode 100644 index 0000000000..cc9fdb205f --- /dev/null +++ b/ComplexityTests/WordRAMRealizationLoop.lean @@ -0,0 +1,193 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: local-only (regression tests for realization loop infrastructure) + +public import Complexity.Computability.WordRAM.Realization.Loop +import Complexity.Computability.WordRAM.Realization.Glue +import Complexity.Computability.WordRAM.Realization.Primitive + +/-! +# Runtime-loop regression clients + +A query-free identity loop has zero memory charge but a local-work charge growing with runtime +fuel. Its uniform native witness also executes at zero word width when the state and fuel are +zero, so materializing the decrement constant in the positive body cannot break the empty case. +-/ + +public section + +namespace Cslib.WordRAM.Realization.Tests + +open Cslib.DataStructure + +private def unitInput : Unit ↪ (Fin 0 → ℕ) := + (Equiv.ofUnique Unit (Fin 0 → ℕ)).toEmbedding + +private def wordOutput : ℕ ↪ (Fin 1 → ℕ) := + (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + +private def bodyInput : Unit × ℕ ↪ (Fin (0 + 1) → ℕ) := + (unitInput.prodMap wordOutput).trans (Fin.appendEquiv 0 1).toEmbedding + +/-- A certified word copy performs local work without any memory query. -/ +@[expose] def identityBody (d : Unit × ℕ) : Memory.Prog ℕ := pure d.2 + +private theorem identityBody_realizes : + Realizes .exact identityBody bodyInput wordOutput + (fun w d _ => Fits w d.2) (fun _ _ => 1) := by + unfold identityBody + have h : GlueComputes .exact (fun _ : Fin 1 → ℕ => True) + (fun v => v 0) (fun v => v 0) 1 := GlueComputes.proj 0 (fun _ _ => le_rfl) + simpa [identityBody, bodyInput, unitInput, wordOutput, Fin.appendEquiv, + Fin.append, Fin.addCases] using Realizes.glue bodyInput h + +/-- A single body certificate is reused before all runtime fuel and data choices. -/ +private theorem identityLoop_realizes : + Realizes .exact (fun d : (Unit × ℕ) × ℕ => iterate identityBody d.1.1 d.2 d.1.2) + ((bodyInput.prodMap wordOutput).trans (Fin.appendEquiv (0 + 1) 1).toEmbedding) + wordOutput (fun w d s => iterationSafe identityBody (fun w d _ => Fits w d.2) + w d.1.1 d.2 d.1.2 s) + (fun d s => iterationWork identityBody (fun _ _ => 1) d.1.1 d.2 d.1.2 s) := + Realizes.iterateRuntime unitInput wordOutput identityBody_realizes + +private theorem identity_iterate (n a : ℕ) : iterate identityBody () n a = pure a := by + induction n with + | zero => rfl + | succ n ih => simpa only [iterate, identityBody, pure_bind] using ih + +example (n a : ℕ) (s : Memory.State) : memoryCost (iterate identityBody () n a) s = 0 := by + rw [identity_iterate] + rfl + +private theorem identity_work (n a : ℕ) (s : Memory.State) : + iterationWork identityBody (fun _ _ => 1) () n a s = 2 * n := by + induction n generalizing a s with + | zero => rfl + | succ n ih => + simp only [iterationWork, ih] + omega + +/-- Query-free iteration still has increasing certified local work. -/ +theorem queryFree_work_grows (n m a : ℕ) (s : Memory.State) (h : n < m) : + iterationWork identityBody (fun _ _ => 1) () n a s < + iterationWork identityBody (fun _ _ => 1) () m a s := by + rw [identity_work, identity_work] + omega + +/-- The uniform loop witness has a genuine zero-width, zero-fuel flat execution. -/ +example : ∃ r, ∃ p : Program .exact r, ∃ steps, + Executes 0 p ⟨0, ⟨fun _ => 0, fun _ => 0⟩⟩ 0 steps := by + obtain ⟨n, C, emit, he⟩ := identityLoop_realizes + let r := 2 + (1 + n + 1) + 1 + let frontier : Fin r := ⟨r - 1, by omega⟩ + let src : Fin 2 → Fin r := fun i => ⟨i.val, by omega⟩ + let tmp : Fin (1 + n + 1) → Fin r := fun i => ⟨2 + i.val, by omega⟩ + have hi : Function.Injective tmp := by + intro i j h + apply Fin.ext + have hh := congrArg (fun a : Fin r => a.val) h + dsimp only [tmp] at hh + omega + have hd (i : Fin 2) (j : Fin (1 + n + 1)) : src i ≠ tmp j := by + intro h + have hh := congrArg (fun a : Fin r => a.val) h + dsimp only [src, tmp] at hh + omega + have hf (j : Fin (1 + n + 1)) : tmp j ≠ frontier := by + intro h + have hh := congrArg (fun a : Fin r => a.val) h + dsimp only [tmp, frontier, r] at hh + omega + have hs (i : Fin 2) : src i ≠ frontier := by + intro h + have hh := congrArg (fun a : Fin r => a.val) h + dsimp only [src, frontier, r] at hh + omega + let x : Data r := ⟨fun _ => 0, fun _ => 0⟩ + have hx : x.Bounded 0 := ⟨fun _ => fits_zero 0, fun _ => fits_zero 0⟩ + have hin : x.regs ∘ src = + ((bodyInput.prodMap wordOutput).trans (Fin.appendEquiv (0 + 1) 1).toEmbedding) + (((), 0), 0) := by + funext i + fin_cases i <;> rfl + obtain ⟨y, ⟨steps, _, hy⟩, _, hout, _⟩ := + (he frontier src tmp hi hd hf hs).2 0 (((), 0), 0) x hx hin trivial trivial + have ho : y.regs (tmp 0) = 0 := hout 0 + refine ⟨r, (emit frontier src tmp).program (tmp 0), steps + 1, ?_⟩ + simpa only [ho] using hy.executes (tmp 0) + +private def decrementBody (d : Unit × ℕ) : Memory.Prog ℕ := pure (d.2 - 1) + +private theorem decrementBody_realizes : + Realizes .exact decrementBody bodyInput wordOutput + (fun w d _ => 1 ≤ d.2 ∧ Fits w d.2) (fun _ _ => 3) := by + unfold decrementBody + have hp : GlueComputes .exact (fun v : Fin 1 → ℕ => 1 ≤ v 0) + (fun v => v 0) (fun v => v 0) 1 := GlueComputes.proj 0 (fun _ _ => le_rfl) + have hc : GlueComputes .exact (fun v : Fin 1 → ℕ => 1 ≤ v 0) + (fun _ => 1) (fun v => v 0) 1 := GlueComputes.const 1 (fun _ h => h) + have h := GlueComputes.bin_exact hp hc .sub + (fun v hv => by simp [BinOp.evalExact, hv, BinOp.eval]) + (fun v _ => Nat.sub_le (v 0) 1) + simpa [bodyInput, unitInput, wordOutput, Fin.appendEquiv, + Fin.append, Fin.addCases, BinOp.eval] using Realizes.glue bodyInput h + +private theorem decrementLoop_realizes : + Realizes .exact (fun d : (Unit × ℕ) × ℕ => iterate decrementBody d.1.1 d.2 d.1.2) + ((bodyInput.prodMap wordOutput).trans (Fin.appendEquiv (0 + 1) 1).toEmbedding) + wordOutput (fun w d s => iterationSafe decrementBody + (fun w d _ => 1 ≤ d.2 ∧ Fits w d.2) w d.1.1 d.2 d.1.2 s) + (fun d s => iterationWork decrementBody (fun _ _ => 3) d.1.1 d.2 d.1.2 s) := + Realizes.iterateRuntime unitInput wordOutput decrementBody_realizes + +example (s : Memory.State) : (Memory.spec 4).clientResult s + (iterate decrementBody () 3 7) = 4 := rfl + +example (s : Memory.State) : iterationSafe decrementBody + (fun w d _ => 1 ≤ d.2 ∧ Fits w d.2) 4 () 3 7 s := by + change (1 ≤ 7 ∧ Fits 4 7) ∧ (1 ≤ 6 ∧ Fits 4 6) ∧ (1 ≤ 5 ∧ Fits 4 5) ∧ True + decide + +example (s : Memory.State) : iterationSafe decrementBody + (fun w d _ => 1 ≤ d.2 ∧ Fits w d.2) 0 () 0 0 s := trivial + +private def pointerBody (d : Unit × ℕ) : Memory.Prog ℕ := Memory.read d.2 + +private theorem pointerBody_realizes : + Realizes .exact pointerBody bodyInput wordOutput + (fun _ _ _ => True) (fun _ _ => 0) := by + unfold pointerBody + simpa [bodyInput, unitInput, wordOutput, Fin.appendEquiv, Fin.append, Fin.addCases] using + Realizes.read (B := .exact) bodyInput (0 : Fin 1) + +/-- The mutable state supplies each next address; the loop is not repeated at its first address. -/ +private theorem pointerLoop_realizes : + Realizes .exact (fun d : (Unit × ℕ) × ℕ => iterate pointerBody d.1.1 d.2 d.1.2) + ((bodyInput.prodMap wordOutput).trans (Fin.appendEquiv (0 + 1) 1).toEmbedding) + wordOutput (fun w d s => iterationSafe pointerBody (fun _ _ _ => True) + w d.1.1 d.2 d.1.2 s) + (fun d s => iterationWork pointerBody (fun _ _ => 0) d.1.1 d.2 d.1.2 s) := + Realizes.iterateRuntime unitInput wordOutput pointerBody_realizes + +private def linkedMemory : Memory.State := + ⟨fun a => if a = 0 then 1 else if a = 1 then 2 else 0, 3⟩ + +example : (Memory.spec 2).clientResult linkedMemory (iterate pointerBody () 2 0) = 2 := rfl + +example : (Memory.spec 2).clientHistory linkedMemory (iterate pointerBody () 2 0) = + [.read 0, .read 1] := rfl + +example : (Memory.spec 2).ValidHistory linkedMemory + ((Memory.spec 2).clientHistory linkedMemory (iterate pointerBody () 2 0)) := by decide + +example : Memory.WellFormed 2 linkedMemory := by + refine ⟨by decide, fun a => ?_⟩ + change Fits 2 (if a = 0 then 1 else if a = 1 then 2 else 0) + split_ifs <;> decide + +end Cslib.WordRAM.Realization.Tests diff --git a/ComplexityTests/WordRAMRealizationProgram.lean b/ComplexityTests/WordRAMRealizationProgram.lean new file mode 100644 index 0000000000..808b775985 --- /dev/null +++ b/ComplexityTests/WordRAMRealizationProgram.lean @@ -0,0 +1,294 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.WordRAM.Realization.Program + +/-! Nonvacuous flat-program projections with uniform code and clock constants, for both +already installed arguments and an actually charged native list-input prefix. The former +includes width zero; the latter explicitly checks frontier overflow and the empty-input case. -/ + +namespace ComplexityTests.WordRAMRealizationProgram + +open Cslib.WordRAM Cslib.WordRAM.Realization Cslib.DataStructure + +private def word : ℕ ↪ (Fin 1 → ℕ) := + ⟨fun d _ => d, fun _ _ h => congrFun h 0⟩ + +private theorem copy_realizes : + Realizes .exact (fun d : ℕ => (pure d : Memory.Prog ℕ)) word word + (fun _ _ _ => True) (fun _ _ => 0) := by + refine ⟨0, 1, fun _ src tmp => .op (.copy (tmp 0) (src 0)), ?_⟩ + intro r frontier src tmp _ _ hf _ + refine ⟨by simp [Code.ReadsWidth, Op.ReadsWidth], ?_⟩ + intro w d x _ hin _ _ + have harg : x.regs (src 0) = d := congrFun hin 0 + refine ⟨x.set (tmp 0) d, ?_, ?_, ?_, ?_⟩ + · change ExecWithin _ _ _ _ 1 + simpa [harg] using + (ExecWithin.copy (B := .exact) (w := w) (x := x) (d := tmp 0) (s := src 0)) + · apply Memory.State.ext + · rfl + · exact Function.update_of_ne (hf 0).symm _ _ + · intro i + have hi : i = 0 := Fin.eq_zero i + subst i + change (x.set (tmp 0) d).regs (tmp 0) = d + simp [Data.set] + · intro i _ htmp + exact Function.update_of_ne (htmp 0).symm _ _ + +private def entry (r d : ℕ) : Data r := + ⟨fun i => if i.val = 0 then d else 0, fun _ => 0⟩ + +-- The register count, program and exchange constant precede every width and argument. +example : ∃ (r C : ℕ) (p : Program .exact r), + ∀ w d, Fits w d → + ∃ t, Executes w p ⟨0, entry r d⟩ d t ∧ t ≤ C + 1 := by + obtain ⟨n, C, emit, he⟩ := copy_realizes.exists_program + let m := 1 + n + 1 + let r := m + 2 + let frontier : Fin r := ⟨1, by dsimp [r, m]; omega⟩ + let src : Fin 1 → Fin r := fun _ => ⟨0, by dsimp [r, m]; omega⟩ + let tmp : Fin (1 + n + 1) → Fin r := fun j => ⟨j.val + 2, by dsimp [r, m]; omega⟩ + have hi : Function.Injective tmp := by + intro a b h + apply Fin.ext + have := congrArg Fin.val h + dsimp [tmp] at this + omega + have hd : ∀ i j, src i ≠ tmp j := by + intro i j h + have := congrArg Fin.val h + change 0 = j.val + 2 at this + omega + have hf : ∀ j, tmp j ≠ frontier := by + intro j h + have := congrArg Fin.val h + change j.val + 2 = 1 at this + omega + have hs : ∀ i, src i ≠ frontier := by + intro i h + have := congrArg Fin.val h + change 0 = 1 at this + omega + refine ⟨r, C, (emit frontier src tmp).program (tmp (Fin.castAdd (n + 1) (0 : Fin 1))), ?_⟩ + intro w d hfit + have hx : (entry r d).Bounded w := by + constructor + · intro i + by_cases h : i.val = 0 + · change Fits w (if i.val = 0 then d else 0) + rwa [ite_eq_left h] + · change Fits w (if i.val = 0 then d else 0) + rw [ite_eq_right h] + exact fits_zero w + · intro a + exact fits_zero w + have hin : (entry r d).regs ∘ src = word d := by + funext i + rfl + obtain ⟨t, ht, htime⟩ := he frontier src tmp hi hd hf hs 0 w d (entry r d) hx hin + trivial (by trivial) + change t ≤ C * (0 + 0 + 1) + 1 at htime + exact ⟨t, ht, by simpa using htime⟩ + +-- One fixed flat program now includes actual argument and frontier installation. +example : ∃ (r C : ℕ) (p : Program .exact r), + ∀ w d, Fits w 2 → Fits w d → + ∃ t, Executes w p (State.init [d]) d t ∧ t ≤ C + 1 := by + obtain ⟨n, C, emit, he⟩ := copy_realizes.exists_init_program (fun d => [d]) (by + intro d i + have hi : i = 0 := Fin.eq_zero i + subst i + rfl) + let m := 1 + n + 1 + let r := m + 2 + let frontier : Fin r := ⟨1, by dsimp [r, m]; omega⟩ + let src : Fin 1 → Fin r := fun _ => ⟨0, by dsimp [r, m]; omega⟩ + let tmp : Fin (1 + n + 1) → Fin r := fun j => ⟨j.val + 2, by dsimp [r, m]; omega⟩ + have hi : Function.Injective tmp := by + intro a b h + apply Fin.ext + have := congrArg Fin.val h + dsimp [tmp] at this + omega + have hd : ∀ i j, src i ≠ tmp j := by + intro i j h + have := congrArg Fin.val h + change 0 = j.val + 2 at this + omega + have hf : ∀ j, tmp j ≠ frontier := by + intro j h + have := congrArg Fin.val h + change j.val + 2 = 1 at this + omega + have hs : ∀ i, src i ≠ frontier := by + intro i h + have := congrArg Fin.val h + change 0 = 1 at this + omega + refine ⟨r, C, (Code.seq (inputPrefix frontier (tmp (Fin.last (1 + n))) src) + (emit frontier src tmp)).program (tmp (Fin.castAdd (n + 1) (0 : Fin 1))), ?_⟩ + intro w d h2 hfit + have hin : InputFits w [d] := by + refine ⟨h2.of_le (by simp), ?_⟩ + intro v hv + simp only [List.mem_singleton] at hv + exact hv.symm ▸ hfit + obtain ⟨t, ht, htime⟩ := he frontier src tmp (fun _ _ _ => Subsingleton.elim _ _) hi + hd hf hs 0 w d (by simp) h2 hin trivial (by trivial) + change t ≤ C * (0 + 0 + 1) + 1 at htime + exact ⟨t, ht, by simpa using htime⟩ + +private def unitWords : Unit ↪ (Fin 0 → ℕ) := + (Equiv.ofUnique Unit (Fin 0 → ℕ)).toEmbedding + +private def storeZero {r : ℕ} (src ptr : Fin r) : Code .exact r := + .seq (.op (.const ptr 0)) (.op (.store ptr src)) + +-- The client really writes a cell and returns Unit with zero output coordinates. +private theorem write_realizes : + Realizes .exact (fun d : ℕ => Memory.write 0 d) word unitWords + (fun _ _ _ => True) (fun _ _ => 0) := by + refine ⟨0, 1, fun _ src tmp => storeZero (src 0) (tmp 0), ?_⟩ + intro r frontier src tmp _ hd hf _ + refine ⟨by simp [storeZero, Code.ReadsWidth, Op.ReadsWidth], ?_⟩ + intro w d x _ hin _ _ + have harg : x.regs (src 0) = d := congrFun hin 0 + let y := (x.set (tmp 0) 0).write 0 d + refine ⟨y, ?_, ?_, (fun i => Fin.elim0 i), ?_⟩ + · have e₁ := ExecWithin.const (B := .exact) (x := x) (d := tmp 0) (fits_zero w) + have e₂ := ExecWithin.store (B := .exact) (w := w) (x := x.set (tmp 0) 0) + (a := tmp 0) (s := src 0) + have hs : (x.set (tmp 0) 0).regs (src 0) = d := by + simpa [hd 0 0] using harg + rw [hs] at e₂ + change ExecWithin w (storeZero (src 0) (tmp 0)) x y (1 * (1 + 0 + 1)) + simpa [storeZero] using e₁.seq e₂ + · apply Memory.State.ext + · rfl + · exact Function.update_of_ne (hf 0).symm _ _ + · intro j _ htmp + exact Function.update_of_ne (htmp 0).symm _ _ + +-- The flat Unit write starts at Data.init, not a preinstalled register environment. +example : ∃ (r C : ℕ) (p : Program .exact r), + ∀ w d, Fits w 2 → Fits w d → + ∃ t, Executes w p (State.init [d]) 0 t ∧ t ≤ C * 2 + 2 := by + obtain ⟨n, C, emit, he⟩ := write_realizes.exists_init_unit_program (fun d => [d]) (by + intro d i + have hi : i = 0 := Fin.eq_zero i + subst i + rfl) + let r := (0 + n + 1) + 2 + let frontier : Fin r := ⟨1, by dsimp [r]; omega⟩ + let src : Fin 1 → Fin r := fun _ => ⟨0, by dsimp [r]; omega⟩ + let tmp : Fin (0 + n + 1) → Fin r := fun j => ⟨j.val + 2, by dsimp [r]; omega⟩ + have hi : Function.Injective tmp := by + intro a b h + apply Fin.ext + have := congrArg Fin.val h + dsimp [tmp] at this + omega + have hd : ∀ i j, src i ≠ tmp j := by + intro i j h + have := congrArg Fin.val h + change 0 = j.val + 2 at this + omega + have hf : ∀ j, tmp j ≠ frontier := by + intro j h + have := congrArg Fin.val h + change j.val + 2 = 1 at this + omega + have hs : ∀ i, src i ≠ frontier := by + intro i h + have := congrArg Fin.val h + change 0 = 1 at this + omega + refine ⟨r, C, (Code.seq (inputPrefix frontier (tmp (Fin.last (0 + n))) src) + (.seq (emit frontier src tmp) (.op (.const (tmp 0) 0)))).program (tmp 0), ?_⟩ + intro w d h2 hfit + have hin : InputFits w [d] := by + refine ⟨h2.of_le (by simp), ?_⟩ + intro v hv + simp only [List.mem_singleton] at hv + exact hv.symm ▸ hfit + have hv : (Memory.spec w).ValidHistory (inputState [d]) + ((Memory.spec w).clientHistory (inputState [d]) (Memory.write 0 d)) := by + exact ⟨⟨by simp [inputState], hfit⟩, trivial⟩ + obtain ⟨t, ht, htime⟩ := he frontier src tmp (fun _ _ _ => Subsingleton.elim _ _) hi + hd hf hs w d (by simp) h2 hin trivial hv + change t ≤ C * (1 + 0 + 1) + 2 at htime + exact ⟨t, ht, by simpa using htime⟩ + +-- The same Unit observation supports all widths, including zero, for a pure Unit client. +private theorem pure_unit_realizes : + Realizes .exact (fun _ : Unit => (pure () : Memory.Prog Unit)) unitWords unitWords + (fun _ _ _ => True) (fun _ _ => 0) := by + refine ⟨0, 1, fun _ _ tmp => .op (.copy (tmp 0) (tmp 0)), ?_⟩ + intro r frontier src tmp _ _ _ _ + refine ⟨by simp [Code.ReadsWidth, Op.ReadsWidth], ?_⟩ + intro w d x _ _ _ _ + refine ⟨x, ?_, rfl, (fun i => Fin.elim0 i), fun _ _ _ => rfl⟩ + change ExecWithin w (.op (.copy (tmp 0) (tmp 0))) x x 1 + simpa [Data.set] using + (ExecWithin.copy (B := .exact) (w := w) (x := x) (d := tmp 0) (s := tmp 0)) + +example : ∃ (r C : ℕ) (p : Program .exact r), + ∀ w, ∃ t, Executes w p (State.init []) 0 t ∧ t ≤ C + 2 := by + obtain ⟨n, C, emit, he⟩ := pure_unit_realizes.exists_unit_program + let r := (0 + n + 1) + 1 + let frontier : Fin r := ⟨0, by dsimp [r]; omega⟩ + let src : Fin 0 → Fin r := Fin.elim0 + let tmp : Fin (0 + n + 1) → Fin r := fun j => ⟨j.val + 1, by dsimp [r]; omega⟩ + have hi : Function.Injective tmp := by + intro a b h + apply Fin.ext + have := congrArg Fin.val h + dsimp [tmp] at this + omega + have hf : ∀ j, tmp j ≠ frontier := by + intro j h + have := congrArg Fin.val h + change j.val + 1 = 0 at this + omega + refine ⟨r, C, (Code.seq (emit frontier src tmp) (.op (.const (tmp 0) 0))).program + (tmp 0), ?_⟩ + intro w + have hx : (Data.init [] : Data r).Bounded w := + Data.Bounded.init ⟨fits_zero w, by simp⟩ + obtain ⟨t, ht, htime⟩ := he frontier src tmp hi (fun i => Fin.elim0 i) hf + (fun i => Fin.elim0 i) w () (Data.init []) hx (by funext i; exact Fin.elim0 i) + trivial (by trivial) + change t ≤ C * (0 + 0 + 1) + 2 at htime + exact ⟨t, ht, by simpa [State.init] using htime⟩ + +-- The actual prefix, Unit store and scalar-observation instruction preserve the write. +example : ((Code.seq (inputPrefix (r := 4) 1 2 (fun _ : Fin 1 => 0)) + (.seq (storeZero 0 2) (.op (.const 2 0)))).eval 2 30 (Data.init [3])).map + (fun z => (z.1.mem 0, z.1.regs 1, z.1.regs 2, z.2)) = some (3, 2, 0, 10) := by + decide + +-- The charged prefix really reads memory and installs the frontier before its body. +example : ((inputPrefix (r := 4) 1 2 (fun _ : Fin 1 => 0)).eval 2 20 (Data.init [3])).map + (fun z => (z.1.regs 0, z.1.regs 1, z.2)) = some (3, 2, 7) := by + decide + +-- With no arguments the real empty-case copy still costs one instruction. +example : ((inputPrefix (r := 2) 0 1 (Fin.elim0 : Fin 0 → Fin 2)).eval 1 20 + (Data.init [])).map (fun z => (z.1.regs 0, z.2)) = some (1, 5) := by + decide + +-- A fitting input word alone is insufficient: the next free address must also fit. +example : (inputPrefix (r := 4) 1 2 (fun _ : Fin 1 => 0)).eval 1 20 (Data.init [1]) = none := by + decide + +-- The frontier cannot fit at width zero, even for empty input. +example : (inputPrefix (r := 2) 0 1 (Fin.elim0 : Fin 0 → Fin 2)).eval 0 20 + (Data.init []) = none := by + decide + +end ComplexityTests.WordRAMRealizationProgram diff --git a/ROADMAP.md b/ROADMAP.md index 678d8e30d8..134291d41d 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -635,10 +635,11 @@ reusing graph and matroid foundations and the shared data-structure interfaces. all four memory queries, register-word glue, native branches and binds with one fixed continuation emitter for all runtime replies. Its functional representation retains the complete memory and frontier, and its instruction currency adds local work to memory charges. - Remaining: bounded runtime loops, charged input installation and flat programs, operation-level - memory implementation and framed upper-ADT adapters. The full C2/M1b and queue transfer - objectives remain open; this foundation does not turn arbitrary host continuations into - machine-time certificates. + Bounded runtime loops also charge local computation and every iteration, including query-free + iterations. Fixed flat programs include the actual input-installation prefix and final halt, + with explicit input layout, fits and safe-domain hypotheses. Remaining: operation-level memory + implementation, framed upper-ADT adapters and the representative queue transfer. The full + C2/M1b objective remains open; arbitrary host continuations do not acquire machine-time bounds. - **Stateful algorithms and data structures:** Word-RAM controllers for larger adaptive and iterative clients, including the two-stack queue and timeline-editing controllers; reusable allocation and deallocation, shrinking, and live and peak space accounting; ownership and diff --git a/UPSTREAM.md b/UPSTREAM.md index 7a8ebc36cb..299a193948 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -513,9 +513,12 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he The bounded `WordRAM/Realization` foundation (#2213, under #2205) reuses the memory oracle and the same native `Code`/`ExecWithin` semantics. Its explicit faithful register encodings support all four memory queries, certified word glue, branches and uniform - reply-dependent bind, charging local work as well as memory operations. Query arguments are - already in live registers; whole-input installation, final halt, bounded-loop and local-ADT - adapter endpoints remain separate work. This adds no abstract-catalogue headline. + reply-dependent bind, charging local work as well as memory operations. Bounded native loops + charge iterations even without a query. The initialized flat-program projection installs + live inputs in `2*k+5` instructions and includes the final halt; Unit observation additionally + writes a real zero word. Input layout, bounded words and safe domains remain explicit. + Generic memory and local-ADT adapter endpoints remain separate work. This adds no + abstract-catalogue headline. `DataStructure/OrderedSet` and `DataStructure/OrderedSet/*` provide neutral optional-minimum contracts, a typed finite-set interface including deletion, and adaptive minimum extraction, with no tree or cost dependency. Bounded-key implementations may specialize From 04a06b527e18c346174a445eed09e2e9c342f9e8 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 19:41:13 -0500 Subject: [PATCH 16/27] feat(WordRAM): derive global and framed implementation certificates Project the four fixed memory-query bodies into the existing semantic StateTransitionImpl interface with explicit delivered-argument preparation and body-only cost bounds. Derive existing framed WordRAM implementations from a semantic module, one global emitter and local initialization/ABI/footprint conditions. Runtime argument maps do not choose a new emitter per object. Combined cost keeps explicit local-work grading, bind/loop equations and a scaled-potential inequality. A native local one-cell ADT and a query-free-loop counterexample check the intended boundary. Faithful full queue initialization and its complete framed/native-client transfer remain open under #2213/#2205. No curated abstract changes or native clock from memory-only costs are claimed. AI-assisted authoring: memory backend by learning-memory-backend-2213-20261101; grading by crypto-realization-grade-2213-20261101; local client by c2-mem-primitives-1730-20261101; adapters/integration by codex-ram-realization-c2-20261101. All new public API needs outside-fleet review. --- Complexity.lean | 5 + .../WordRAM/Realization/Grade.lean | 110 ++++++ .../WordRAM/Realization/MemoryBackend.lean | 320 ++++++++++++++++++ .../WordRAM/Realization/Module.lean | 168 +++++++++ .../WordRAM/Realization/Result.lean | 68 ++++ .../WordRAM/Realization/Witness.lean | 70 ++++ ComplexityTests/WordRAMMemoryBackend.lean | 107 ++++++ ComplexityTests/WordRAMRealizationGrade.lean | 111 ++++++ ComplexityTests/WordRAMRealizationLocal.lean | 247 ++++++++++++++ ROADMAP.md | 9 +- UPSTREAM.md | 9 +- 11 files changed, 1219 insertions(+), 5 deletions(-) create mode 100644 Complexity/Computability/WordRAM/Realization/Grade.lean create mode 100644 Complexity/Computability/WordRAM/Realization/MemoryBackend.lean create mode 100644 Complexity/Computability/WordRAM/Realization/Module.lean create mode 100644 Complexity/Computability/WordRAM/Realization/Result.lean create mode 100644 Complexity/Computability/WordRAM/Realization/Witness.lean create mode 100644 ComplexityTests/WordRAMMemoryBackend.lean create mode 100644 ComplexityTests/WordRAMRealizationGrade.lean create mode 100644 ComplexityTests/WordRAMRealizationLocal.lean diff --git a/Complexity.lean b/Complexity.lean index 25721a3ac3..3c22b44980 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2560,9 +2560,14 @@ public import Complexity.Computability.WordRAM.Realization.Basic public import Complexity.Computability.WordRAM.Realization.Bind public import Complexity.Computability.WordRAM.Realization.Branch public import Complexity.Computability.WordRAM.Realization.Glue +public import Complexity.Computability.WordRAM.Realization.Grade public import Complexity.Computability.WordRAM.Realization.Loop +public import Complexity.Computability.WordRAM.Realization.MemoryBackend +public import Complexity.Computability.WordRAM.Realization.Module public import Complexity.Computability.WordRAM.Realization.Primitive public import Complexity.Computability.WordRAM.Realization.Program +public import Complexity.Computability.WordRAM.Realization.Result +public import Complexity.Computability.WordRAM.Realization.Witness public import Complexity.Computability.WordRAM.RegisterFrame public import Complexity.Computability.WordRAM.Sequence public import Complexity.Computability.WordRAM.Signed.Arithmetic diff --git a/Complexity/Computability/WordRAM/Realization/Grade.lean b/Complexity/Computability/WordRAM/Realization/Grade.lean new file mode 100644 index 0000000000..5e3ae79824 --- /dev/null +++ b/Complexity/Computability/WordRAM/Realization/Grade.lean @@ -0,0 +1,110 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Realization.Loop + +/-! +# Combined memory and local-work currency + +`combinedCost p work s` adds the actual memory-history charge to an explicit local-work grading. +The grading is supplied by the client and matched by its `Realizes` certificate; the extensional +`Memory.Prog` term does not determine a canonical local-work cost. In particular, monad laws may +erase pure binds, whereas a chosen native realization must still pay for its control and glue. + +The bind law uses the actual interpreted reply and memory state and includes one control-work +unit. The loop law reuses `iterationSum_eq`, retaining one control-work unit even on iterations +that issue no memory query. These are currency identities, not native instruction certificates. +A memory-only upper estimate requires a separate uniform bound on the supplied local work. + +## Main definitions + +* `combinedCost`: memory-history charge plus the explicitly supplied local-work grading. + +## Main results + +* `combinedCost_pure`: semantic return retains its local-work charge. +* `combinedCost_bind`: combined charges split at the actual returned value and state. +* `combinedCost_iterate`: a bounded loop sums body charges and adds its iteration count. +* `combinedCost_le_of_work_le`: a uniform local-work envelope gives a memory-only estimate. +* `scaled_potential_pay`: explicit clock, local-work and memory-potential bounds imply a scaled + potential payment inequality. + +## References + +This grading API is original infrastructure for the existing memory-client realization rules. +-/ + +@[expose] public section + +namespace Cslib.WordRAM.Realization + +open Cslib.DataStructure + +variable {α β δ σ : Type} + +/-- The actual memory-history charge plus an explicit realization grading. This definition +alone does not certify that `work` pays for any native implementation. -/ +def combinedCost (p : Memory.Prog α) (work : Memory.State → ℕ) (s : Memory.State) : ℕ := + memoryCost p s + work s + +/-- Returning without a memory query still retains the supplied local-work grading. -/ +@[simp] theorem combinedCost_pure (a : α) (work : Memory.State → ℕ) (s : Memory.State) : + combinedCost (pure a) work s = work s := Nat.zero_add (work s) + +/-- Sequential local work includes one control unit and evaluates the continuation grading at +the reply and state actually reached by the first computation. -/ +theorem combinedCost_bind (p : Memory.Prog α) (q : α → Memory.Prog β) + (first : Memory.State → ℕ) (next : α → Memory.State → ℕ) (s : Memory.State) : + combinedCost (p.bind q) + (fun s => first s + next ((Memory.spec 0).clientResult s p) + ((Memory.spec 0).clientState s p) + 1) s = + combinedCost p first s + + combinedCost (q ((Memory.spec 0).clientResult s p)) + (next ((Memory.spec 0).clientResult s p)) ((Memory.spec 0).clientState s p) + 1 := by + simp only [combinedCost, memoryCost_bind] + omega + +/-- Combined body charges are summed along the actual joint value/memory transition. The +additional `n` pays control work on every iteration, including query-free iterations. -/ +theorem combinedCost_iterate (body : δ × σ → Memory.Prog σ) + (work : (δ × σ) → Memory.State → ℕ) (d : δ) (n : ℕ) (a : σ) (s : Memory.State) : + combinedCost (iterate body d n a) (iterationWork body work d n a) s = + iterationSum (iterationStep body d) + (fun q => combinedCost (body (d, q.1)) (work (d, q.1)) q.2) n (a, s) + n := + (iterationSum_eq body work d n a s).symm + +/-- A memory-only estimate is available under an explicit uniform local-work envelope. The +constants precede every state. This arithmetic estimate supplies no native execution claim. -/ +theorem combinedCost_le_of_work_le (p : Memory.Prog α) (work : Memory.State → ℕ) + (a b : ℕ) (hwork : ∀ s, work s ≤ a * memoryCost p s + b) (s : Memory.State) : + combinedCost p work s ≤ (a + 1) * memoryCost p s + b := by + have h := hwork s + simp only [combinedCost, Nat.add_mul, Nat.one_mul] + omega + +/-- Transfer a certified combined clock through an explicit local-work envelope and a memory +amortization inequality. Scale the memory potential by the same fixed exchange factor; the +result is pure arithmetic and does not supply an execution certificate. All constants and +charges are explicit, so a consumer must discharge the three premises for its own operation. -/ +theorem scaled_potential_pay (M G n Φpre Φpost q factor a b : ℕ) + (hn : n ≤ factor * (M + G + 1)) (hwork : G ≤ a * M + b) + (hmem : M + Φpost ≤ q + Φpre) : + n + factor * (a + 1) * Φpost ≤ factor * (a + 1) * q + factor * (b + 1) + + factor * (a + 1) * Φpre := by + have hcharge : M + G + 1 ≤ (a + 1) * M + (b + 1) := by + rw [Nat.add_mul, Nat.one_mul] + omega + have hclock := hn.trans (Nat.mul_le_mul_left factor hcharge) + rw [Nat.mul_add, ← Nat.mul_assoc] at hclock + have hpotential := Nat.mul_le_mul_left (factor * (a + 1)) hmem + rw [Nat.mul_add (factor * (a + 1)) M Φpost, + Nat.mul_add (factor * (a + 1)) q Φpre] at hpotential + omega + +end Cslib.WordRAM.Realization diff --git a/Complexity/Computability/WordRAM/Realization/MemoryBackend.lean b/Complexity/Computability/WordRAM/Realization/MemoryBackend.lean new file mode 100644 index 0000000000..423bd3338a --- /dev/null +++ b/Complexity/Computability/WordRAM/Realization/MemoryBackend.lean @@ -0,0 +1,320 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Realization.Primitive +public import Complexity.Computability.WordRAM.Realization.Witness +public import Complexity.DataStructure.StateTransition.Resource + +/-! +# An operation-level native backend for the memory interface + +Four uniform primitive realization witnesses supply four static native bodies. The source query +constructor selects its body; its argument values never select or generate native code. This is +an operation-level implementation for `DataStructure.Module.realize`, not a single native +dispatcher for all queries. Witness selection uses classical choice from realization proofs; +no effective code extraction is asserted. + +`prepare` installs already delivered arguments in two registers without changing memory or the +frontier. This is a semantic calling convention, and its installation and result decoding are +uncharged. `instruction_bound` counts only the selected body's actual `Exec` instructions; it +does not include a final halt or imply a whole-client machine-clock bound. Client-side argument +computation, glue and control require the separate graded realization rules. + +The representation retains the literal global memory function and frontier, as well as global +word bounds. Every actual execution from the prepared state is correct, by determinism of the +same native code whose existence follows from its realization certificate. + +## Main definitions + +* `MemoryBackend.body`, `MemoryBackend.prepare`, `MemoryBackend.exec`: static operation bodies + and their existing native execution evidence. +* `MemoryBackend.implementation`: a generic `DataStructure.StateTransitionImpl`. + +## Main results + +* `MemoryBackend.instruction_bound`: a uniform body-instruction bound in `Memory.charge + 1`. +-/ + + +namespace Cslib.WordRAM.Realization.MemoryBackend + +open Cslib.DataStructure + +private abbrev word : ℕ ↪ (Fin 1 → ℕ) := + (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + +private theorem readCertificate : + Realizes .exact (fun a : ℕ => Memory.read a) word word + (fun _ _ _ => True) (fun _ _ => 0) := Realizes.read word 0 + +private theorem writeCertificate : + Realizes .exact (fun a : Fin 2 → ℕ => Memory.write (a 0) (a 1)) + (Function.Embedding.refl _) (Equiv.ofUnique Unit (Fin 0 → ℕ)).toEmbedding + (fun _ _ _ => True) (fun _ _ => 0) := Realizes.write (.refl _) 0 1 + +private theorem allocCertificate : + Realizes .exact (fun a : ℕ => Memory.alloc a) word word + (fun _ _ _ => True) (fun _ _ => 0) := Realizes.alloc word 0 + +private theorem dirtyCertificate : + Realizes .exact (fun a : ℕ => Memory.allocDirty a) word word + (fun _ _ _ => True) (fun _ _ => 0) := Realizes.allocDirty word 0 + +private noncomputable def slots : ℕ := + (1 + readCertificate.scratchSize + 1) + (0 + writeCertificate.scratchSize + 1) + + (1 + allocCertificate.scratchSize + 1) + (1 + dirtyCertificate.scratchSize + 1) + +/-- One register count is selected before every width, state, query and argument. -/ +public noncomputable def registers : ℕ := slots + 3 + +/-- Register zero retains the allocation frontier across calls. -/ +public noncomputable def frontier : Fin registers := ⟨0, by unfold registers; omega⟩ + +private noncomputable def src {k : ℕ} (hk : k ≤ 2) (i : Fin k) : Fin registers := + ⟨i.val + 1, by have := i.isLt; unfold registers; omega⟩ + +private noncomputable def tmp {n : ℕ} (hn : n ≤ slots) (i : Fin n) : Fin registers := + ⟨i.val + 3, by have := i.isLt; unfold registers; omega⟩ + +private theorem layout {k n : ℕ} (hk : k ≤ 2) (hn : n ≤ slots) : + Function.Injective (tmp hn) ∧ (∀ i j, src hk i ≠ tmp hn j) ∧ + (∀ j, tmp hn j ≠ frontier) ∧ (∀ i, src hk i ≠ frontier) := by + refine ⟨?_, ?_, ?_, ?_⟩ + · intro i j h + apply Fin.ext + have := congrArg Fin.val h + simp only [tmp] at this + omega + · intro i j h + have := congrArg Fin.val h + simp only [src, tmp] at this + have := i.isLt + omega + · intro j h + have := congrArg Fin.val h + simp only [tmp, frontier] at this + omega + · intro i h + have := congrArg Fin.val h + simp only [src, frontier] at this + omega + +private theorem readSlots : 1 + readCertificate.scratchSize + 1 ≤ slots := by + unfold slots; omega + +private theorem writeSlots : 0 + writeCertificate.scratchSize + 1 ≤ slots := by + unfold slots; omega + +private theorem allocSlots : 1 + allocCertificate.scratchSize + 1 ≤ slots := by + unfold slots; omega + +private theorem dirtySlots : 1 + dirtyCertificate.scratchSize + 1 ≤ slots := by + unfold slots; omega + +/-- Four static bodies, selected only by the query constructor. All emitters were chosen before +the query's runtime arguments. There is no native operation-tag dispatcher. -/ +public noncomputable def body : Memory.Op → Code .exact registers + | .read _ => readCertificate.code frontier (src (by omega)) (tmp readSlots) + | .write _ _ => writeCertificate.code frontier (src le_rfl) (tmp writeSlots) + | .alloc _ => allocCertificate.code frontier (src (by omega)) (tmp allocSlots) + | .allocDirty _ => dirtyCertificate.code frontier (src (by omega)) (tmp dirtySlots) + +private def first : Memory.Op → ℕ + | .read a | .write a _ => a + | .alloc n | .allocDirty n => n + +private def second : Memory.Op → ℕ + | .write _ v => v + | _ => 0 + +/-- Semantic installation of delivered arguments in registers one and two. Installation is +uncharged and is not asserted to execute as part of `body`. -/ +public noncomputable def prepare (o : Memory.Op) (x : Data registers) : Data registers := + (x.set (src (k := 2) le_rfl 0) (first o)).set (src (k := 2) le_rfl 1) (second o) + +/-- Argument delivery leaves the literal whole memory and frontier unchanged. -/ +public theorem prepare_memoryState (o : Memory.Op) (x : Data registers) : + memoryState frontier (prepare o x) = memoryState frontier x := by + apply Memory.State.ext + · rfl + · simp [memoryState, prepare, Data.set, src, frontier] + +private theorem prepare_bounded {w : ℕ} {o : Memory.Op} {x : Data registers} + (hx : x.Bounded w) (hv : Memory.valid w (memoryState frontier x) o) : + (prepare o x).Bounded w := by + have hw := wellFormed_memoryState hx (frontier := frontier) + have hfirst : Fits w (first o) := by + cases o with + | read a => exact hw.fits_of_lt_frontier hv + | write a v => exact hw.fits_of_lt_frontier hv.1 + | alloc n => exact hv.of_le (Nat.le_add_left n _) + | allocDirty n => exact hv.of_le (Nat.le_add_left n _) + have hsecond : Fits w (second o) := by + cases o with + | write a v => exact hv.2 + | _ => exact fits_zero w + exact (hx.set hfirst).set hsecond + +private theorem prepare_unary (o : Memory.Op) (x : Data registers) : + (prepare o x).regs ∘ src (k := 1) (by omega) = word (first o) := by + funext i + have hi : i = 0 := Fin.eq_zero i + subst i + simp [prepare, src, Data.set, word] + +private theorem prepare_binary (a v : ℕ) (x : Data registers) : + (prepare (.write a v) x).regs ∘ src (k := 2) le_rfl = + fun i => if i = 0 then a else v := by + funext i + fin_cases i <;> simp [prepare, src, first, second, Data.set] + +/-- Typed observation of the primitive result: word answers occupy the first scratch register; +the store has the unique unit answer. Decoding performs no charged native instruction. -/ +public noncomputable def result (o : Memory.Op) (y : Data registers) : o.Result := + match o with + | .read _ => y.regs (tmp readSlots 0) + | .write _ _ => () + | .alloc _ => y.regs (tmp allocSlots 0) + | .allocDirty _ => y.regs (tmp dirtySlots 0) + +/-- Existing native `Exec` evidence for a typed operation, following uncharged argument delivery. +Its natural-number trace counts body instructions only, with no final halt. -/ +public noncomputable def exec (w : ℕ) : Execution (Memory.spec w) (Data registers) ℕ := + fun x o a y n => Exec w (body o) (prepare o x) y n ∧ a = result o y + +/-- The execution interface records actual native body instructions and the typed observation. -/ +public theorem exec_iff (w : ℕ) (x y : Data registers) (o : Memory.Op) (a : o.Result) (n : ℕ) : + exec w x o a y n ↔ Exec w (body o) (prepare o x) y n ∧ a = result o y := Iff.rfl + +/-- Changing a read's delivered address leaves its static native body unchanged. -/ +public theorem body_read_eq (a b : ℕ) : body (.read a) = body (.read b) := by rfl + +/-- Changing a store's delivered address or value leaves its static body unchanged. -/ +public theorem body_write_eq (a v b u : ℕ) : body (.write a v) = body (.write b u) := by rfl + +/-- Changing the allocation count leaves its static native body unchanged. -/ +public theorem body_alloc_eq (a b : ℕ) : body (.alloc a) = body (.alloc b) := by rfl + +/-- Changing the dirty-allocation count leaves its static body unchanged. -/ +public theorem body_allocDirty_eq (a b : ℕ) : body (.allocDirty a) = body (.allocDirty b) := by rfl + +/-- A single body-instruction exchange factor, independent of widths and arguments. -/ +public noncomputable def factor : ℕ := + max (max readCertificate.factor writeCertificate.factor) + (max allocCertificate.factor dirtyCertificate.factor) + +private theorem exists_body {w : ℕ} {x : Data registers} (o : Memory.Op) + (hx : x.Bounded w) (hv : Memory.valid w (memoryState frontier x) o) : + ∃ y n, Exec w (body o) (prepare o x) y n ∧ + n ≤ factor * (Memory.charge (memoryState frontier x) o + 1) ∧ + memoryState frontier y = (Memory.step (memoryState frontier x) o).1 ∧ + result o y = (Memory.step (memoryState frontier x) o).2 := by + have hb := prepare_bounded hx hv + cases o with + | read a => + obtain ⟨hi, hd, hf, hs⟩ := layout (k := 1) (by omega) readSlots + have hv' : (Memory.spec w).ValidHistory (memoryState frontier (prepare (.read a) x)) + ((Memory.spec w).clientHistory (memoryState frontier (prepare (.read a) x)) + (Memory.read a)) := by + simpa only [StateTransitionSpec.clientHistory, StateTransitionSpec.runClient_lift, + prepare_memoryState, StateTransitionSpec.validHistory_cons, + StateTransitionSpec.validHistory_nil, and_true] using hv + obtain ⟨y, ⟨n, hn, he⟩, hm, hout, _⟩ := + (readCertificate.code_spec frontier _ _ hi hd hf hs).2 w a _ hb + (prepare_unary (.read a) x) trivial hv' + refine ⟨y, n, he, ?_, ?_, ?_⟩ + · change n ≤ readCertificate.factor * (1 + 0 + 1) at hn + exact hn.trans (Nat.mul_le_mul_right _ (by unfold factor; omega)) + · simpa [StateTransitionSpec.clientState, StateTransitionSpec.runClient_lift, + prepare_memoryState] using hm + · simpa [result, word, tmp, StateTransitionSpec.clientResult, + StateTransitionSpec.runClient_lift, prepare_memoryState] using hout 0 + | write a v => + obtain ⟨hi, hd, hf, hs⟩ := layout (k := 2) le_rfl writeSlots + have hv' : (Memory.spec w).ValidHistory (memoryState frontier (prepare (.write a v) x)) + ((Memory.spec w).clientHistory (memoryState frontier (prepare (.write a v) x)) + (Memory.write a v)) := by + simpa only [StateTransitionSpec.clientHistory, StateTransitionSpec.runClient_lift, + prepare_memoryState, StateTransitionSpec.validHistory_cons, + StateTransitionSpec.validHistory_nil, and_true] using hv + obtain ⟨y, ⟨n, hn, he⟩, hm, _, _⟩ := + (writeCertificate.code_spec frontier _ _ hi hd hf hs).2 w + (fun i => if i = 0 then a else v) _ hb (prepare_binary a v x) trivial hv' + refine ⟨y, n, he, ?_, ?_, rfl⟩ + · change n ≤ writeCertificate.factor * (1 + 0 + 1) at hn + exact hn.trans (Nat.mul_le_mul_right _ (by unfold factor; omega)) + · simpa [StateTransitionSpec.clientState, StateTransitionSpec.runClient_lift, + prepare_memoryState] using hm + | alloc a => + obtain ⟨hi, hd, hf, hs⟩ := layout (k := 1) (by omega) allocSlots + have hv' : (Memory.spec w).ValidHistory (memoryState frontier (prepare (.alloc a) x)) + ((Memory.spec w).clientHistory (memoryState frontier (prepare (.alloc a) x)) + (Memory.alloc a)) := by + simpa only [StateTransitionSpec.clientHistory, StateTransitionSpec.runClient_lift, + prepare_memoryState, StateTransitionSpec.validHistory_cons, + StateTransitionSpec.validHistory_nil, and_true] using hv + obtain ⟨y, ⟨n, hn, he⟩, hm, hout, _⟩ := + (allocCertificate.code_spec frontier _ _ hi hd hf hs).2 w a _ hb + (prepare_unary (.alloc a) x) trivial hv' + refine ⟨y, n, he, ?_, ?_, ?_⟩ + · change n ≤ allocCertificate.factor * (a + 1 + 0 + 1) at hn + exact hn.trans (Nat.mul_le_mul_right _ (by unfold factor; omega)) + · simpa [StateTransitionSpec.clientState, StateTransitionSpec.runClient_lift, + prepare_memoryState] using hm + · simpa [result, word, tmp, StateTransitionSpec.clientResult, + StateTransitionSpec.runClient_lift, prepare_memoryState] using hout 0 + | allocDirty a => + obtain ⟨hi, hd, hf, hs⟩ := layout (k := 1) (by omega) dirtySlots + have hv' : (Memory.spec w).ValidHistory (memoryState frontier (prepare (.allocDirty a) x)) + ((Memory.spec w).clientHistory (memoryState frontier (prepare (.allocDirty a) x)) + (Memory.allocDirty a)) := by + simpa only [StateTransitionSpec.clientHistory, StateTransitionSpec.runClient_lift, + prepare_memoryState, StateTransitionSpec.validHistory_cons, + StateTransitionSpec.validHistory_nil, and_true] using hv + obtain ⟨y, ⟨n, hn, he⟩, hm, hout, _⟩ := + (dirtyCertificate.code_spec frontier _ _ hi hd hf hs).2 w a _ hb + (prepare_unary (.allocDirty a) x) trivial hv' + refine ⟨y, n, he, ?_, ?_, ?_⟩ + · change n ≤ dirtyCertificate.factor * (1 + 0 + 1) at hn + exact hn.trans (Nat.mul_le_mul_right _ (by unfold factor; omega)) + · simpa [StateTransitionSpec.clientState, StateTransitionSpec.runClient_lift, + prepare_memoryState] using hm + · simpa [result, word, tmp, StateTransitionSpec.clientResult, + StateTransitionSpec.runClient_lift, prepare_memoryState] using hout 0 + +/-- The memory interface implemented by four static bodies. The representation includes every +cell, the frontier and global word bounds; argument installation is semantic and uncharged. -/ +public noncomputable def implementation (w : ℕ) : StateTransitionImpl (Memory.spec w) (exec w) := + StateTransitionImpl.ofFn + (fun s x => memoryState frontier x = s ∧ x.Bounded w) + (fun o hr hv => by + obtain ⟨y, n, he, _, _, _⟩ := exists_body o hr.2 (hr.1 ▸ hv) + exact ⟨result o y, y, n, he, rfl⟩) + (by + intro s x o a y n hr hv he + obtain ⟨z, m, hz, _, hm, hout⟩ := exists_body o hr.2 (hr.1 ▸ hv) + obtain ⟨rfl, _⟩ := he.1.unique hz + exact ⟨he.2.trans (hout.trans (congrArg (fun t => (Memory.step t o).2) hr.1)), + (hm.trans (congrArg (fun t => (Memory.step t o).1) hr.1)), + he.1.bounded (prepare_bounded hr.2 (hr.1 ▸ hv))⟩) + +/-- The backend represents the full global memory and frontier, with global word bounds. -/ +public theorem rep_iff (w : ℕ) (s : Memory.State) (x : Data registers) : + (implementation w).rep s x ↔ memoryState frontier x = s ∧ x.Bounded w := Iff.rfl + +/-- A body-only instruction certificate. This omits argument delivery, decoding and halt, and +therefore does not assert a whole-client machine-clock bound. -/ +public theorem instruction_bound (w : ℕ) : + Bound (implementation w) id (fun s o => factor * (Memory.charge s o + 1)) := by + intro s x o a y n hr hv he + obtain ⟨z, m, hz, hn, _, _⟩ := exists_body o hr.2 (hr.1 ▸ hv) + obtain ⟨_, rfl⟩ := he.1.unique hz + simpa only [id_eq, hr.1] using hn + +end Cslib.WordRAM.Realization.MemoryBackend diff --git a/Complexity/Computability/WordRAM/Realization/Module.lean b/Complexity/Computability/WordRAM/Realization/Module.lean new file mode 100644 index 0000000000..76b6adbeab --- /dev/null +++ b/Complexity/Computability/WordRAM/Realization/Module.lean @@ -0,0 +1,168 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Realization.Witness +public import Complexity.DataStructure.StateTransition.Module +public import Complexity.DataStructure.WordRAM.StateTransition + +/-! +# Local data-structure adapters for memory-client realizations + +The kernel represents the entire memory function. A finite-footprint data structure instead +supplies its existing `SubroutineBase`, including initialization, register ABI and locality. +`Realizes.toStateTransitionImpl` combines those boundary conditions with a semantic `Module` +over the memory oracle. It derives the native method execution from the single realization +certificate; callers do not supply another execution proof or intermediate assertions. + +The adapter keeps the semantic method footprint and the resource inequality explicit. In +particular, global whole-memory equality does not satisfy finite-block `rep_supported`. +An upper ADT must prove its own local representation and initialization obligations. + +## Main definitions + +* `taggedMethod`: retain the operation index in a typed method result. +* `Realizes.toStateTransitionImplWithArgs`: instantiate one global emitter at an ADT boundary. +* `Realizes.toStateTransitionImpl`: specialize to operation-indexed method families. +-/ + +@[expose] public section + +namespace Cslib.WordRAM.Realization + +open Cslib.DataStructure + +variable {δ : Type} {B : Basis} {w r k l kInit : ℕ} {D : StateTransitionSpec.{0, 0, 0}} + +/-- Retaining the operation index permits one faithful result encoding for dependent observations. +This is an ordinary existing memory program, with no additional memory operation. -/ +def taggedMethod (M : Module D (Memory.spec w)) (o : D.Op) : Memory.Prog D.Label := do + let a ← M.methods o + pure ⟨o, a⟩ + +/-- Tagging changes only the result; the memory state and operation history are identical. -/ +theorem runClient_taggedMethod (M : Module D (Memory.spec w)) (o : D.Op) (s : Memory.State) : + (Memory.spec w).runClient s (taggedMethod M o) = + (⟨o, (Memory.spec w).clientResult s (M.methods o)⟩, + (Memory.spec w).clientState s (M.methods o), + (Memory.spec w).clientHistory s (M.methods o)) := by + change (Memory.spec w).runClient s + ((M.methods o).bind (fun a => .pure (⟨o, a⟩ : D.Label))) = _ + rw [StateTransitionSpec.runClient_bind] + simp only [StateTransitionSpec.runClient_pure, List.append_nil] + +namespace Realizes + +/-- Derive the existing finite-footprint Word-RAM implementation at the upper ADT boundary. +The supplied base already proves initialization and representation locality. `entry` matches +the actual register ABI; `finish` re-establishes that local representation from semantic module +correctness and the kernel's register frame. `footprint` is a semantic memory property of the +method, and `pay` is a pure inequality for the already certified combined instruction currency. +The argument map instantiates an already selected global emitter; runtime layouts stay encoded +data rather than choosing new code for each object. Neither obligation introduces an extra +execution semantics or grants free native glue. -/ +noncomputable def toStateTransitionImplWithArgs (M : Module D (Memory.spec w)) + {p : δ → Memory.Prog D.Label} {input : δ ↪ (Fin k → ℕ)} {output : D.Label ↪ (Fin l → ℕ)} + {safe : ℕ → δ → Memory.State → Prop} {work : δ → Memory.State → ℕ} + (h : Realizes B p input output safe work) + (arguments : D.Op → δ) (source : ∀ o, p (arguments o) = taggedMethod M o) + (frontier : Fin r) (src : Fin k → Fin r) + (tmp : Fin (l + h.scratchSize + 1) → Fin r) + (hi : Function.Injective tmp) (hd : ∀ i j, src i ≠ tmp j) + (hf : ∀ j, tmp j ≠ frontier) (hs : ∀ i, src i ≠ frontier) + {P : SubroutineCode B r kInit} {base : ℕ} {params : Fin kInit → ℕ} {s₀ : D.State} + (C : SubroutineBase P D.Result D.valid w base params s₀) + (hcode : P.code = h.code frontier src tmp) + (entry : ∀ {s x} o, C.rep s x → D.valid s o → C.argMatch o x.regs → + x.regs ∘ src = input (arguments o) ∧ safe w (arguments o) (memoryState frontier x) ∧ + M.rep s (memoryState frontier x)) + (decode : ∀ o a regs, + (∀ i : Fin l, regs (tmp (Fin.castAdd (h.scratchSize + 1) i)) = output ⟨o, a⟩ i) → + C.decode o regs = some a) + (finish : ∀ {s s' x y} o a, C.rep s x → D.valid s o → C.argMatch o x.regs → + D.step s o s' a → M.rep s' (memoryState frontier y) → y.Bounded w → + (∀ i, i ≠ frontier → (∀ j, tmp j ≠ i) → y.regs i = x.regs i) → C.rep s' y) + (footprint : ∀ {s e} o, M.rep s e → D.valid s o → + Modifies (Block.mk base C.space).footprint e.mem + ((Memory.spec w).clientState e (M.methods o)).mem) + (pay : ∀ {s s' x y} o a, C.rep s x → D.valid s o → C.argMatch o x.regs → + D.step s o s' a → M.rep s' (memoryState frontier y) → + memoryState frontier y = (Memory.spec w).clientState (memoryState frontier x) (M.methods o) → + (∀ i, i ≠ frontier → (∀ j, tmp j ≠ i) → y.regs i = x.regs i) → + h.factor * (memoryCost (p (arguments o)) (memoryState frontier x) + + work (arguments o) (memoryState frontier x) + 1) + C.potential y ≤ + C.charge s o + C.potential x) : + DataStructure.WordRAM.StateTransitionImpl P D w base params s₀ where + toSubroutineBase := C + step_exec {s x} o hr hv ha := by + obtain ⟨hin, hsafe, hM⟩ := entry o hr hv ha + have hvalid := ((Memory.spec w).demonicValid_iff_validHistory_runClient _ _).mp + (M.valid o hM hv) + have htag : (Memory.spec w).ValidHistory (memoryState frontier x) + ((Memory.spec w).clientHistory (memoryState frontier x) (p (arguments o))) := by + rw [source o] + simpa only [StateTransitionSpec.clientHistory, runClient_taggedMethod] using hvalid + obtain ⟨y, hy, hm, hout, hframe⟩ := + (h.code_spec frontier src tmp hi hd hf hs).2 w (arguments o) x (C.bounded hr) hin hsafe htag + have hm' : memoryState frontier y = + (Memory.spec w).clientState (memoryState frontier x) (M.methods o) := by + simpa only [source o, StateTransitionSpec.clientState, runClient_taggedMethod] using hm + have hrun := (Memory.spec w).clientRun_canonical (memoryState frontier x) (M.methods o) hvalid + obtain ⟨s', hstep, hM'⟩ := M.correct o hM hv hrun + rw [← hm'] at hM' + have hrep := finish o _ hr hv ha hstep hM' (hy.bounded (C.bounded hr)) hframe + have hdec := decode o ((Memory.spec w).clientResult (memoryState frontier x) (M.methods o)) + y.regs (by + simpa only [source o, StateTransitionSpec.clientResult, runClient_taggedMethod] using hout) + have hmod : Modifies (Block.mk base C.space).footprint x.mem y.mem := by + change Modifies (Block.mk base C.space).footprint + (memoryState frontier x).mem (memoryState frontier y).mem + rw [hm'] + exact footprint o hM hv + have hpay := pay o _ hr hv ha hstep hM' hm' hframe + obtain ⟨n, hn, he⟩ := hy + rw [← hcode] at he + exact ⟨y, s', _, n, he, hstep, hdec, hrep, hmod, by omega⟩ + +/-- Operation-indexed specialization of the global argument-map adapter. -/ +noncomputable def toStateTransitionImpl (M : Module D (Memory.spec w)) + {input : D.Op ↪ (Fin k → ℕ)} {output : D.Label ↪ (Fin l → ℕ)} + {safe : ℕ → D.Op → Memory.State → Prop} {work : D.Op → Memory.State → ℕ} + (h : Realizes B (taggedMethod M) input output safe work) + (frontier : Fin r) (src : Fin k → Fin r) + (tmp : Fin (l + h.scratchSize + 1) → Fin r) + (hi : Function.Injective tmp) (hd : ∀ i j, src i ≠ tmp j) + (hf : ∀ j, tmp j ≠ frontier) (hs : ∀ i, src i ≠ frontier) + {P : SubroutineCode B r kInit} {base : ℕ} {params : Fin kInit → ℕ} {s₀ : D.State} + (C : SubroutineBase P D.Result D.valid w base params s₀) + (hcode : P.code = h.code frontier src tmp) + (entry : ∀ {s x} o, C.rep s x → D.valid s o → C.argMatch o x.regs → + x.regs ∘ src = input o ∧ safe w o (memoryState frontier x) ∧ + M.rep s (memoryState frontier x)) + (decode : ∀ o a regs, + (∀ i : Fin l, regs (tmp (Fin.castAdd (h.scratchSize + 1) i)) = output ⟨o, a⟩ i) → + C.decode o regs = some a) + (finish : ∀ {s s' x y} o a, C.rep s x → D.valid s o → C.argMatch o x.regs → + D.step s o s' a → M.rep s' (memoryState frontier y) → y.Bounded w → + (∀ i, i ≠ frontier → (∀ j, tmp j ≠ i) → y.regs i = x.regs i) → C.rep s' y) + (footprint : ∀ {s e} o, M.rep s e → D.valid s o → + Modifies (Block.mk base C.space).footprint e.mem + ((Memory.spec w).clientState e (M.methods o)).mem) + (pay : ∀ {s s' x y} o a, C.rep s x → D.valid s o → C.argMatch o x.regs → + D.step s o s' a → M.rep s' (memoryState frontier y) → + memoryState frontier y = (Memory.spec w).clientState (memoryState frontier x) (M.methods o) → + (∀ i, i ≠ frontier → (∀ j, tmp j ≠ i) → y.regs i = x.regs i) → + h.factor * (memoryCost (taggedMethod M o) (memoryState frontier x) + + work o (memoryState frontier x) + 1) + C.potential y ≤ C.charge s o + C.potential x) : + DataStructure.WordRAM.StateTransitionImpl P D w base params s₀ := + toStateTransitionImplWithArgs M h id (fun _ => rfl) frontier src tmp hi hd hf hs C hcode + entry decode finish footprint pay + +end Realizes + +end Cslib.WordRAM.Realization diff --git a/Complexity/Computability/WordRAM/Realization/Result.lean b/Complexity/Computability/WordRAM/Realization/Result.lean new file mode 100644 index 0000000000..72db88c116 --- /dev/null +++ b/Complexity/Computability/WordRAM/Realization/Result.lean @@ -0,0 +1,68 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Realization.Basic + +/-! +# Reinterpreting faithfully encoded results + +Changing a semantic result costs no native instructions only when its complete encoded word +vector stays identical. This rule covers adding a uniquely determined type-level operation tag; +it does not allow host computation to change runtime output words for free. + +## Main results + +* `Realizes.result`: preserve the native code under an explicit equality of result encodings. +-/ + +@[expose] public section + +namespace Cslib.WordRAM.Realization + +open Cslib.DataStructure + +variable {B : Basis} {δ α β : Type} {k l : ℕ} + +private theorem runClient_result (w : ℕ) (p : Memory.Prog α) (f : α → β) (s : Memory.State) : + (Memory.spec w).runClient s (p.bind fun a => .pure (f a)) = + (f ((Memory.spec w).clientResult s p), (Memory.spec w).clientState s p, + (Memory.spec w).clientHistory s p) := by + rw [StateTransitionSpec.runClient_bind] + simp only [StateTransitionSpec.runClient_pure, List.append_nil] + +namespace Realizes + +/-- Relabel results only if their entire faithfully encoded register vectors are unchanged. +All witnesses, native instructions, safety hypotheses and local-work charges are preserved. -/ +theorem result {p : δ → Memory.Prog α} {input : δ ↪ (Fin k → ℕ)} + {output : α ↪ (Fin l → ℕ)} {output' : β ↪ (Fin l → ℕ)} + {safe : ℕ → δ → Memory.State → Prop} {work : δ → Memory.State → ℕ} + (h : Realizes B p input output safe work) (f : δ → α → β) + (hf : ∀ d a, output a = output' (f d a)) : + Realizes B (fun d => (p d).bind fun a => .pure (f d a)) input output' safe work := by + obtain ⟨n, C, emit, he⟩ := h + refine ⟨n, C, emit, ?_⟩ + intro r frontier src tmp hi hd hfr hs + obtain ⟨hnw, hrun⟩ := he frontier src tmp hi hd hfr hs + refine ⟨hnw, ?_⟩ + intro w d x hx hin hsafe hv + have hv' : (Memory.spec w).ValidHistory (memoryState frontier x) + ((Memory.spec w).clientHistory (memoryState frontier x) (p d)) := by + simpa only [StateTransitionSpec.clientHistory, runClient_result] using hv + obtain ⟨y, hy, hm, hout, hframe⟩ := hrun w d x hx hin hsafe hv' + refine ⟨y, ?_, ?_, ?_, hframe⟩ + · simpa only [memoryCost_bind, PFunctor.FreeM.pure_eq_pure, memoryCost_pure, + Nat.add_zero] using hy + · simpa only [StateTransitionSpec.clientState, runClient_result] using hm + · intro i + simpa only [StateTransitionSpec.clientResult, runClient_result, ← hf] using hout i + +end Realizes + +end Cslib.WordRAM.Realization diff --git a/Complexity/Computability/WordRAM/Realization/Witness.lean b/Complexity/Computability/WordRAM/Realization/Witness.lean new file mode 100644 index 0000000000..80ccb9fe1d --- /dev/null +++ b/Complexity/Computability/WordRAM/Realization/Witness.lean @@ -0,0 +1,70 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Realization.Basic + +/-! +# Selecting a uniform realization witness + +These projections choose one witness of `Realizes` before any runtime argument, width, memory +or register layout. They use classical choice from a proposition: they do not claim executable +extraction from an arbitrary proof. They let adapters refer to the same emitted native code and +its proved exchange factor, without adding another realization judgment. + +## Main definitions + +* `Realizes.scratchSize`, `Realizes.factor`, `Realizes.code`: select the uniform witness. + +## Main results + +* `Realizes.code_spec`: the selected code has the complete original realization contract. +-/ + +@[expose] public section + +namespace Cslib.WordRAM.Realization.Realizes + +open Cslib.DataStructure + +variable {B : Basis} {δ α : Type} {k l : ℕ} + {p : δ → Memory.Prog α} {input : δ ↪ (Fin k → ℕ)} {output : α ↪ (Fin l → ℕ)} + {safe : ℕ → δ → Memory.State → Prop} {work : δ → Memory.State → ℕ} + +/-- Number of auxiliary registers, chosen independently of every runtime value. -/ +noncomputable def scratchSize (h : Realizes B p input output safe work) : ℕ := h.choose + +/-- Uniform exchange factor for memory charges plus local work. -/ +noncomputable def factor (h : Realizes B p input output safe work) : ℕ := h.choose_spec.choose + +/-- One emitter, selected before the register layout or any runtime data. -/ +noncomputable def code (h : Realizes B p input output safe work) {r : ℕ} : + (frontier : Fin r) → (Fin k → Fin r) → (Fin (l + h.scratchSize + 1) → Fin r) → Code B r := + h.choose_spec.choose_spec.choose + +/-- The selected witness retains width independence, literal whole-memory semantics, all result +coordinates, and the original live-register frame. -/ +theorem code_spec (h : Realizes B p input output safe work) {r : ℕ} + (frontier : Fin r) (src : Fin k → Fin r) (tmp : Fin (l + h.scratchSize + 1) → Fin r) + (hi : Function.Injective tmp) (hd : ∀ i j, src i ≠ tmp j) + (hf : ∀ j, tmp j ≠ frontier) (hs : ∀ i, src i ≠ frontier) : + ¬(h.code frontier src tmp).ReadsWidth ∧ + ∀ (w : ℕ) (d : δ) (x : Data r), x.Bounded w → x.regs ∘ src = input d → + safe w d (memoryState frontier x) → + (Memory.spec w).ValidHistory (memoryState frontier x) + ((Memory.spec w).clientHistory (memoryState frontier x) (p d)) → + ∃ y, ExecWithin w (h.code frontier src tmp) x y + (h.factor * (memoryCost (p d) (memoryState frontier x) + + work d (memoryState frontier x) + 1)) ∧ + memoryState frontier y = (Memory.spec w).clientState (memoryState frontier x) (p d) ∧ + (∀ i : Fin l, y.regs (tmp (Fin.castAdd (h.scratchSize + 1) i)) = + output ((Memory.spec w).clientResult (memoryState frontier x) (p d)) i) ∧ + ∀ i, i ≠ frontier → (∀ j, tmp j ≠ i) → y.regs i = x.regs i := + h.choose_spec.choose_spec.choose_spec frontier src tmp hi hd hf hs + +end Cslib.WordRAM.Realization.Realizes diff --git a/ComplexityTests/WordRAMMemoryBackend.lean b/ComplexityTests/WordRAMMemoryBackend.lean new file mode 100644 index 0000000000..3103100266 --- /dev/null +++ b/ComplexityTests/WordRAMMemoryBackend.lean @@ -0,0 +1,107 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +-- upstream: candidate + +import Complexity.Computability.WordRAM.Realization.MemoryBackend + +/-! The operation-level backend composes through the existing generic semantics. These examples +exercise reply-dependent addresses, full-memory retention, body-only costs and zero-width empty +allocation. Argument delivery and client control are not charged by this backend. -/ + +open Cslib Cslib.DataStructure Cslib.WordRAM Cslib.WordRAM.Realization + +namespace ComplexityTests.WordRAMMemoryBackend + +open MemoryBackend + +-- The backend plugs into the existing semantic module projection without changing that API. +noncomputable example {D : StateTransitionSpec} (w : ℕ) + (M : DataStructure.Module D (Memory.spec w)) : + StateTransitionImpl D (fun x o a y ts => DataStructure.Executes (exec w) + (M.methods o) x a y ts) := M.realize (implementation w) + +-- Every actual native execution from the prepared state has the specified typed result and +-- full-memory post-state, even when that execution was not obtained from `total`. +example {w n : ℕ} {s : Memory.State} {x y : Data registers} {o : Memory.Op} + (hr : memoryState frontier x = s ∧ x.Bounded w) (hv : Memory.valid w s o) + (he : Exec w (body o) (prepare o x) y n) : + result o y = (Memory.step s o).2 ∧ memoryState frontier y = (Memory.step s o).1 ∧ + n ≤ factor * (Memory.charge s o + 1) := by + have hr' := (rep_iff w s x).mpr hr + have he' := (exec_iff w x y o (result o y) n).mpr ⟨he, rfl⟩ + obtain ⟨ha, hs⟩ := (implementation w).correct_ofFn hr' hv he' + exact ⟨ha, ((rep_iff w _ _).mp hs).1, instruction_bound w hr' hv he'⟩ + +private def client : Memory.Prog ℕ := do + let base ← Memory.alloc 2 + Memory.write base 7 + Memory.read base + +private noncomputable def initial : Data registers := ⟨fun _ => 3, fun _ => 9⟩ + +private theorem initial_bounded : initial.Bounded 4 := by + constructor + · intro i; change Fits 4 3; decide + · intro a; change Fits 4 9; decide + +private theorem initial_valid : + (Memory.spec 4).ValidHistory (memoryState frontier initial) + ((Memory.spec 4).clientHistory (memoryState frontier initial) client) := by + change Fits 4 (3 + 2) ∧ (3 < 3 + 2 ∧ Fits 4 7) ∧ 3 < 3 + 2 ∧ True + decide + +-- Allocation's answer determines both later query addresses. The backend retains inaccessible +-- cells as well as the frontier, and exposes the actual three native instruction-count traces. +example : ∃ y ts, DataStructure.Executes (exec 4) client initial 7 y ts ∧ + (memoryState frontier y).frontier = 5 ∧ y.mem 3 = 7 ∧ y.mem 10 = 9 ∧ + DataStructure.total id ts ≤ factor * 8 := by + have hr := (rep_iff 4 _ initial).mpr ⟨rfl, initial_bounded⟩ + obtain ⟨y, ts, he, hy⟩ := (implementation 4).exists_executes client hr initial_valid + have hb := DataStructure.Bound.client (instruction_bound 4) hr initial_valid he + obtain ⟨hm, _⟩ := (rep_iff 4 _ y).mp hy + refine ⟨y, ts, he, ?_, ?_, ?_, ?_⟩ + · exact congrArg Memory.State.frontier hm + · exact congrArg (fun s : Memory.State => s.mem 3) hm + · change (memoryState frontier y).mem 10 = 9 + have hc := congrArg (fun s : Memory.State => s.mem 10) hm + simpa [StateTransitionSpec.clientState, StateTransitionSpec.runClient, client, + Memory.step, memoryState, initial, clear, Block.Owns] using hc + · change DataStructure.total id ts ≤ factor * 4 + (factor * 2 + (factor * 2 + 0)) at hb + omega + +private noncomputable def zeroState : Data registers := ⟨fun _ => 0, fun _ => 0⟩ + +private theorem zero_rep : + (implementation 0).rep (memoryState frontier zeroState) zeroState := by + apply (rep_iff 0 _ _).mpr + refine ⟨rfl, ?_, ?_⟩ <;> intro i <;> exact fits_zero 0 + +-- Empty allocations remain valid at width zero, for both static allocation bodies. +example : ∃ y n, exec 0 zeroState (.alloc 0) 0 y n ∧ + memoryState frontier y = memoryState frontier zeroState := by + obtain ⟨a, y, n, he⟩ := (implementation 0).total (.alloc 0) zero_rep (fits_zero 0) + obtain ⟨ha, hr⟩ := (implementation 0).correct_ofFn (o := .alloc 0) zero_rep (fits_zero 0) he + change a = 0 at ha + subst a + refine ⟨y, n, he, ?_⟩ + exact ((rep_iff 0 _ _).mp hr).1.trans (by + apply Memory.State.ext + · simp [Memory.step] + · rfl) + +example : ∃ y n, exec 0 zeroState (.allocDirty 0) 0 y n ∧ + memoryState frontier y = memoryState frontier zeroState := by + obtain ⟨a, y, n, he⟩ := (implementation 0).total (.allocDirty 0) zero_rep (fits_zero 0) + obtain ⟨ha, hr⟩ := (implementation 0).correct_ofFn (o := .allocDirty 0) zero_rep (fits_zero 0) he + change a = 0 at ha + subst a + exact ⟨y, n, he, ((rep_iff 0 _ _).mp hr).1⟩ + +-- The four bodies are invariant under delivered argument values. +example (a v b u : ℕ) : body (.write a v) = body (.write b u) := body_write_eq a v b u + +end ComplexityTests.WordRAMMemoryBackend diff --git a/ComplexityTests/WordRAMRealizationGrade.lean b/ComplexityTests/WordRAMRealizationGrade.lean new file mode 100644 index 0000000000..b76834f193 --- /dev/null +++ b/ComplexityTests/WordRAMRealizationGrade.lean @@ -0,0 +1,111 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.WordRAM.Realization.Grade + +/-! The grading keeps actual replies, changed memory and query-free loop control visible. -/ + +namespace ComplexityTests.WordRAMRealizationGrade + +open Cslib.DataStructure Cslib.WordRAM.Realization + +private def initial : Memory.State := ⟨fun _ => 7, 3⟩ + +private def first : Memory.Prog ℕ := do + Memory.write 0 5 + Memory.read 0 + +private def next (a : ℕ) : Memory.Prog Unit := Memory.write 1 a + +-- The continuation receives reply5 and the updated cell0=5, not the initial cell0=7. +example : combinedCost (first.bind next) + (fun s => s.frontier + ((Memory.spec 0).clientResult s first + + ((Memory.spec 0).clientState s first).mem 0) + 1) initial = 17 := by + rw [combinedCost_bind first next (fun s => s.frontier) (fun a s => a + s.mem 0)] + decide + +example : (Memory.spec 3).ValidHistory initial + ((Memory.spec 3).clientHistory initial (first.bind next)) := by + decide + +-- Pure work is explicit: a semantic return does not erase the supplied glue charge. +example : combinedCost (pure () : Memory.Prog Unit) (fun s => s.frontier + 4) initial = 7 := by + rw [combinedCost_pure] + rfl + +private def growBody (d : Unit × ℕ) : Memory.Prog ℕ := do + let base ← Memory.allocDirty 1 + pure (base + d.2) + +private def growWork (d : Unit × ℕ) (s : Memory.State) : ℕ := d.2 + s.frontier + +-- Two allocations move the frontier3→4→5 and returned accumulator0→3→7. +-- The local body charges3+7 and two control units accompany the two memory charges. +example : combinedCost (iterate growBody () 2 0) (iterationWork growBody growWork () 2 0) + initial = 14 := by + rw [combinedCost_iterate] + decide + +example : (Memory.spec 3).ValidHistory initial + ((Memory.spec 3).clientHistory initial (iterate growBody () 2 0)) := by + decide + +-- A uniform local-work hypothesis justifies the optional memory-only arithmetic estimate. +example (s : Memory.State) : + combinedCost (Memory.read 0) (fun _ => 5) s ≤ 3 * memoryCost (Memory.read 0) s + 3 := by + apply combinedCost_le_of_work_le (a := 2) (b := 3) + intro t + change 5 ≤ 2 * 1 + 3 + decide + +-- A nonzero potential decrease pays memory charge12 from amortized charge9. All three +-- premises are tight: work29=2*12+5 and native budget126=3*(12+29+1). +example : 126 + 3 * (2 + 1) * 4 ≤ + 3 * (2 + 1) * 9 + 3 * (5 + 1) + 3 * (2 + 1) * 7 := by + exact scaled_potential_pay 12 29 126 7 4 9 3 2 5 (by decide) (by decide) (by decide) + +-- The queue's charge9 payment adapter can use its certified budget itself for n; the clock +-- premise is reflexive. Local work and the actual memory-potential inequality stay visible. +example (M G Φpre Φpost factor a b : ℕ) (hwork : G ≤ a * M + b) + (hmem : M + Φpost ≤ 9 + Φpre) : + factor * (M + G + 1) + factor * (a + 1) * Φpost ≤ + factor * (a + 1) * 9 + factor * (b + 1) + factor * (a + 1) * Φpre := + scaled_potential_pay M G (factor * (M + G + 1)) Φpre Φpost 9 factor a b le_rfl hwork hmem + +private def idleBody (_ : Unit × Unit) : Memory.Prog Unit := pure () + +private theorem idle_eq (n : ℕ) : iterate idleBody () n () = pure () := by + induction n with + | zero => rfl + | succ n ih => exact ih + +private theorem idle_work (n : ℕ) (s : Memory.State) : + iterationWork idleBody (fun _ _ => 0) () n () s = n := by + induction n with + | zero => rfl + | succ n ih => + change 0 + iterationWork idleBody (fun _ _ => 0) () n () s + 1 = n + 1 + rw [ih] + simp only [Nat.zero_add] + +private theorem idle_cost (n : ℕ) (s : Memory.State) : + combinedCost (iterate idleBody () n ()) (iterationWork idleBody (fun _ _ => 0) () n ()) s = + n := by + rw [idle_eq, combinedCost_pure, idle_work] + +-- Although every idle program is extensionally pure, its chosen loop grading is unbounded. +-- No uniform constant can pay this grading from its zero memory charge alone. +example : ¬∃ C, ∀ n, + combinedCost (iterate idleBody () n ()) + (iterationWork idleBody (fun _ _ => 0) () n ()) initial ≤ + C * (memoryCost (iterate idleBody () n ()) initial + 1) := by + rintro ⟨C, h⟩ + have hc := h (C + 1) + rw [idle_cost, idle_eq] at hc + change C + 1 ≤ C * (0 + 1) at hc + omega + +end ComplexityTests.WordRAMRealizationGrade diff --git a/ComplexityTests/WordRAMRealizationLocal.lean b/ComplexityTests/WordRAMRealizationLocal.lean new file mode 100644 index 0000000000..898191eb00 --- /dev/null +++ b/ComplexityTests/WordRAMRealizationLocal.lean @@ -0,0 +1,247 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.WordRAM.Realization.Primitive +import Complexity.Computability.WordRAM.Realization.Result +import Complexity.Computability.WordRAM.Realization.Module + +/-! +# A concrete local ADT realized through the memory kernel + +A read-only cell has natural-number state and one unit-indexed operation returning its state. +Its local representation owns exactly address zero. This client supplies the existing +`SubroutineBase` initialization, boundedness, locality and ABI obligations, then obtains the +native method execution from `Realizes.toStateTransitionImpl`. It does not supply an independent +execution proof for the method. +-/ + +namespace ComplexityTests.WordRAMRealizationLocal + +open Cslib Cslib.WordRAM Cslib.WordRAM.Realization Cslib.DataStructure + +private abbrev cell : StateTransitionSpec := + StateTransitionSpec.ofFn (State := ℕ) (Op := Unit) (Result := fun _ => ℕ) + (fun s _ => (s, s)) (fun _ _ => True) + +private def cellModule (w : ℕ) : Module cell (Memory.spec w) := + Module.ofFn (methods := fun _ => Memory.read 0) + (rep := fun s e => e.mem 0 = s ∧ e.frontier = 1) + (valid := by + intro s e o hr _ + change 0 < e.frontier ∧ True + exact ⟨by omega, trivial⟩) + (correct := by + intro s e o hr _ + change e.mem 0 = s ∧ (e.mem 0 = s ∧ e.frontier = 1) + exact ⟨hr.1, hr⟩) + +private def input : Unit ↪ (Fin 1 → ℕ) where + toFun _ _ := 0 + inj' := fun _ _ _ => Subsingleton.elim _ _ + +private abbrev word : ℕ ↪ (Fin 1 → ℕ) := + Equiv.toEmbedding (Equiv.symm (Equiv.funUnique (Fin 1) ℕ)) + +private def output : cell.Label ↪ (Fin 1 → ℕ) where + toFun a := word a.2 + inj' := by + rintro ⟨o, a⟩ ⟨o', a'⟩ h + cases o + cases o' + have ha := congrFun h 0 + change a = a' at ha + cases ha + rfl + +private def method (o : Unit) : Memory.Prog cell.Label := do + let a ← Memory.read 0 + pure ⟨o, a⟩ + +private theorem realized : + Realizes .exact method input output (fun _ _ _ => True) (fun _ _ => 0) := + (Realizes.read (B := .exact) input 0).result (fun o a => (⟨o, a⟩ : cell.Label)) + (fun _ _ => rfl) + +private noncomputable def registers : ℕ := realized.scratchSize + 8 + +private noncomputable def reg (i : Fin 8) : Fin registers := + ⟨i.val, by have := i.isLt; unfold registers; omega⟩ + +private noncomputable def src : Fin 1 → Fin registers := fun _ => reg 0 + +private noncomputable def scratch : Fin (1 + realized.scratchSize + 1) → Fin registers := + fun i => ⟨6 + i.val, by have := i.isLt; unfold registers; omega⟩ + +private theorem scratch_injective : Function.Injective scratch := by + intro i j hij + apply Fin.ext + have hval := congrArg Fin.val hij + change 6 + i.val = 6 + j.val at hval + omega + +private theorem src_scratch (i : Fin 1) (j : Fin (1 + realized.scratchSize + 1)) : + src i ≠ scratch j := by + intro hij + have hval := congrArg Fin.val hij + change 0 = 6 + j.val at hval + omega + +private theorem scratch_frontier (j : Fin (1 + realized.scratchSize + 1)) : + scratch j ≠ reg 1 := by + intro hj + have hval := congrArg Fin.val hj + change 6 + j.val = 1 at hval + omega + +private theorem src_frontier (i : Fin 1) : src i ≠ reg 1 := by + intro hi + have hval := congrArg Fin.val hi + change 0 = 1 at hval + omega + +private noncomputable def program : SubroutineCode .exact registers 0 where + allocSpan := .seq (.op (.copy (reg 2) (reg 1))) (.op (.const (reg 1) 1)) + initSetup := .op (.copy (reg 2) (reg 2)) + code := realized.code (reg 1) src scratch + frontierReg := reg 1 + baseReg := reg 2 + initParams := Fin.elim0 + curReg := reg 3 + zeroReg := reg 4 + oneReg := reg 5 + incCur := .exactBin .add (reg 3) (reg 3) (reg 5) + input := fun i => decide (i = reg 0) + output := scratch 0 + +private noncomputable def cellBase (w : ℕ) (h1 : Fits w 1) : + SubroutineBase program cell.Result cell.valid w 0 Fin.elim0 0 where + space := 1 + spanTime := 2 + setupTime := 1 + initPotential := 0 + charge _ _ := 2 * realized.factor + rep s x := x.Bounded w ∧ x.mem 0 = s ∧ x.regs (reg 1) = 1 + potential _ := 0 + encode _ _ := 0 + argMatch _ regs := regs (reg 0) = 0 + decode _ regs := some (regs (scratch 0)) + bounded hr := hr.1 + arguments_fit _ _ _ _ := fits_zero w + encode_matches _ _ := by simp [program] + rep_supported := by + intro s x x' hregs hmem hx' hr + refine ⟨hx', ?_, ?_⟩ + · exact (hmem 0 (by simp [Block.mem_footprint, Block.Owns])).trans hr.2.1 + · exact (hregs (reg 1) (by simp [program, reg])).trans hr.2.2 + potential_supported _ _ _ := rfl + clear_nodup := by simp [program, reg] + params_not_clear j := Fin.elim0 j + incCur_eval := by + intro x ho hfit + change x.regs (reg 5) = 1 at ho + change Fits w (x.regs (reg 3) + 1) at hfit + change (Op.exactBin .add (reg 3) (reg 3) (reg 5)).eval w x = + some (x.set (reg 3) (x.regs (reg 3) + 1)) + rw [Op.eval_exactBin, ho] + exact Data.put_of_fits hfit + span_exec := by + intro x hx hfront _ + change x.regs (reg 1) = 0 at hfront + let a := x.set (reg 2) (x.regs (reg 1)) + let y := a.set (reg 1) 1 + have hcopy : ExecWithin (B := .exact) w (.op (.copy (reg 2) (reg 1))) x a 1 := + ExecWithin.copy + have hconst : ExecWithin (B := .exact) w (.op (.const (reg 1) 1)) a y 1 := + ExecWithin.const h1 + refine ⟨y, hcopy.seq hconst, rfl, ?_, ?_, fun j => Fin.elim0 j⟩ + · simpa [program, y, a, Data.set, reg] using hfront + · simp [program, y, Data.set] + setup_exec := by + intro x hx _ hfront _ hzero + have hself : x.set (reg 2) (x.regs (reg 2)) = x := by + apply Data.ext + · exact Function.update_eq_self _ _ + · rfl + have he : ExecWithin (B := .exact) w (.op (.copy (reg 2) (reg 2))) x x 1 := by + simpa only [hself] using + (ExecWithin.copy (B := .exact) (w := w) (d := reg 2) (s := reg 2) (x := x)) + exact ⟨x, he, hfront, ⟨hx, hzero 0 (by simp [Block.mem_footprint, Block.Owns]), hfront⟩, + le_rfl, Modifies.refl _ _⟩ + +private noncomputable def implementation (w : ℕ) (h1 : Fits w 1) : + DataStructure.WordRAM.StateTransitionImpl program cell w 0 Fin.elim0 0 := by + refine Realizes.toStateTransitionImpl (cellModule w) realized (reg 1) src scratch + scratch_injective src_scratch scratch_frontier src_frontier (cellBase w h1) rfl + ?_ ?_ ?_ ?_ ?_ + · intro s x o hr _ ha + refine ⟨?_, trivial, hr.2⟩ + funext i + exact ha + · intro o a regs hout + have h0 := hout 0 + change regs (scratch 0) = a at h0 + exact congrArg some h0 + · intro s s' x y o a _ _ _ _ hrep hy _ + exact ⟨hy, hrep⟩ + · intro s e o _ _ + exact Modifies.refl _ _ + · intro s s' x y o a _ _ _ _ _ _ _ + change realized.factor * (1 + 0 + 1) + 0 ≤ 2 * realized.factor + 0 + omega + +private noncomputable def fixture : Data registers := + ⟨fun i => if i = reg 1 then 1 else 0, fun a => if a = 0 then 7 else 9⟩ + +-- The resulting native ADT returns a nonzero stored word and preserves unowned dirty memory. +example : + ∃ y n, Exec 4 program.code fixture y n ∧ + y.regs (scratch 0) = 7 ∧ y.mem 0 = 7 ∧ y.mem 100 = 9 ∧ + n ≤ 2 * realized.factor := by + let I := implementation 4 (by norm_num [Fits]) + have hb : fixture.Bounded 4 := by + constructor + · intro i + change Fits 4 (if i = reg 1 then 1 else 0) + split_ifs <;> norm_num [Fits] + · intro a + change Fits 4 (if a = 0 then 7 else 9) + split_ifs <;> norm_num [Fits] + have hr : I.rep 7 fixture := by + refine ⟨hb, rfl, ?_⟩ + simp [fixture] + have ha : I.argMatch () fixture.regs := by + change fixture.regs (reg 0) = 0 + simp [fixture, reg] + obtain ⟨y, s', a, n, he, hs, hd, hy, hm, hn⟩ := I.step_exec () hr trivial ha + have hstep := (cell.step_iff_stepFn 7 () s' a).mp hs + change (s', a) = (7, 7) at hstep + have hstate : s' = 7 := congrArg Prod.fst hstep + have hanswer : a = 7 := congrArg Prod.snd hstep + subst s' + subst a + change some (y.regs (scratch 0)) = some 7 at hd + change y.Bounded 4 ∧ y.mem 0 = 7 ∧ y.regs (reg 1) = 1 at hy + change n + 0 ≤ 2 * realized.factor + 0 at hn + have hout : y.regs (scratch 0) = 7 := Option.some.inj hd + have houtside := hm 100 (by + change 100 ∉ (Block.mk 0 1).footprint + simp [Block.mem_footprint, Block.Owns]) + exact ⟨y, n, he, hout, hy.2.1, houtside, hn⟩ + +-- The full existing initialization API is inhabited, including clearing an initially dirty cell. +example : + ∃ y, ExecWithin 4 program.initClear + (⟨fun _ => 0, fun _ => 9⟩ : Data registers) y 13 ∧ + (implementation 4 (by norm_num [Fits])).rep 0 y ∧ y.mem 100 = 9 := by + let I := implementation 4 (by norm_num [Fits]) + let x : Data registers := ⟨fun _ => 0, fun _ => 9⟩ + have hx : x.Bounded 4 := ⟨fun _ => fits_zero 4, fun _ => by norm_num [Fits]⟩ + obtain ⟨y, he, _, hr, _, hm⟩ := I.initClear_exec hx rfl (fun j => Fin.elim0 j) + exact ⟨y, he, hr, hm 100 (by + change 100 ∉ (Block.mk 0 1).footprint + simp [Block.mem_footprint, Block.Owns])⟩ + +end ComplexityTests.WordRAMRealizationLocal diff --git a/ROADMAP.md b/ROADMAP.md index 134291d41d..6037a5e4aa 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -637,9 +637,12 @@ reusing graph and matroid foundations and the shared data-structure interfaces. complete memory and frontier, and its instruction currency adds local work to memory charges. Bounded runtime loops also charge local computation and every iteration, including query-free iterations. Fixed flat programs include the actual input-installation prefix and final halt, - with explicit input layout, fits and safe-domain hypotheses. Remaining: operation-level memory - implementation, framed upper-ADT adapters and the representative queue transfer. The full - C2/M1b objective remains open; arbitrary host continuations do not acquire machine-time bounds. + with explicit input layout, fits and safe-domain hypotheses. A separate operation-level + memory implementation exposes body-only costs under delivered arguments. Existing framed + Word-RAM certificates can be derived from semantic modules after discharging their local + initialization, footprint and ABI conditions; a one-cell ADT checks that boundary. Remaining: + the representative framed queue and its faithful whole-memory allocation/native-client path. + The full C2/M1b objective remains open; arbitrary host continuations do not acquire runtime bounds. - **Stateful algorithms and data structures:** Word-RAM controllers for larger adaptive and iterative clients, including the two-stack queue and timeline-editing controllers; reusable allocation and deallocation, shrinking, and live and peak space accounting; ownership and diff --git a/UPSTREAM.md b/UPSTREAM.md index 299a193948..9e35c7b57a 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -517,8 +517,13 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he charge iterations even without a query. The initialized flat-program projection installs live inputs in `2*k+5` instructions and includes the final halt; Unit observation additionally writes a real zero word. Input layout, bounded words and safe domains remain explicit. - Generic memory and local-ADT adapter endpoints remain separate work. This adds no - abstract-catalogue headline. + The operation-level memory backend uses four fixed constructor bodies and the generic + implementation interface; its delivered-argument preparation is explicit and uncharged, so + its body bounds are not whole-client clocks. The upper local-ADT adapter reuses the existing + initialization/locality bundle and semantic module; its argument map instantiates one global + emitter rather than selecting code after runtime layouts. Combined cost takes the program and + an explicit work grading, with a separately proved envelope before dropping local work. + The full framed queue/faithful allocation client remains separate. No abstract headline is added. `DataStructure/OrderedSet` and `DataStructure/OrderedSet/*` provide neutral optional-minimum contracts, a typed finite-set interface including deletion, and adaptive minimum extraction, with no tree or cost dependency. Bounded-key implementations may specialize From 5917ae43a32fbff256a272e17cc0071a8ba69e36 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 19:57:32 -0500 Subject: [PATCH 17/27] feat(WordRAM): realize runtime-layout memory-stack methods Stage multiword results from fixed scalar-glue witnesses, charging every coordinate and output copy. Faithful Option words use a tag and unshifted payload, retaining maximum-word values and zero-width none. Consume the unchanged published Stack.Memory methods over full runtime layout words. Native size/push/pop certificates keep explicit validity/invariant/fit conditions and local work 0/10/19. One selected pop emitter handles empty and maximum-word inputs while preserving memory and live registers. This is a bounded #2213/#2205 milestone. The framed queue and faithful allocated queue-client path remain open. No abstract selection or headline changes. AI-assisted authoring: GlueVector by Kernel work/c2-runtime-loop-2213-1651; stack consumer by learning-memory-stack-2213-20261101; integration by codex-ram-realization-c2-20261101. Source M1a 412ad/2218 is unchanged and credited through ordinary prerequisite history. Outside-fleet review remains required. --- Complexity.lean | 2 + .../Realization/Client/MemoryStack.lean | 265 ++++++++++++++++++ .../WordRAM/Realization/GlueVector.lean | 214 ++++++++++++++ ComplexityTests/WordRAMMemoryStack.lean | 172 ++++++++++++ .../WordRAMRealizationGlueVector.lean | 154 ++++++++++ ROADMAP.md | 4 +- UPSTREAM.md | 4 + 7 files changed, 814 insertions(+), 1 deletion(-) create mode 100644 Complexity/Computability/WordRAM/Realization/Client/MemoryStack.lean create mode 100644 Complexity/Computability/WordRAM/Realization/GlueVector.lean create mode 100644 ComplexityTests/WordRAMMemoryStack.lean create mode 100644 ComplexityTests/WordRAMRealizationGlueVector.lean diff --git a/Complexity.lean b/Complexity.lean index 0e24792779..cb9df7d950 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2559,7 +2559,9 @@ public import Complexity.Computability.WordRAM.Randomized.UniformLoopExit public import Complexity.Computability.WordRAM.Realization.Basic public import Complexity.Computability.WordRAM.Realization.Bind public import Complexity.Computability.WordRAM.Realization.Branch +public import Complexity.Computability.WordRAM.Realization.Client.MemoryStack public import Complexity.Computability.WordRAM.Realization.Glue +public import Complexity.Computability.WordRAM.Realization.GlueVector public import Complexity.Computability.WordRAM.Realization.Grade public import Complexity.Computability.WordRAM.Realization.Loop public import Complexity.Computability.WordRAM.Realization.MemoryBackend diff --git a/Complexity/Computability/WordRAM/Realization/Client/MemoryStack.lean b/Complexity/Computability/WordRAM/Realization/Client/MemoryStack.lean new file mode 100644 index 0000000000..24be91672b --- /dev/null +++ b/Complexity/Computability/WordRAM/Realization/Client/MemoryStack.lean @@ -0,0 +1,265 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Realization.Bind +public import Complexity.Computability.WordRAM.Realization.Branch +public import Complexity.Computability.WordRAM.Realization.Glue +public import Complexity.Computability.WordRAM.Realization.GlueVector +public import Complexity.Computability.WordRAM.Realization.Primitive +public import Complexity.DataStructure.Stack.Memory + +/-! +# Native realizations of the memory-oracle stack + +The certificates consume the existing `Stack.Memory` methods unchanged. Every field of the +runtime layout has a faithful word encoding, so the emitter is chosen before the layout's +allocation addresses and all operation arguments. Arithmetic, copies, binds and branches have +explicit local-work charges in addition to the actual memory history. + +## Main definitions + +* `layoutWords`, `pushWords`: faithful encodings of the complete layout and push arguments. + +## Main results + +* `realizes_size`, `realizes_push`, `realizes_pop`: uniform native realizations of the existing + stack methods. +-/ + +@[expose] public section + +namespace Cslib.WordRAM.Realization.MemoryStack + +open Cslib.DataStructure + +private abbrev word : ℕ ↪ (Fin 1 → ℕ) := (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding +private abbrev unitWords : Unit ↪ (Fin 0 → ℕ) := (Equiv.ofUnique Unit (Fin 0 → ℕ)).toEmbedding + +/-- All four layout fields are retained: count base/length, then items base/length. -/ +def layoutWords : Stack.Memory.Layout ↪ (Fin 4 → ℕ) where + toFun L := ![L.count.base, L.count.length, L.items.base, L.items.length] + inj' := by + rintro ⟨⟨a, b⟩, ⟨c, d⟩⟩ ⟨⟨a', b'⟩, ⟨c', d'⟩⟩ h + have ha := congrFun h 0 + have hb := congrFun h 1 + have hc := congrFun h 2 + have hd := congrFun h 3 + change a = a' at ha + change b = b' at hb + change c = c' at hc + change d = d' at hd + cases ha; cases hb; cases hc; cases hd + rfl + +/-- Push arguments append the unshifted value to the complete runtime layout. -/ +def pushWords : Stack.Memory.Layout × ℕ ↪ (Fin 5 → ℕ) := + (layoutWords.prodMap (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding).trans + (Fin.appendEquiv 4 1).toEmbedding + +private def append {δ α : Type} {k l : ℕ} (input : δ ↪ (Fin k → ℕ)) + (answer : α ↪ (Fin l → ℕ)) : δ × α ↪ (Fin (k + l) → ℕ) := + (input.prodMap answer).trans (Fin.appendEquiv k l).toEmbedding + +private theorem addWords {δ : Type} {k : ℕ} (input : δ ↪ (Fin k → ℕ)) (i j : Fin k) : + Realizes .exact (fun d => pure (input d i + input d j)) input word + (fun w d _ => Fits w (input d i + input d j)) (fun _ _ => 3) := by + have ha : GlueComputes .exact (fun _ : Fin k → ℕ => True) (fun v => v i) + (fun v => v i + v j) 1 := GlueComputes.proj i (fun _ _ => Nat.le_add_right _ _) + have hb : GlueComputes .exact (fun _ : Fin k → ℕ => True) (fun v => v j) + (fun v => v i + v j) 1 := GlueComputes.proj j (fun _ _ => Nat.le_add_left _ _) + have h := Realizes.glue input (ha.bin_exact hb .add (fun _ _ => rfl) (fun _ _ => le_rfl)) + exact h.consequence (fun _ _ _ _ _ hf => ⟨trivial, hf⟩) (fun _ _ => le_rfl) + +private theorem increment {δ : Type} {k : ℕ} (input : δ ↪ (Fin k → ℕ)) (i : Fin k) : + Realizes .exact (fun d => pure (input d i + 1)) input word + (fun w d _ => Fits w (input d i + 1)) (fun _ _ => 3) := by + have ha : GlueComputes .exact (fun _ : Fin k → ℕ => True) (fun v => v i) + (fun v => v i + 1) 1 := GlueComputes.proj i (fun _ _ => Nat.le_add_right _ _) + have hb : GlueComputes .exact (fun _ : Fin k → ℕ => True) (fun _ => 1) + (fun v => v i + 1) 1 := GlueComputes.const 1 (fun _ _ => by omega) + have h := Realizes.glue input (ha.bin_exact hb .add (fun _ _ => rfl) (fun _ _ => le_rfl)) + exact h.consequence (fun _ _ _ _ _ hf => ⟨trivial, hf⟩) (fun _ _ => le_rfl) + +private theorem decrement {δ : Type} {k : ℕ} (input : δ ↪ (Fin k → ℕ)) (i : Fin k) : + Realizes .exact (fun d => pure (input d i - 1)) input word + (fun w d _ => 1 ≤ input d i ∧ Fits w (input d i)) (fun _ _ => 3) := by + have ha : GlueComputes .exact (fun v : Fin k → ℕ => 1 ≤ v i) (fun v => v i) + (fun v => v i) 1 := GlueComputes.proj i (fun _ _ => le_rfl) + have hb : GlueComputes .exact (fun v : Fin k → ℕ => 1 ≤ v i) (fun _ => 1) + (fun v => v i) 1 := GlueComputes.const 1 (fun _ h => h) + exact Realizes.glue input (ha.bin_exact hb .sub + (fun v h => by simp [h]) (fun _ _ => Nat.sub_le _ _)) + +private theorem returnNone {δ : Type} {k : ℕ} (input : δ ↪ (Fin k → ℕ)) : + Realizes .exact (fun _ => pure none) input optionWords + (fun _ _ _ => True) (fun _ _ => 4) := by + have h := Realizes.glueVector (B := .exact) input optionWords (fun _ => none) + (fun _ _ => 0) (fun _ _ => 0) (fun _ => 1) + (fun _ i => by fin_cases i <;> rfl) + (P := fun _ => True) (fun _ => GlueComputes.const 0 (fun _ _ => le_rfl)) + exact h.consequence (fun w _ _ _ _ _ => ⟨trivial, fun _ => fits_zero w⟩) + (fun _ _ => by simp) + +private theorem returnSome {δ : Type} {k : ℕ} (input : δ ↪ (Fin k → ℕ)) (j : Fin k) : + Realizes .exact (fun d => pure (some (input d j))) input optionWords + (fun w d _ => Fits w 1 ∧ Fits w (input d j)) (fun _ _ => 4) := by + let g : Fin 2 → (Fin k → ℕ) → ℕ := fun i v => if i = 0 then 1 else v j + have hc (i : Fin 2) : GlueComputes .exact (fun _ : Fin k → ℕ => True) (g i) (g i) 1 := by + fin_cases i + · exact GlueComputes.const 1 (fun _ _ => le_rfl) + · exact GlueComputes.proj j (fun _ _ => le_rfl) + have h := Realizes.glueVector input optionWords (fun d => some (input d j)) g g + (fun _ => 1) (fun _ i => by fin_cases i <;> rfl) hc + apply h.consequence (hwork := fun _ _ => by simp) + intro w d s _ _ ⟨h1, hv⟩ + refine ⟨trivial, fun i => ?_⟩ + fin_cases i + · exact h1 + · exact hv + +/-- Size uses the existing count query with no additional local computation. -/ +theorem realizes_size : + Realizes .exact Stack.Memory.size layoutWords + (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + (fun _ _ _ => True) (fun _ _ => 0) := Realizes.read layoutWords 0 + +/-- Push works uniformly for every runtime layout and value. The visible invariant and spare +capacity discharge the address and increment fit obligations. The two additions cost six local +instructions; four bind rules contribute four units of local control work. -/ +theorem realizes_push : + Realizes .exact (fun d : Stack.Memory.Layout × ℕ => Stack.Memory.push d.1 d.2) + pushWords (Equiv.ofUnique Unit (Fin 0 → ℕ)).toEmbedding + (fun w d s => d.1.Valid w d.1.items.length ∧ Stack.Memory.Inv d.1 s ∧ + s.mem d.1.count.base < d.1.items.length) (fun _ _ => 10) := by + let e₁ := append pushWords word + let e₂ := append e₁ word + let e₃ := append e₂ word + let e₄ := append e₃ unitWords + have hread : Realizes .exact (fun d : Stack.Memory.Layout × ℕ => Memory.read d.1.count.base) + pushWords word (fun _ _ _ => True) (fun _ _ => 0) := Realizes.read pushWords 0 + have haddr : Realizes .exact + (fun d : (Stack.Memory.Layout × ℕ) × ℕ => pure (d.1.1.items.base + d.2)) + e₁ word (fun w d _ => Fits w (d.1.1.items.base + d.2)) (fun _ _ => 3) := + addWords e₁ 2 5 + have hnext : Realizes .exact + (fun d : ((Stack.Memory.Layout × ℕ) × ℕ) × ℕ => pure (d.1.2 + 1)) + e₂ word (fun w d _ => Fits w (d.1.2 + 1)) (fun _ _ => 3) := increment e₂ 5 + have hwrite : Realizes .exact + (fun d : (((Stack.Memory.Layout × ℕ) × ℕ) × ℕ) × ℕ => + Memory.write d.1.2 d.1.1.1.2) + e₃ unitWords (fun _ _ _ => True) (fun _ _ => 0) := Realizes.write e₃ 6 4 + have hcount : Realizes .exact + (fun d : ((((Stack.Memory.Layout × ℕ) × ℕ) × ℕ) × ℕ) × Unit => + Memory.write d.1.1.1.1.1.count.base d.1.2) + e₄ unitWords (fun _ _ _ => True) (fun _ _ => 0) := Realizes.write e₄ 0 7 + have hwrites := (Realizes.bind hwrite hcount).consequence + (safe' := fun _ _ _ => True) (work' := fun _ _ => 1) + (fun _ _ _ _ _ _ => ⟨trivial, trivial⟩) (fun _ _ => le_rfl) + have hinc := (Realizes.bind hnext hwrites).consequence + (safe' := fun w d _ => Fits w (d.1.2 + 1)) (work' := fun _ _ => 5) + (fun _ _ _ _ _ h => ⟨h, trivial⟩) (fun _ _ => le_rfl) + have hcompute := (Realizes.bind haddr hinc).consequence + (safe' := fun w d _ => Fits w (d.1.1.items.base + d.2) ∧ Fits w (d.2 + 1)) + (work' := fun _ _ => 9) (fun _ _ _ _ _ h => h) (fun _ _ => le_rfl) + apply (Realizes.bind hread hcompute).consequence (hwork := fun _ _ => le_rfl) + intro w d s _ hw ⟨hL, hInv, hcap⟩ + change True ∧ Fits w (d.1.items.base + s.mem d.1.count.base) ∧ + Fits w (s.mem d.1.count.base + 1) + exact ⟨trivial, hw.fits_of_lt_frontier (hInv.item_lt hcap), + hL.capacity_fits.of_le (by omega)⟩ + +/-- Pop returns a presence tag and unshifted payload. Its positive branch uses defined exact +subtraction and performs the existing memory reads/write. All copies and branch/bind overhead +are charged; the local-work allowance is 19 for either path, including the shorter empty path. -/ +theorem realizes_pop : + Realizes .exact Stack.Memory.pop layoutWords optionWords + (fun w L s => L.Valid w L.items.length ∧ Stack.Memory.Inv L s) (fun _ _ => 19) := by + let e₁ := append layoutWords word + let e₂ := append e₁ word + let e₃ := append e₂ word + let e₄ := append e₃ word + let e₅ := append e₄ word + let e₆ := append e₅ unitWords + have hread : Realizes .exact (fun L : Stack.Memory.Layout => Memory.read L.count.base) + layoutWords word (fun _ _ _ => True) (fun _ _ => 0) := Realizes.read layoutWords 0 + have hzero : Realizes .exact (fun _ : Stack.Memory.Layout × ℕ => pure 0) e₁ word + (fun _ _ _ => True) (fun _ _ => 1) := by + have h := Realizes.glue e₁ + (GlueComputes.const (B := .exact) (P := fun _ => True) (bound := fun _ => 0) + 0 (fun _ _ => le_rfl)) + exact h.consequence (fun w _ _ _ _ _ => ⟨trivial, fits_zero w⟩) (fun _ _ => le_rfl) + have hsub : Realizes .exact + (fun d : (Stack.Memory.Layout × ℕ) × ℕ => pure (d.1.2 - 1)) e₂ word + (fun w d _ => 1 ≤ d.1.2 ∧ Fits w d.1.2) (fun _ _ => 3) := decrement e₂ 4 + have haddr : Realizes .exact + (fun d : ((Stack.Memory.Layout × ℕ) × ℕ) × ℕ => pure (d.1.1.1.items.base + d.2)) + e₃ word (fun w d _ => Fits w (d.1.1.1.items.base + d.2)) (fun _ _ => 3) := + addWords e₃ 2 6 + have hitem : Realizes .exact + (fun d : (((Stack.Memory.Layout × ℕ) × ℕ) × ℕ) × ℕ => Memory.read d.2) + e₄ word (fun _ _ _ => True) (fun _ _ => 0) := Realizes.read e₄ 7 + have hwrite : Realizes .exact + (fun d : ((((Stack.Memory.Layout × ℕ) × ℕ) × ℕ) × ℕ) × ℕ => + Memory.write d.1.1.1.1.1.count.base d.1.1.2) + e₅ unitWords (fun _ _ _ => True) (fun _ _ => 0) := Realizes.write e₅ 0 6 + have hsome : Realizes .exact + (fun d : (((((Stack.Memory.Layout × ℕ) × ℕ) × ℕ) × ℕ) × ℕ) × Unit => + pure (some d.1.2)) e₆ optionWords + (fun w d _ => Fits w 1 ∧ Fits w d.1.2) (fun _ _ => 4) := returnSome e₆ 8 + have hpos := (Realizes.bind hsub (Realizes.bind haddr + (Realizes.bind hitem (Realizes.bind hwrite hsome)))).consequence + (safe' := fun w d s => 1 ≤ d.1.2 ∧ Fits w d.1.2 ∧ + Fits w (d.1.1.items.base + (d.1.2 - 1)) ∧ Fits w 1 ∧ + Fits w (s.mem (d.1.1.items.base + (d.1.2 - 1)))) + (work' := fun _ _ => 14) + (by + intro w d s _ _ ⟨hpos, hn, ha, h1, hv⟩ + change (1 ≤ d.1.2 ∧ Fits w d.1.2) ∧ + Fits w (d.1.1.items.base + (d.1.2 - 1)) ∧ True ∧ True ∧ + Fits w 1 ∧ Fits w (s.mem (d.1.1.items.base + (d.1.2 - 1))) + exact ⟨⟨hpos, hn⟩, ha, trivial, trivial, h1, hv⟩) + (fun _ _ => le_rfl) + have hbranch := Realizes.branch (.eq 4 5) (returnNone e₂) hpos + apply (Realizes.bind hread (Realizes.bind hzero hbranch)).consequence + · intro w L s hwords hw ⟨hL, hInv⟩ + have hn : Fits w (s.mem L.count.base) := hw.mem_fits _ + have h1 : Fits w 1 := hL.count_length ▸ hwords 1 + change True ∧ True ∧ (if s.mem L.count.base = 0 then True else + 1 ≤ s.mem L.count.base ∧ Fits w (s.mem L.count.base) ∧ + Fits w (L.items.base + (s.mem L.count.base - 1)) ∧ Fits w 1 ∧ + Fits w (s.mem (L.items.base + (s.mem L.count.base - 1)))) + refine ⟨trivial, trivial, ?_⟩ + split + · trivial + · have hle := hInv.count_le + exact ⟨by omega, hn, hw.fits_of_lt_frontier (hInv.item_lt (by omega)), h1, + hw.mem_fits _⟩ + · intro L s + change 0 + (1 + ((if test (.eq 4 5) (e₂ ((L, + (Memory.spec 0).clientResult s (Memory.read L.count.base)), 0)) then 4 else 14) + 2) + 1) + + 1 ≤ 19 + split <;> omega + +/-- The push's explicit local work is dominated by its actual three-query memory charge. -/ +theorem push_work_bound (L : Stack.Memory.Layout) (v : ℕ) (s : Memory.State) : + 10 ≤ 4 * memoryCost (Stack.Memory.push L v) s := by + change 10 ≤ 4 * 3 + decide + +/-- Even the empty pop has a memory query. This domination uses the actual method history, +after separately certifying all 19 units of local work, and is not a general query-only rule. -/ +theorem pop_work_bound (L : Stack.Memory.Layout) (s : Memory.State) : + 19 ≤ 19 * memoryCost (Stack.Memory.pop L) s := by + by_cases h : s.mem L.count.base = 0 + · simp [memoryCost, StateTransitionSpec.clientHistory, Stack.Memory.runClient_pop_of_eq_zero, + h, StateTransitionSpec.totalCost, Memory.charge] + · simp [memoryCost, StateTransitionSpec.clientHistory, Stack.Memory.runClient_pop_of_pos, + h, StateTransitionSpec.totalCost, Memory.charge] + +end Cslib.WordRAM.Realization.MemoryStack diff --git a/Complexity/Computability/WordRAM/Realization/GlueVector.lean b/Complexity/Computability/WordRAM/Realization/GlueVector.lean new file mode 100644 index 0000000000..6b52f24bf0 --- /dev/null +++ b/Complexity/Computability/WordRAM/Realization/GlueVector.lean @@ -0,0 +1,214 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Realization.Basic +public import Complexity.Computability.WordRAM.Glue + +/-! +# Certified finite-result word glue + +A finite family of scalar `GlueComputes` certificates realizes one faithfully encoded result. +The scalar witnesses are selected before every runtime input and word width. All coordinates +reuse a static auxiliary bank disjoint from the output slots, and each computed coordinate is +copied into its final slot. Both those copies and the empty-vector case are charged. + +`optionWords` supplies the shared two-word result convention needed by memory-stack and queue +clients: `none` is `(0,0)` and `some v` is `(1,v)`. The payload is not incremented, so the largest +fitting word remains representable. `boolWords` retains a zero slot for active-loop tests; a +true tag still requires the visible word-fit condition for `1`. + +## Main definitions + +* `optionWords`: a faithful tag/payload encoding of optional natural words. +* `boolWords`: a Boolean tag with a retained zero slot for native comparisons. + +## Main results + +* `Realizes.glueVector`: realize a finite result using certified scalar coordinate computations. + +This is a realization rule over the existing memory client and structured native code, not an +additional program syntax or execution judgment. +-/ + +@[expose] public section + +namespace Cslib.WordRAM.Realization + +open Cslib.DataStructure + +variable {B : Basis} {δ α : Type} {k l r : ℕ} + +/-- A presence tag and unshifted payload distinguish `none` from every `some v`. -/ +def optionWords : Option ℕ ↪ (Fin 2 → ℕ) where + toFun + | none => ![0, 0] + | some v => ![1, v] + inj' := by + intro a b h + cases a with + | none => + cases b with + | none => rfl + | some v => have hh := congrFun h 0; simp at hh + | some u => + cases b with + | none => have hh := congrFun h 0; simp at hh + | some v => exact congrArg some (congrFun h 1) + +@[simp] theorem optionWords_none : optionWords none = ![0, 0] := rfl +@[simp] theorem optionWords_some (v : ℕ) : optionWords (some v) = ![1, v] := rfl + +/-- A Boolean tag with a retained zero slot supports native comparisons without immediates. -/ +def boolWords : Bool ↪ (Fin 2 → ℕ) where + toFun b := ![b.toNat, 0] + inj' := by + intro a b h + have hh := congrFun h 0 + cases a <;> cases b <;> simp_all + +@[simp] theorem boolWords_false : boolWords false = ![0, 0] := rfl +@[simp] theorem boolWords_true : boolWords true = ![1, 0] := rfl + +private def coordinateSeq (code : Fin l → Code B r) (idle : Fin r) : List (Fin l) → Code B r + | [] => .op (.copy idle idle) + | i :: is => .seq (code i) (coordinateSeq code idle is) + +private theorem coordinateSeq_glue (code : Fin l → Code B r) (idle : Fin r) + (h : ∀ i, (code i).IsGlue) (is : List (Fin l)) : (coordinateSeq code idle is).IsGlue := by + induction is with + | nil => trivial + | cons i is ih => exact ⟨h i, ih⟩ + +namespace Realizes + +/-- Compute every output coordinate using actual certified scalar code. The common domain and +all coordinate envelopes remain visible, while code, exchange constant and scratch are uniform. +A final neutral copy pays for the empty result without materializing a non-fitting constant. -/ +theorem glueVector (input : δ ↪ (Fin k → ℕ)) (output : α ↪ (Fin l → ℕ)) + (f : δ → α) (g bound : Fin l → (Fin k → ℕ) → ℕ) (time : Fin l → ℕ) + {P : (Fin k → ℕ) → Prop} + (hresult : ∀ d i, output (f d) i = g i (input d)) + (hcoord : ∀ i, GlueComputes B P (g i) (bound i) (time i)) : + Realizes B (fun d => pure (f d)) input output + (fun w d _ => P (input d) ∧ ∀ i, Fits w (bound i (input d))) + (fun _ _ => (∑ i, time i) + l) := by + classical + choose n emit he using fun i => (hcoord i).2 + let N := ∑ i, n i + have hn (i : Fin l) : n i ≤ N := + Finset.single_le_sum (fun j _ => Nat.zero_le (n j)) (Finset.mem_univ i) + refine ⟨N, 1, fun _ src tmp => + coordinateSeq (fun i => .seq + (emit i src (fun j => tmp ⟨l + j.val, by have := hn i; omega⟩)) + (.op (.copy (tmp (Fin.castAdd (N + 1) i)) (tmp ⟨l, by omega⟩)))) + (tmp ⟨l, by omega⟩) (List.finRange l), ?_⟩ + intro r frontier src tmp hi hd hf hs + let dst : Fin l → Fin r := fun i => tmp (Fin.castAdd (N + 1) i) + let aux : ∀ i : Fin l, Fin (n i + 1) → Fin r := + fun i j => tmp ⟨l + j.val, by have := hn i; omega⟩ + let scratch : Fin (N + 1) → Fin r := fun j => tmp ⟨l + j.val, by omega⟩ + let coord : Fin l → Code B r := fun i => + .seq (emit i src (aux i)) (.op (.copy (dst i) (scratch 0))) + have hdsti : Function.Injective dst := by + intro i j hij + apply Fin.ext + exact congrArg (fun a : Fin (l + N + 1) => a.val) (hi hij) + have hauxi (i : Fin l) : Function.Injective (aux i) := by + intro a b hab + apply Fin.ext + have h := congrArg (fun a : Fin (l + N + 1) => a.val) (hi hab) + dsimp only [aux] at h + omega + have had (i : Fin l) (j : Fin k) (a : Fin (n i + 1)) : src j ≠ aux i a := hd _ _ + have hauxDst (i : Fin l) (j : Fin l) : ∀ a, aux i a ≠ dst j := by + intro a h + have hh := congrArg (fun a : Fin (l + N + 1) => a.val) (hi h) + dsimp only [aux, dst] at hh + simp only [Fin.val_castAdd] at hh + omega + have hcglue (i : Fin l) : (coord i).IsGlue := + ⟨(he i src (aux i) (hauxi i) (had i)).1, trivial⟩ + refine ⟨(coordinateSeq_glue coord (scratch 0) hcglue _).not_readsWidth, ?_⟩ + intro w d x hx hin hsafe hv + have hruni (i : Fin l) (a : Data r) (ha : a.regs ∘ src = input d) : + ∃ b, ExecWithin w (coord i) a b (time i + 1) ∧ b.mem = a.mem ∧ + b.regs ∘ src = input d ∧ b.regs (dst i) = output (f d) i ∧ + ∀ j, (∀ z, scratch z ≠ j) → dst i ≠ j → b.regs j = a.regs j := by + obtain ⟨b, hb, hbo, hbm, hbf⟩ := + (he i src (aux i) (hauxi i) (had i)).2 w a (ha.symm ▸ hsafe.1) + (ha.symm ▸ hsafe.2 i) + let c := b.set (dst i) (g i (input d)) + have ho : b.regs (scratch 0) = g i (input d) := by + have hz : aux i 0 = scratch 0 := by + apply congrArg tmp + apply Fin.ext + rfl + rw [← hz] + simpa only [ha] using hbo + have hc : ExecWithin (B := B) w (.op (.copy (dst i) (scratch 0))) b c 1 := by + simpa only [ho] using + (ExecWithin.copy (B := B) (w := w) (d := dst i) (s := scratch 0) (x := b)) + refine ⟨c, hb.seq hc, hbm, ?_, ?_, ?_⟩ + · funext j + change c.regs (src j) = input d j + rw [show c.regs (src j) = b.regs (src j) from Function.update_of_ne (hd _ _) _ _, + hbf _ (fun a => (had i j a).symm)] + exact congrFun ha j + · change (b.set (dst i) _).regs (dst i) = _ + simp [Data.set, hresult] + · intro j hsj hdj + rw [show c.regs j = b.regs j from Function.update_of_ne hdj.symm _ _, + hbf j (fun z => ?_)] + exact hsj ⟨z.val, by have := hn i; omega⟩ + have hseq (is : List (Fin l)) (hnodup : is.Nodup) (a : Data r) + (ha : a.regs ∘ src = input d) : + ∃ b, ExecWithin w (coordinateSeq coord (scratch 0) is) a b + ((is.map fun i => time i + 1).sum + 1) ∧ + b.mem = a.mem ∧ b.regs ∘ src = input d ∧ + (∀ i ∈ is, b.regs (dst i) = output (f d) i) ∧ + ∀ j, (∀ z, scratch z ≠ j) → (∀ i ∈ is, dst i ≠ j) → b.regs j = a.regs j := by + induction is generalizing a with + | nil => + have hself : ExecWithin (B := B) w (.op (.copy (scratch 0) (scratch 0))) a a 1 := by + convert (ExecWithin.copy (B := B) (w := w) (d := scratch 0) (s := scratch 0) + (x := a)) using 1 + apply Data.ext <;> simp [Data.set] + exact ⟨a, hself, rfl, ha, by simp, fun _ _ _ => rfl⟩ + | cons i is ih => + obtain ⟨b, hb, hbm, hbs, hbo, hbf⟩ := hruni i a ha + obtain ⟨c, hc, hcm, hcs, hco, hcf⟩ := ih hnodup.of_cons b hbs + refine ⟨c, (hb.seq hc).mono (by simp; omega), hcm.trans hbm, hcs, ?_, ?_⟩ + · intro j hj + rcases List.mem_cons.mp hj with rfl | hj + · rw [hcf _ (fun z => ?_) (fun j hj h => ?_)] + · exact hbo + · intro hh + have hval := congrArg (fun a : Fin (l + N + 1) => a.val) (hi hh) + dsimp only [scratch, dst] at hval + simp only [Fin.val_castAdd] at hval + omega + · exact (List.nodup_cons.mp hnodup).1 (hdsti h ▸ hj) + · exact hco j hj + · intro j hsj hdj + rw [hcf j hsj (fun i hi => hdj i (by simp [hi]))] + exact hbf j hsj (hdj i (by simp)) + obtain ⟨y, hy, hym, _, hyo, hyf⟩ := hseq (List.finRange l) (List.nodup_finRange l) x hin + refine ⟨y, hy.mono ?_, ?_, fun i => hyo i (by simp), ?_⟩ + · change ((List.finRange l).map fun i => time i + 1).sum + 1 ≤ + 1 * (0 + ((∑ i, time i) + l) + 1) + rw [← List.sum_toFinset (fun i => time i + 1) (List.nodup_finRange l)] + simp [Finset.sum_add_distrib] + · apply Memory.State.ext + · exact hym + · exact hyf frontier (fun _ => hf _) (fun _ _ => hf _) + · intro i hif htmp + exact hyf i (fun _ => htmp _) (fun _ _ => htmp _) + +end Realizes +end Cslib.WordRAM.Realization diff --git a/ComplexityTests/WordRAMMemoryStack.lean b/ComplexityTests/WordRAMMemoryStack.lean new file mode 100644 index 0000000000..b65055e4c1 --- /dev/null +++ b/ComplexityTests/WordRAMMemoryStack.lean @@ -0,0 +1,172 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.WordRAM.Realization.Client.MemoryStack + +/-! Concrete native instances of the unchanged stack methods. The pop uses the largest four-bit +payload, so a successor-based option encoding could not satisfy this instance. Both methods retain +the literal inaccessible memory. These instruction bounds include certified local work. -/ + +namespace ComplexityTests.WordRAMMemoryStack + +open Cslib.DataStructure Cslib.WordRAM Cslib.WordRAM.Realization +open MemoryStack + +private def front {k t : ℕ} : Fin (1 + (k + t)) := ⟨0, by omega⟩ +private def src {k t : ℕ} (i : Fin k) : Fin (1 + (k + t)) := + Fin.natAdd 1 (Fin.castAdd t i) +private def tmp {k t : ℕ} (i : Fin t) : Fin (1 + (k + t)) := Fin.natAdd 1 (Fin.natAdd k i) + +private theorem layout (k t : ℕ) : + Function.Injective (tmp (k := k) (t := t)) ∧ + (∀ i j, src (t := t) i ≠ tmp (k := k) j) ∧ + (∀ j, tmp (k := k) (t := t) j ≠ front) ∧ + (∀ i, src (k := k) (t := t) i ≠ front) := by + refine ⟨?_, ?_, ?_, ?_⟩ + · intro i j h + apply Fin.ext + have := congrArg Fin.val h + simp only [tmp, Fin.val_natAdd] at this + omega + · intro i j h + have := congrArg Fin.val h + simp only [src, tmp, Fin.val_natAdd, Fin.val_castAdd] at this + have := i.isLt + omega + · intro i h + have := congrArg Fin.val h + simp only [tmp, front, Fin.val_natAdd] at this + omega + · intro i h + have := congrArg Fin.val h + simp only [src, front, Fin.val_natAdd, Fin.val_castAdd] at this + omega + +private def packed {k t : ℕ} (v : Fin k → ℕ) (s : Memory.State) : Data (1 + (k + t)) := + ⟨Fin.addCases (fun _ => s.frontier) (Fin.addCases v (fun _ => 0)), s.mem⟩ + +private theorem packed_bounded {k t w : ℕ} {v : Fin k → ℕ} {s : Memory.State} + (hv : ∀ i, Fits w (v i)) (hs : Memory.WellFormed w s) : + (packed (t := t) v s).Bounded w := by + refine ⟨fun i => ?_, hs.mem_fits⟩ + refine Fin.addCases (fun _ => by simpa [packed] using hs.frontier_fits) (fun j => ?_) i + exact Fin.addCases (fun i => by simpa [packed] using hv i) + (fun _ => by simp [packed]) j + +private theorem packed_input {k t : ℕ} (v : Fin k → ℕ) (s : Memory.State) : + (packed (t := t) v s).regs ∘ src = v := by + funext i + simp [packed, src] + +private theorem packed_state {k t : ℕ} (v : Fin k → ℕ) (s : Memory.State) : + memoryState front (packed (t := t) v s) = s := by + cases s + rfl + +private def L : Stack.Memory.Layout := ⟨⟨2, 1⟩, ⟨3, 2⟩⟩ +private def empty : Memory.State := ⟨fun a => if a = 2 then 0 else 7, 8⟩ +private def singleton : Memory.State := + ⟨fun a => if a = 2 then 1 else if a = 3 then 15 else 7, 8⟩ + +private theorem valid_layout : L.Valid 4 2 := by + refine ⟨rfl, ?_, rfl, by decide⟩ + change ¬(3 ≤ 2 ∧ 2 < 3 + 2) + omega + +private theorem empty_inv : Stack.Memory.Inv L empty := by + constructor + · change 2 + 1 ≤ 8; decide + · change 3 + 2 ≤ 8; decide + · change 0 ≤ 2; decide + +private theorem singleton_inv : Stack.Memory.Inv L singleton := by + constructor + · change 2 + 1 ≤ 8; decide + · change 3 + 2 ≤ 8; decide + · change 1 ≤ 2; decide + +private theorem empty_wf : Memory.WellFormed 4 empty := by + refine ⟨by decide, fun a => ?_⟩ + change Fits 4 (if a = 2 then 0 else 7) + split <;> decide + +private theorem singleton_wf : Memory.WellFormed 4 singleton := by + refine ⟨by decide, fun a => ?_⟩ + change Fits 4 (if a = 2 then 1 else if a = 3 then 15 else 7) + split + · decide + · split <;> decide + +-- A maximum-word push executes the generic certificate on a complete runtime layout. +example : ∃ r C, ∃ (code : Code .exact r) (x y : Data r), + ExecWithin 4 code x y (C * 14) ∧ y.mem 3 = 15 ∧ y.mem 10 = 7 := by + obtain ⟨n, C, emit, he⟩ := realizes_push + let x := packed (t := 0 + n + 1) (pushWords (L, 15)) empty + obtain ⟨hi, hd, hf, hs⟩ := layout 5 (0 + n + 1) + have hx : x.Bounded 4 := packed_bounded (by intro i; fin_cases i <;> decide) empty_wf + have hin := packed_input (t := 0 + n + 1) (pushWords (L, 15)) empty + have hm := packed_state (t := 0 + n + 1) (pushWords (L, 15)) empty + have hsafe : L.Valid 4 L.items.length ∧ Stack.Memory.Inv L (memoryState front x) ∧ + (memoryState front x).mem L.count.base < L.items.length := by + rw [hm] + exact ⟨valid_layout, empty_inv, by decide⟩ + have hv : (Memory.spec 4).ValidHistory (memoryState front x) + ((Memory.spec 4).clientHistory (memoryState front x) (Stack.Memory.push L 15)) := by + rw [hm] + decide + obtain ⟨y, hy, hmem, _, _⟩ := (he front src tmp hi hd hf hs).2 4 (L, 15) x hx hin hsafe hv + rw [hm] at hy hmem + refine ⟨_, C, emit front src tmp, x, y, hy, ?_, ?_⟩ + · exact congrArg (fun s : Memory.State => s.mem 3) hmem + · exact congrArg (fun s : Memory.State => s.mem 10) hmem + +-- One native pop body handles both actual branches. The positive branch returns the maximum +-- word as (tag=1,payload=15), while the empty branch returns (0,0). +example : ∃ r C, ∃ (code : Code .exact r) (x y x₀ y₀ : Data r) (tag payload : Fin r), + ExecWithin 4 code x y (C * 23) ∧ y.regs tag = 1 ∧ y.regs payload = 15 ∧ + y.mem 2 = 0 ∧ y.mem 10 = 7 ∧ ExecWithin 4 code x₀ y₀ (C * 21) ∧ + y₀.regs tag = 0 ∧ y₀.regs payload = 0 ∧ y₀.mem 10 = 7 := by + obtain ⟨n, C, emit, he⟩ := realizes_pop + let x := packed (t := 2 + n + 1) (layoutWords L) singleton + obtain ⟨hi, hd, hf, hs⟩ := layout 4 (2 + n + 1) + have hx : x.Bounded 4 := packed_bounded (by intro i; fin_cases i <;> decide) singleton_wf + have hin := packed_input (t := 2 + n + 1) (layoutWords L) singleton + have hm := packed_state (t := 2 + n + 1) (layoutWords L) singleton + have hsafe : L.Valid 4 L.items.length ∧ Stack.Memory.Inv L (memoryState front x) := by + rw [hm] + exact ⟨valid_layout, singleton_inv⟩ + have hv : (Memory.spec 4).ValidHistory (memoryState front x) + ((Memory.spec 4).clientHistory (memoryState front x) (Stack.Memory.pop L)) := by + rw [hm] + decide + obtain ⟨y, hy, hmem, hout, _⟩ := (he front src tmp hi hd hf hs).2 4 L x hx hin hsafe hv + rw [hm] at hy hmem hout + let x₀ := packed (t := 2 + n + 1) (layoutWords L) empty + have hx₀ : x₀.Bounded 4 := packed_bounded (by intro i; fin_cases i <;> decide) empty_wf + have hin₀ := packed_input (t := 2 + n + 1) (layoutWords L) empty + have hm₀ := packed_state (t := 2 + n + 1) (layoutWords L) empty + have hsafe₀ : L.Valid 4 L.items.length ∧ Stack.Memory.Inv L (memoryState front x₀) := by + rw [hm₀] + exact ⟨valid_layout, empty_inv⟩ + have hv₀ : (Memory.spec 4).ValidHistory (memoryState front x₀) + ((Memory.spec 4).clientHistory (memoryState front x₀) (Stack.Memory.pop L)) := by + rw [hm₀] + decide + obtain ⟨y₀, hy₀, hmem₀, hout₀, _⟩ := + (he front src tmp hi hd hf hs).2 4 L x₀ hx₀ hin₀ hsafe₀ hv₀ + rw [hm₀] at hy₀ hmem₀ hout₀ + refine ⟨_, C, emit front src tmp, x, y, x₀, y₀, tmp (Fin.castAdd (n + 1) 0), + tmp (Fin.castAdd (n + 1) 1), hy, hout 0, hout 1, ?_, ?_, hy₀, hout₀ 0, hout₀ 1, ?_⟩ + · exact congrArg (fun s : Memory.State => s.mem 2) hmem + · exact congrArg (fun s : Memory.State => s.mem 10) hmem + · exact congrArg (fun s : Memory.State => s.mem 10) hmem₀ + +-- The empty path is semantically none and uses a valid, non-vacuous layout and memory state. +example : (Memory.spec 4).clientResult empty (Stack.Memory.pop L) = none := by decide + +example : optionWords (some 15) = ![1, 15] ∧ Fits 4 15 ∧ ¬Fits 4 (15 + 1) := by decide + +end ComplexityTests.WordRAMMemoryStack diff --git a/ComplexityTests/WordRAMRealizationGlueVector.lean b/ComplexityTests/WordRAMRealizationGlueVector.lean new file mode 100644 index 0000000000..2ac5f49c02 --- /dev/null +++ b/ComplexityTests/WordRAMRealizationGlueVector.lean @@ -0,0 +1,154 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: local-only (regression clients for finite-result glue realization) + +public import Complexity.Computability.WordRAM.Realization.GlueVector + +/-! +# Optional-word vector glue regressions + +Every coordinate is computed by an actual scalar certificate. `none` uses two zero constants +and remains valid at word width zero; `some v` uses a presence constant and an unshifted word +projection, including the maximal word. Empty results retain the charged neutral-copy case. +-/ + +public section + +namespace Cslib.WordRAM.Realization.Tests + +open Cslib.DataStructure + +private def unitWords : Unit ↪ (Fin 0 → ℕ) := + (Equiv.ofUnique Unit (Fin 0 → ℕ)).toEmbedding + +private def wordInput : ℕ ↪ (Fin 1 → ℕ) := + (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + +private theorem none_realizes : + Realizes .exact (fun _ : Unit => pure (none : Option ℕ)) unitWords optionWords + (fun w _ _ => True ∧ ∀ _ : Fin 2, Fits w 0) (fun _ _ => 4) := by + have hcoord (i : Fin 2) : GlueComputes .exact (fun _ : Fin 0 → ℕ => True) + (fun _ => 0) (fun _ => 0) 1 := GlueComputes.const 0 (fun _ _ => le_rfl) + have hresult (d : Unit) (i : Fin 2) : optionWords none i = 0 := by + fin_cases i <;> rfl + simpa using Realizes.glueVector unitWords optionWords (fun _ => none) + (fun _ _ => 0) (fun _ _ => 0) (fun _ => 1) hresult hcoord + +private def someCoordinate : Fin 2 → (Fin 1 → ℕ) → ℕ := ![fun _ => 1, fun v => v 0] + +private theorem some_realizes : + Realizes .exact (fun v : ℕ => pure (some v)) wordInput optionWords + (fun w v _ => True ∧ Fits w 1 ∧ Fits w v) (fun _ _ => 4) := by + have hcoord (i : Fin 2) : GlueComputes .exact (fun _ : Fin 1 → ℕ => True) + (someCoordinate i) (someCoordinate i) 1 := by + fin_cases i + · exact GlueComputes.const 1 (fun _ _ => le_rfl) + · exact GlueComputes.proj 0 (fun _ _ => le_rfl) + have hresult (v : ℕ) (i : Fin 2) : optionWords (some v) i = + someCoordinate i (wordInput v) := by + fin_cases i <;> rfl + simpa [someCoordinate, wordInput, Fin.forall_fin_succ] using + Realizes.glueVector wordInput optionWords some someCoordinate someCoordinate + (fun _ => 1) hresult hcoord + +private theorem unit_realizes : + Realizes .exact (fun _ : Unit => pure ()) unitWords unitWords + (fun _ _ _ => True ∧ True) (fun _ _ => 0) := by + have hcoord (i : Fin 0) : GlueComputes .exact (fun _ : Fin 0 → ℕ => True) + (Fin.elim0 i) (Fin.elim0 i) (Fin.elim0 i) := Fin.elim0 i + have hresult (d : Unit) (i : Fin 0) : + unitWords () i = (Fin.elim0 i : (Fin 0 → ℕ) → ℕ) (unitWords d) := + Fin.elim0 i + simpa using Realizes.glueVector unitWords unitWords (fun _ => ()) + Fin.elim0 Fin.elim0 Fin.elim0 hresult hcoord + +/-- The optional payload is not shifted, so the maximal two-bit word is retained. -/ +theorem optionWords_some_max : optionWords (some 3) = ![1, 3] := rfl + +example : optionWords none ≠ optionWords (some 0) := by decide +example : ¬Fits 0 (optionWords (some 0) 0) := by decide +example : Fits 2 (optionWords (some 3) 0) ∧ Fits 2 (optionWords (some 3) 1) := by decide + +/-- The certified two-coordinate `none` emitter actually executes at width zero. -/ +example : ∃ r, ∃ p : Program .exact r, ∃ steps, + Executes 0 p ⟨0, ⟨fun _ => 0, fun _ => 0⟩⟩ 0 steps := by + obtain ⟨n, C, emit, he⟩ := none_realizes + let r := (2 + n + 1) + 1 + let frontier : Fin r := ⟨r - 1, by omega⟩ + let src : Fin 0 → Fin r := Fin.elim0 + let tmp : Fin (2 + n + 1) → Fin r := fun i => ⟨i.val, by omega⟩ + have hi : Function.Injective tmp := by + intro i j h + apply Fin.ext + exact congrArg (fun i : Fin r => i.val) h + have hf (j : Fin (2 + n + 1)) : tmp j ≠ frontier := by + intro h + have hh := congrArg (fun i : Fin r => i.val) h + dsimp only [tmp, frontier, r] at hh + omega + let x : Data r := ⟨fun _ => 0, fun _ => 0⟩ + have hx : x.Bounded 0 := ⟨fun _ => fits_zero 0, fun _ => fits_zero 0⟩ + have hin : x.regs ∘ src = unitWords () := Subsingleton.elim _ _ + obtain ⟨y, ⟨steps, _, hy⟩, _, hout, _⟩ := + (he frontier src tmp hi (fun i => Fin.elim0 i) hf (fun i => Fin.elim0 i)).2 + 0 () x hx hin ⟨trivial, fun _ => fits_zero 0⟩ trivial + have ho : y.regs (tmp 0) = 0 := hout 0 + refine ⟨r, (emit frontier src tmp).program (tmp 0), steps + 1, ?_⟩ + simpa only [ho] using hy.executes (tmp 0) + +example : boolWords false = ![0, 0] := rfl +example : Fits 0 (boolWords false 0) ∧ Fits 0 (boolWords false 1) := by decide +example : ¬Fits 0 (boolWords true 0) := by decide + +/-- The certified payload emitter returns the maximal two-bit word without incrementing it. -/ +example : ∃ r, ∃ p : Program .exact r, ∃ x : Data r, ∃ steps, + x.Bounded 2 ∧ Executes 2 p ⟨0, x⟩ 3 steps := by + obtain ⟨n, C, emit, he⟩ := some_realizes + let r := 1 + (2 + n + 1) + 1 + let frontier : Fin r := ⟨r - 1, by omega⟩ + let src : Fin 1 → Fin r := fun i => ⟨i.val, by omega⟩ + let tmp : Fin (2 + n + 1) → Fin r := fun i => ⟨1 + i.val, by omega⟩ + have hi : Function.Injective tmp := by + intro i j h + apply Fin.ext + have hh := congrArg (fun i : Fin r => i.val) h + dsimp only [tmp] at hh + omega + have hd (i : Fin 1) (j : Fin (2 + n + 1)) : src i ≠ tmp j := by + intro h + have hh := congrArg (fun i : Fin r => i.val) h + dsimp only [src, tmp] at hh + omega + have hf (j : Fin (2 + n + 1)) : tmp j ≠ frontier := by + intro h + have hh := congrArg (fun i : Fin r => i.val) h + dsimp only [tmp, frontier, r] at hh + omega + have hs (i : Fin 1) : src i ≠ frontier := by + intro h + have hh := congrArg (fun i : Fin r => i.val) h + dsimp only [src, frontier, r] at hh + omega + let x : Data r := ⟨fun i => if i = src 0 then 3 else 0, fun _ => 0⟩ + have hx : x.Bounded 2 := by + refine ⟨fun i => ?_, fun _ => fits_zero 2⟩ + change Fits 2 (if i = src 0 then 3 else 0) + split_ifs <;> decide + have hin : x.regs ∘ src = wordInput 3 := by + funext i + have hi : i = 0 := Fin.eq_zero i + subst i + simp [x, wordInput] + obtain ⟨y, ⟨steps, _, hy⟩, _, hout, _⟩ := + (he frontier src tmp hi hd hf hs).2 2 3 x hx hin ⟨trivial, by decide, by decide⟩ trivial + let out := tmp (Fin.castAdd (n + 1) (1 : Fin 2)) + have ho : y.regs out = 3 := hout 1 + refine ⟨r, (emit frontier src tmp).program out, x, steps + 1, hx, ?_⟩ + simpa only [ho] using hy.executes out + +end Cslib.WordRAM.Realization.Tests diff --git a/ROADMAP.md b/ROADMAP.md index 6037a5e4aa..0db31f5c09 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -640,7 +640,9 @@ reusing graph and matroid foundations and the shared data-structure interfaces. with explicit input layout, fits and safe-domain hypotheses. A separate operation-level memory implementation exposes body-only costs under delivered arguments. Existing framed Word-RAM certificates can be derived from semantic modules after discharging their local - initialization, footprint and ABI conditions; a one-cell ADT checks that boundary. Remaining: + initialization, footprint and ABI conditions; a one-cell ADT checks that boundary. Multiword + glue preserves full-word Option payloads, and the actual memory-stack size/push/pop methods + have uniform native certificates over runtime layouts, with local work 0/10/19. Remaining: the representative framed queue and its faithful whole-memory allocation/native-client path. The full C2/M1b objective remains open; arbitrary host continuations do not acquire runtime bounds. - **Stateful algorithms and data structures:** Word-RAM controllers for larger adaptive and diff --git a/UPSTREAM.md b/UPSTREAM.md index 9e35c7b57a..c7761ca654 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -523,6 +523,10 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he initialization/locality bundle and semantic module; its argument map instantiates one global emitter rather than selecting code after runtime layouts. Combined cost takes the program and an explicit work grading, with a separately proved envelope before dropping local work. + Certified coordinate-wise glue stages multiword results with every copy charged; Option + values use a tag and unshifted payload, including maximal fitting words. The actual + `Stack.Memory` size/push/pop clients reuse those rules over faithfully encoded runtime layouts, + with visible layout/representation/spare-capacity conditions and local work 0/10/19. The full framed queue/faithful allocation client remains separate. No abstract headline is added. `DataStructure/OrderedSet` and `DataStructure/OrderedSet/*` provide neutral optional-minimum contracts, a typed finite-set interface including deletion, and adaptive From a929a76f23194a8e94428973bca2a317f9f23755 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 20:59:29 -0500 Subject: [PATCH 18/27] refactor(WordRAM): centralize realization history and word encodings Address outside review5731255619: move the existing public width/history lemmas into Basic and use them from Bind/Loop; remove the private duplicate. Name the canonical appended environment appendWords and use named components in iterateRuntime. Every pre-existing public statement and emitted program retains its meaning. Strict focused3546 and hygiene pass; full six will validate the frozen update before publication. Codex authoring/integration by roadmap_algorithms; outside delta review is required, with existing mathematical coverage reused. No selected abstract impact. --- .../WordRAM/Realization/Basic.lean | 27 ++++++++++++ .../WordRAM/Realization/Bind.lean | 25 +++-------- .../WordRAM/Realization/Loop.lean | 43 ++++++------------- 3 files changed, 44 insertions(+), 51 deletions(-) diff --git a/Complexity/Computability/WordRAM/Realization/Basic.lean b/Complexity/Computability/WordRAM/Realization/Basic.lean index 07f59ad67b..59be6c2b0d 100644 --- a/Complexity/Computability/WordRAM/Realization/Basic.lean +++ b/Complexity/Computability/WordRAM/Realization/Basic.lean @@ -70,6 +70,33 @@ theorem runClient_width (w : ℕ) (p : Memory.Prog α) (s : Memory.State) : exact congrArg (fun t : α × Memory.State × List Memory.Op => (t.1, t.2.1, o :: t.2.2)) (ih (Memory.step s o).2 (Memory.step s o).1) +/-- The result of the memory interpreter is independent of the admissibility width. -/ +theorem clientResult_width (w : ℕ) (p : Memory.Prog α) (s : Memory.State) : + (Memory.spec w).clientResult s p = (Memory.spec 0).clientResult s p := + congrArg Prod.fst (runClient_width w p s) + +/-- The final memory/frontier state is independent of the admissibility width. -/ +theorem clientState_width (w : ℕ) (p : Memory.Prog α) (s : Memory.State) : + (Memory.spec w).clientState s p = (Memory.spec 0).clientState s p := + congrArg (fun t => t.2.1) (runClient_width w p s) + +/-- Admissible sequential histories split at the actual returned value and memory state. -/ +theorem validHistory_bind (w : ℕ) {α β : Type} (p : Memory.Prog α) + (q : α → Memory.Prog β) (s : Memory.State) : + (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s (p.bind q)) ↔ + (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s p) ∧ + (Memory.spec w).ValidHistory ((Memory.spec w).clientState s p) + ((Memory.spec w).clientHistory ((Memory.spec w).clientState s p) + (q ((Memory.spec w).clientResult s p))) := by + simp only [StateTransitionSpec.clientHistory, StateTransitionSpec.runClient_bind] + rw [StateTransitionSpec.validHistory_append, StateTransitionSpec.run_runClient] + +/-- Faithfully append a returned word vector to the original input environment. +This is static register wiring; it performs no native computation or copies. -/ +def appendWords (input : δ ↪ (Fin k → ℕ)) (output : α ↪ (Fin l → ℕ)) : + δ × α ↪ (Fin (k + l) → ℕ) := + (input.prodMap output).trans (Fin.appendEquiv k l).toEmbedding + /-- A semantic return issues no memory operation. Its native glue is charged separately. -/ @[simp] theorem memoryCost_pure (a : α) (s : Memory.State) : memoryCost (pure a) s = 0 := rfl diff --git a/Complexity/Computability/WordRAM/Realization/Bind.lean b/Complexity/Computability/WordRAM/Realization/Bind.lean index 51d75154ea..9858fb81ce 100644 --- a/Complexity/Computability/WordRAM/Realization/Bind.lean +++ b/Complexity/Computability/WordRAM/Realization/Bind.lean @@ -35,21 +35,6 @@ private def bindLeft {m n₂ l n₁ : ℕ} (i : Fin (m + n₂ + 1)) : private def bindRight {m n₂ l n₁ : ℕ} (i : Fin (l + n₁ + 1)) : Fin (m + (n₂ + l + n₁ + 1) + 1) := ⟨m + n₂ + 1 + i.val, by omega⟩ -private theorem validHistory_bind {α β : Type} (w : ℕ) (s : Memory.State) - (p : Memory.Prog α) (q : α → Memory.Prog β) : - (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s (p.bind q)) ↔ - (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s p) ∧ - (Memory.spec w).ValidHistory ((Memory.spec w).clientState s p) - ((Memory.spec w).clientHistory ((Memory.spec w).clientState s p) - (q ((Memory.spec w).clientResult s p))) := by - have hh : (Memory.spec w).clientHistory s (p.bind q) = - (Memory.spec w).clientHistory s p ++ - (Memory.spec w).clientHistory ((Memory.spec w).clientState s p) - (q ((Memory.spec w).clientResult s p)) := by - simp only [StateTransitionSpec.clientHistory, StateTransitionSpec.clientState, - StateTransitionSpec.clientResult, StateTransitionSpec.runClient_bind] - rw [hh, StateTransitionSpec.validHistory_append, StateTransitionSpec.run_runClient] - namespace Realizes variable {B : Basis} {δ α β : Type} {k l m : ℕ} @@ -63,7 +48,7 @@ variable {B : Basis} {δ α β : Type} {k l m : ℕ} answer. Its domain and work charge are evaluated at the deterministic intermediate state. All emitted code, register banks and the exchange constant precede runtime answers. -/ theorem bind (hP : Realizes B p input answer safeP workP) - (hQ : Realizes B q ((input.prodMap answer).trans (Fin.appendEquiv k l).toEmbedding) + (hQ : Realizes B q (appendWords input answer) output safeQ workQ) : Realizes B (fun d => (p d).bind (fun a => q (d, a))) input output (fun w d s => safeP w d s ∧ @@ -124,10 +109,10 @@ theorem bind (hP : Realizes B p input answer safeP workP) let a := (Memory.spec 0).clientResult s (p d) let s' := (Memory.spec 0).clientState s (p d) have hresult : (Memory.spec w).clientResult s (p d) = a := - congrArg Prod.fst (runClient_width w (p d) s) + clientResult_width w (p d) s have hstate : (Memory.spec w).clientState s (p d) = s' := - congrArg (fun z => z.2.1) (runClient_width w (p d) s) - obtain ⟨hvP, hvQ⟩ := (validHistory_bind w s (p d) (fun a => q (d, a))).mp hv + clientState_width w (p d) s + obtain ⟨hvP, hvQ⟩ := (validHistory_bind w (p d) (fun a => q (d, a)) s).mp hv obtain ⟨z, hz, hmP, houtP, hframeP⟩ := hrunP w d x hx hin hsafe.1 hvP change memoryState frontier z = (Memory.spec w).clientState s (p d) at hmP rw [hstate] at hmP @@ -135,7 +120,7 @@ theorem bind (hP : Realizes B p input answer safeP workP) answer ((Memory.spec w).clientResult s (p d)) i at houtP rw [hresult] at houtP have hinQ : z.regs ∘ nextSrc = - ((input.prodMap answer).trans (Fin.appendEquiv k l).toEmbedding) (d, a) := by + (appendWords input answer) (d, a) := by funext i refine Fin.addCases (fun j => ?_) (fun j => ?_) i · change z.regs (nextSrc (Fin.castAdd l j)) = diff --git a/Complexity/Computability/WordRAM/Realization/Loop.lean b/Complexity/Computability/WordRAM/Realization/Loop.lean index 9081953bdc..893fbe6260 100644 --- a/Complexity/Computability/WordRAM/Realization/Loop.lean +++ b/Complexity/Computability/WordRAM/Realization/Loop.lean @@ -110,27 +110,6 @@ theorem copyBank_exec (src dst : Fin k → Fin r) (idle : Fin r) rw [hframe j (fun i => hj i.succ)] exact Function.update_of_ne (hj 0).symm _ _ -/-- The result of the memory interpreter is independent of the admissibility width. -/ -theorem clientResult_width (w : ℕ) (p : Memory.Prog σ) (s : Memory.State) : - (Memory.spec w).clientResult s p = (Memory.spec 0).clientResult s p := - congrArg Prod.fst (runClient_width w p s) - -/-- The final memory/frontier state is independent of the admissibility width. -/ -theorem clientState_width (w : ℕ) (p : Memory.Prog σ) (s : Memory.State) : - (Memory.spec w).clientState s p = (Memory.spec 0).clientState s p := - congrArg (fun t => t.2.1) (runClient_width w p s) - -/-- Admissible sequential histories split at the actual returned value and memory state. -/ -theorem validHistory_bind (w : ℕ) {α β : Type} (p : Memory.Prog α) - (q : α → Memory.Prog β) (s : Memory.State) : - (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s (p.bind q)) ↔ - (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s p) ∧ - (Memory.spec w).ValidHistory ((Memory.spec w).clientState s p) - ((Memory.spec w).clientHistory ((Memory.spec w).clientState s p) - (q ((Memory.spec w).clientResult s p))) := by - simp only [StateTransitionSpec.clientHistory, StateTransitionSpec.runClient_bind] - rw [StateTransitionSpec.validHistory_append, StateTransitionSpec.run_runClient] - /-- Memory cost and certified local work decompose at each actual iteration. -/ theorem iteration_charge_succ (body : δ × σ → Memory.Prog σ) (work : (δ × σ) → Memory.State → ℕ) (d : δ) (n : ℕ) (a : σ) (s : Memory.State) : @@ -255,13 +234,13 @@ theorem iterateRuntime (input : δ ↪ (Fin k → ℕ)) (output : σ ↪ (Fin l {safe : ℕ → (δ × σ) → Memory.State → Prop} {work : (δ × σ) → Memory.State → ℕ} (h : Realizes .exact body - ((input.prodMap output).trans (Fin.appendEquiv k l).toEmbedding) output safe work) : - Realizes .exact (fun d : (δ × σ) × ℕ => iterate body d.1.1 d.2 d.1.2) - ((((input.prodMap output).trans (Fin.appendEquiv k l).toEmbedding).prodMap + (appendWords input output) output safe work) : + Realizes .exact (fun ((d, a), fuel) => iterate body d fuel a) + (((appendWords input output).prodMap (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding).trans (Fin.appendEquiv (k + l) 1).toEmbedding) output - (fun w d s => iterationSafe body safe w d.1.1 d.2 d.1.2 s) - (fun d s => iterationWork body work d.1.1 d.2 d.1.2 s) := by + (fun w ((d, a), fuel) s => iterationSafe body safe w d fuel a s) + (fun ((d, a), fuel) s => iterationWork body work d fuel a s) := by obtain ⟨n, C, emit, he⟩ := h refine ⟨l + n + 3, C + 2 * l + 8, loopEmit emit, ?_⟩ intro r frontier src tmp hi hd hf hs @@ -338,7 +317,8 @@ theorem iterateRuntime (input : δ ↪ (Fin k → ℕ)) (output : σ ↪ (Fin l let s₀ := memoryState frontier x have henv (i : Fin k) : x.regs (env i) = input d₀ i := by have hh : x.regs (src (Fin.castAdd 1 (Fin.castAdd l i))) = input d₀ i := by - simpa [d₀, Fin.appendEquiv] using congrFun hin (Fin.castAdd 1 (Fin.castAdd l i)) + simpa [d₀, appendWords, Fin.appendEquiv] using + congrFun hin (Fin.castAdd 1 (Fin.castAdd l i)) have hidx : Fin.castAdd 1 (Fin.castAdd l i) = (⟨i.val, by omega⟩ : Fin (k + l + 1)) := by apply Fin.ext rfl @@ -346,7 +326,8 @@ theorem iterateRuntime (input : δ ↪ (Fin k → ℕ)) (output : σ ↪ (Fin l exact hh have hstate (i : Fin l) : x.regs (src ⟨k + i.val, by omega⟩) = output a₀ i := by have hh : x.regs (src (Fin.castAdd 1 (Fin.natAdd k i))) = output a₀ i := by - simpa [a₀, Fin.appendEquiv] using congrFun hin (Fin.castAdd 1 (Fin.natAdd k i)) + simpa [a₀, appendWords, Fin.appendEquiv] using + congrFun hin (Fin.castAdd 1 (Fin.natAdd k i)) have hidx : Fin.castAdd 1 (Fin.natAdd k i) = (⟨k + i.val, by omega⟩ : Fin (k + l + 1)) := by apply Fin.ext @@ -419,11 +400,11 @@ theorem iterateRuntime (input : δ ↪ (Fin k → ℕ)) (output : σ ↪ (Fin l have hvparts := (validHistory_bind w (body (d₀, q.1)) (iterate body d₀ j) q.2).mp hyv have hinbody : y.regs ∘ bs = - ((input.prodMap output).trans (Fin.appendEquiv k l).toEmbedding) (d₀, q.1) := by + (appendWords input output) (d₀, q.1) := by funext i refine Fin.addCases (fun i => ?_) (fun i => ?_) i - · simpa [bs, Fin.appendEquiv] using hye i - · simpa [bs, Fin.appendEquiv] using hys i + · simpa [bs, appendWords, Fin.appendEquiv] using hye i + · simpa [bs, appendWords, Fin.appendEquiv] using hys i obtain ⟨u, hu, hum, huo, huf⟩ := hrun w (d₀, q.1) y hyb hinbody (hym ▸ hysafe.1) (hym ▸ hvparts.1) rw [hym] at hu hum huo From 383a52021e3bf6f7e6ec5a52703dba4794a287ab Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 21:18:31 -0500 Subject: [PATCH 19/27] refactor(WordRAM): expose shared glue rules and infer stack push Address outside review5731265464. Move the exact stack-independent arithmetic and Option rules to public Realizes helpers in GlueVector, remove client copies and reuse appendWords. Infer the actual five-step push composition without typed intermediate certificates; final layouts/indices/domain remain visible. Statement meanings, clocks, source methods and tests are preserved. Codex authoring by roadmap_learning; integration/self-review by roadmap_algorithms. Ordinary parent merge carries2230a929 fix. Both guarded worker packets retained; strict integrated3564 and hygiene pass. Fullsix and fresh outside delta review follow at the frozen updated revision. No selected abstract impact or broader M1 completion claim. --- .../Realization/Client/MemoryStack.lean | 127 +++--------------- .../WordRAM/Realization/GlueVector.lean | 70 +++++++++- 2 files changed, 89 insertions(+), 108 deletions(-) diff --git a/Complexity/Computability/WordRAM/Realization/Client/MemoryStack.lean b/Complexity/Computability/WordRAM/Realization/Client/MemoryStack.lean index 24be91672b..de5d905ce3 100644 --- a/Complexity/Computability/WordRAM/Realization/Client/MemoryStack.lean +++ b/Complexity/Computability/WordRAM/Realization/Client/MemoryStack.lean @@ -59,69 +59,7 @@ def layoutWords : Stack.Memory.Layout ↪ (Fin 4 → ℕ) where /-- Push arguments append the unshifted value to the complete runtime layout. -/ def pushWords : Stack.Memory.Layout × ℕ ↪ (Fin 5 → ℕ) := - (layoutWords.prodMap (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding).trans - (Fin.appendEquiv 4 1).toEmbedding - -private def append {δ α : Type} {k l : ℕ} (input : δ ↪ (Fin k → ℕ)) - (answer : α ↪ (Fin l → ℕ)) : δ × α ↪ (Fin (k + l) → ℕ) := - (input.prodMap answer).trans (Fin.appendEquiv k l).toEmbedding - -private theorem addWords {δ : Type} {k : ℕ} (input : δ ↪ (Fin k → ℕ)) (i j : Fin k) : - Realizes .exact (fun d => pure (input d i + input d j)) input word - (fun w d _ => Fits w (input d i + input d j)) (fun _ _ => 3) := by - have ha : GlueComputes .exact (fun _ : Fin k → ℕ => True) (fun v => v i) - (fun v => v i + v j) 1 := GlueComputes.proj i (fun _ _ => Nat.le_add_right _ _) - have hb : GlueComputes .exact (fun _ : Fin k → ℕ => True) (fun v => v j) - (fun v => v i + v j) 1 := GlueComputes.proj j (fun _ _ => Nat.le_add_left _ _) - have h := Realizes.glue input (ha.bin_exact hb .add (fun _ _ => rfl) (fun _ _ => le_rfl)) - exact h.consequence (fun _ _ _ _ _ hf => ⟨trivial, hf⟩) (fun _ _ => le_rfl) - -private theorem increment {δ : Type} {k : ℕ} (input : δ ↪ (Fin k → ℕ)) (i : Fin k) : - Realizes .exact (fun d => pure (input d i + 1)) input word - (fun w d _ => Fits w (input d i + 1)) (fun _ _ => 3) := by - have ha : GlueComputes .exact (fun _ : Fin k → ℕ => True) (fun v => v i) - (fun v => v i + 1) 1 := GlueComputes.proj i (fun _ _ => Nat.le_add_right _ _) - have hb : GlueComputes .exact (fun _ : Fin k → ℕ => True) (fun _ => 1) - (fun v => v i + 1) 1 := GlueComputes.const 1 (fun _ _ => by omega) - have h := Realizes.glue input (ha.bin_exact hb .add (fun _ _ => rfl) (fun _ _ => le_rfl)) - exact h.consequence (fun _ _ _ _ _ hf => ⟨trivial, hf⟩) (fun _ _ => le_rfl) - -private theorem decrement {δ : Type} {k : ℕ} (input : δ ↪ (Fin k → ℕ)) (i : Fin k) : - Realizes .exact (fun d => pure (input d i - 1)) input word - (fun w d _ => 1 ≤ input d i ∧ Fits w (input d i)) (fun _ _ => 3) := by - have ha : GlueComputes .exact (fun v : Fin k → ℕ => 1 ≤ v i) (fun v => v i) - (fun v => v i) 1 := GlueComputes.proj i (fun _ _ => le_rfl) - have hb : GlueComputes .exact (fun v : Fin k → ℕ => 1 ≤ v i) (fun _ => 1) - (fun v => v i) 1 := GlueComputes.const 1 (fun _ h => h) - exact Realizes.glue input (ha.bin_exact hb .sub - (fun v h => by simp [h]) (fun _ _ => Nat.sub_le _ _)) - -private theorem returnNone {δ : Type} {k : ℕ} (input : δ ↪ (Fin k → ℕ)) : - Realizes .exact (fun _ => pure none) input optionWords - (fun _ _ _ => True) (fun _ _ => 4) := by - have h := Realizes.glueVector (B := .exact) input optionWords (fun _ => none) - (fun _ _ => 0) (fun _ _ => 0) (fun _ => 1) - (fun _ i => by fin_cases i <;> rfl) - (P := fun _ => True) (fun _ => GlueComputes.const 0 (fun _ _ => le_rfl)) - exact h.consequence (fun w _ _ _ _ _ => ⟨trivial, fun _ => fits_zero w⟩) - (fun _ _ => by simp) - -private theorem returnSome {δ : Type} {k : ℕ} (input : δ ↪ (Fin k → ℕ)) (j : Fin k) : - Realizes .exact (fun d => pure (some (input d j))) input optionWords - (fun w d _ => Fits w 1 ∧ Fits w (input d j)) (fun _ _ => 4) := by - let g : Fin 2 → (Fin k → ℕ) → ℕ := fun i v => if i = 0 then 1 else v j - have hc (i : Fin 2) : GlueComputes .exact (fun _ : Fin k → ℕ => True) (g i) (g i) 1 := by - fin_cases i - · exact GlueComputes.const 1 (fun _ _ => le_rfl) - · exact GlueComputes.proj j (fun _ _ => le_rfl) - have h := Realizes.glueVector input optionWords (fun d => some (input d j)) g g - (fun _ => 1) (fun _ i => by fin_cases i <;> rfl) hc - apply h.consequence (hwork := fun _ _ => by simp) - intro w d s _ _ ⟨h1, hv⟩ - refine ⟨trivial, fun i => ?_⟩ - fin_cases i - · exact h1 - · exact hv + appendWords layoutWords (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding /-- Size uses the existing count query with no additional local computation. -/ theorem realizes_size : @@ -137,42 +75,19 @@ theorem realizes_push : pushWords (Equiv.ofUnique Unit (Fin 0 → ℕ)).toEmbedding (fun w d s => d.1.Valid w d.1.items.length ∧ Stack.Memory.Inv d.1 s ∧ s.mem d.1.count.base < d.1.items.length) (fun _ _ => 10) := by - let e₁ := append pushWords word - let e₂ := append e₁ word - let e₃ := append e₂ word - let e₄ := append e₃ unitWords - have hread : Realizes .exact (fun d : Stack.Memory.Layout × ℕ => Memory.read d.1.count.base) - pushWords word (fun _ _ _ => True) (fun _ _ => 0) := Realizes.read pushWords 0 - have haddr : Realizes .exact - (fun d : (Stack.Memory.Layout × ℕ) × ℕ => pure (d.1.1.items.base + d.2)) - e₁ word (fun w d _ => Fits w (d.1.1.items.base + d.2)) (fun _ _ => 3) := - addWords e₁ 2 5 - have hnext : Realizes .exact - (fun d : ((Stack.Memory.Layout × ℕ) × ℕ) × ℕ => pure (d.1.2 + 1)) - e₂ word (fun w d _ => Fits w (d.1.2 + 1)) (fun _ _ => 3) := increment e₂ 5 - have hwrite : Realizes .exact - (fun d : (((Stack.Memory.Layout × ℕ) × ℕ) × ℕ) × ℕ => - Memory.write d.1.2 d.1.1.1.2) - e₃ unitWords (fun _ _ _ => True) (fun _ _ => 0) := Realizes.write e₃ 6 4 - have hcount : Realizes .exact - (fun d : ((((Stack.Memory.Layout × ℕ) × ℕ) × ℕ) × ℕ) × Unit => - Memory.write d.1.1.1.1.1.count.base d.1.2) - e₄ unitWords (fun _ _ _ => True) (fun _ _ => 0) := Realizes.write e₄ 0 7 - have hwrites := (Realizes.bind hwrite hcount).consequence - (safe' := fun _ _ _ => True) (work' := fun _ _ => 1) - (fun _ _ _ _ _ _ => ⟨trivial, trivial⟩) (fun _ _ => le_rfl) - have hinc := (Realizes.bind hnext hwrites).consequence - (safe' := fun w d _ => Fits w (d.1.2 + 1)) (work' := fun _ _ => 5) - (fun _ _ _ _ _ h => ⟨h, trivial⟩) (fun _ _ => le_rfl) - have hcompute := (Realizes.bind haddr hinc).consequence - (safe' := fun w d _ => Fits w (d.1.1.items.base + d.2) ∧ Fits w (d.2 + 1)) - (work' := fun _ _ => 9) (fun _ _ _ _ _ h => h) (fun _ _ => le_rfl) - apply (Realizes.bind hread hcompute).consequence (hwork := fun _ _ => le_rfl) + let e₁ := appendWords pushWords word + let e₂ := appendWords e₁ word + let e₃ := appendWords e₂ word + let e₄ := appendWords e₃ unitWords + apply ((Realizes.read pushWords 0).bind + ((Realizes.addWords e₁ 2 5).bind ((Realizes.increment e₂ 5).bind + ((Realizes.write e₃ 6 4).bind (Realizes.write e₄ 0 7))))).consequence + (hwork := fun _ _ => le_rfl) intro w d s _ hw ⟨hL, hInv, hcap⟩ change True ∧ Fits w (d.1.items.base + s.mem d.1.count.base) ∧ - Fits w (s.mem d.1.count.base + 1) + Fits w (s.mem d.1.count.base + 1) ∧ True ∧ True exact ⟨trivial, hw.fits_of_lt_frontier (hInv.item_lt hcap), - hL.capacity_fits.of_le (by omega)⟩ + hL.capacity_fits.of_le (by omega), trivial, trivial⟩ /-- Pop returns a presence tag and unshifted payload. Its positive branch uses defined exact subtraction and performs the existing memory reads/write. All copies and branch/bind overhead @@ -180,12 +95,12 @@ are charged; the local-work allowance is 19 for either path, including the short theorem realizes_pop : Realizes .exact Stack.Memory.pop layoutWords optionWords (fun w L s => L.Valid w L.items.length ∧ Stack.Memory.Inv L s) (fun _ _ => 19) := by - let e₁ := append layoutWords word - let e₂ := append e₁ word - let e₃ := append e₂ word - let e₄ := append e₃ word - let e₅ := append e₄ word - let e₆ := append e₅ unitWords + let e₁ := appendWords layoutWords word + let e₂ := appendWords e₁ word + let e₃ := appendWords e₂ word + let e₄ := appendWords e₃ word + let e₅ := appendWords e₄ word + let e₆ := appendWords e₅ unitWords have hread : Realizes .exact (fun L : Stack.Memory.Layout => Memory.read L.count.base) layoutWords word (fun _ _ _ => True) (fun _ _ => 0) := Realizes.read layoutWords 0 have hzero : Realizes .exact (fun _ : Stack.Memory.Layout × ℕ => pure 0) e₁ word @@ -196,11 +111,11 @@ theorem realizes_pop : exact h.consequence (fun w _ _ _ _ _ => ⟨trivial, fits_zero w⟩) (fun _ _ => le_rfl) have hsub : Realizes .exact (fun d : (Stack.Memory.Layout × ℕ) × ℕ => pure (d.1.2 - 1)) e₂ word - (fun w d _ => 1 ≤ d.1.2 ∧ Fits w d.1.2) (fun _ _ => 3) := decrement e₂ 4 + (fun w d _ => 1 ≤ d.1.2 ∧ Fits w d.1.2) (fun _ _ => 3) := Realizes.decrement e₂ 4 have haddr : Realizes .exact (fun d : ((Stack.Memory.Layout × ℕ) × ℕ) × ℕ => pure (d.1.1.1.items.base + d.2)) e₃ word (fun w d _ => Fits w (d.1.1.1.items.base + d.2)) (fun _ _ => 3) := - addWords e₃ 2 6 + Realizes.addWords e₃ 2 6 have hitem : Realizes .exact (fun d : (((Stack.Memory.Layout × ℕ) × ℕ) × ℕ) × ℕ => Memory.read d.2) e₄ word (fun _ _ _ => True) (fun _ _ => 0) := Realizes.read e₄ 7 @@ -211,7 +126,7 @@ theorem realizes_pop : have hsome : Realizes .exact (fun d : (((((Stack.Memory.Layout × ℕ) × ℕ) × ℕ) × ℕ) × ℕ) × Unit => pure (some d.1.2)) e₆ optionWords - (fun w d _ => Fits w 1 ∧ Fits w d.1.2) (fun _ _ => 4) := returnSome e₆ 8 + (fun w d _ => Fits w 1 ∧ Fits w d.1.2) (fun _ _ => 4) := Realizes.returnSome e₆ 8 have hpos := (Realizes.bind hsub (Realizes.bind haddr (Realizes.bind hitem (Realizes.bind hwrite hsome)))).consequence (safe' := fun w d s => 1 ≤ d.1.2 ∧ Fits w d.1.2 ∧ @@ -225,7 +140,7 @@ theorem realizes_pop : Fits w 1 ∧ Fits w (s.mem (d.1.1.items.base + (d.1.2 - 1))) exact ⟨⟨hpos, hn⟩, ha, trivial, trivial, h1, hv⟩) (fun _ _ => le_rfl) - have hbranch := Realizes.branch (.eq 4 5) (returnNone e₂) hpos + have hbranch := Realizes.branch (.eq 4 5) (Realizes.returnNone e₂) hpos apply (Realizes.bind hread (Realizes.bind hzero hbranch)).consequence · intro w L s hwords hw ⟨hL, hInv⟩ have hn : Fits w (s.mem L.count.base) := hw.mem_fits _ diff --git a/Complexity/Computability/WordRAM/Realization/GlueVector.lean b/Complexity/Computability/WordRAM/Realization/GlueVector.lean index 6b52f24bf0..1f5e8ebfc0 100644 --- a/Complexity/Computability/WordRAM/Realization/GlueVector.lean +++ b/Complexity/Computability/WordRAM/Realization/GlueVector.lean @@ -7,8 +7,7 @@ Authors: Samuel Schlesinger module -- upstream: candidate -public import Complexity.Computability.WordRAM.Realization.Basic -public import Complexity.Computability.WordRAM.Glue +public import Complexity.Computability.WordRAM.Realization.Glue /-! # Certified finite-result word glue @@ -31,6 +30,8 @@ true tag still requires the visible word-fit condition for `1`. ## Main results * `Realizes.glueVector`: realize a finite result using certified scalar coordinate computations. +* `Realizes.addWords`, `Realizes.increment`, `Realizes.decrement`: charged scalar word arithmetic. +* `Realizes.returnNone`, `Realizes.returnSome`: charged optional-word results. This is a realization rule over the existing memory client and structured native code, not an additional program syntax or execution judgment. @@ -210,5 +211,70 @@ theorem glueVector (input : δ ↪ (Fin k → ℕ)) (output : α ↪ (Fin l → · intro i hif htmp exact hyf i (fun _ => htmp _) (fun _ _ => htmp _) +/-- Add two input words when their sum fits; both projections and the addition are charged. -/ +theorem addWords {δ : Type} {k : ℕ} (input : δ ↪ (Fin k → ℕ)) (i j : Fin k) : + Realizes .exact (fun d => pure (input d i + input d j)) input + (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + (fun w d _ => Fits w (input d i + input d j)) (fun _ _ => 3) := by + have ha : GlueComputes .exact (fun _ : Fin k → ℕ => True) (fun v => v i) + (fun v => v i + v j) 1 := GlueComputes.proj i (fun _ _ => Nat.le_add_right _ _) + have hb : GlueComputes .exact (fun _ : Fin k → ℕ => True) (fun v => v j) + (fun v => v i + v j) 1 := GlueComputes.proj j (fun _ _ => Nat.le_add_left _ _) + have h := Realizes.glue input (ha.bin_exact hb .add (fun _ _ => rfl) (fun _ _ => le_rfl)) + exact h.consequence (fun _ _ _ _ _ hf => ⟨trivial, hf⟩) (fun _ _ => le_rfl) + +/-- Increment an input word with an explicit successor-fit condition and three local units. -/ +theorem increment {δ : Type} {k : ℕ} (input : δ ↪ (Fin k → ℕ)) (i : Fin k) : + Realizes .exact (fun d => pure (input d i + 1)) input + (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + (fun w d _ => Fits w (input d i + 1)) (fun _ _ => 3) := by + have ha : GlueComputes .exact (fun _ : Fin k → ℕ => True) (fun v => v i) + (fun v => v i + 1) 1 := GlueComputes.proj i (fun _ _ => Nat.le_add_right _ _) + have hb : GlueComputes .exact (fun _ : Fin k → ℕ => True) (fun _ => 1) + (fun v => v i + 1) 1 := GlueComputes.const 1 (fun _ _ => by omega) + have h := Realizes.glue input (ha.bin_exact hb .add (fun _ _ => rfl) (fun _ _ => le_rfl)) + exact h.consequence (fun _ _ _ _ _ hf => ⟨trivial, hf⟩) (fun _ _ => le_rfl) + +/-- Defined subtraction of one keeps positivity and the input-word envelope explicit. -/ +theorem decrement {δ : Type} {k : ℕ} (input : δ ↪ (Fin k → ℕ)) (i : Fin k) : + Realizes .exact (fun d => pure (input d i - 1)) input + (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + (fun w d _ => 1 ≤ input d i ∧ Fits w (input d i)) (fun _ _ => 3) := by + have ha : GlueComputes .exact (fun v : Fin k → ℕ => 1 ≤ v i) (fun v => v i) + (fun v => v i) 1 := GlueComputes.proj i (fun _ _ => le_rfl) + have hb : GlueComputes .exact (fun v : Fin k → ℕ => 1 ≤ v i) (fun _ => 1) + (fun v => v i) 1 := GlueComputes.const 1 (fun _ h => h) + exact Realizes.glue input (ha.bin_exact hb .sub + (fun v h => by simp [h]) (fun _ _ => Nat.sub_le _ _)) + +/-- Return the two zero words encoding `none`, charging both constants and output copies. -/ +theorem returnNone {δ : Type} {k : ℕ} (input : δ ↪ (Fin k → ℕ)) : + Realizes .exact (fun _ => pure none) input optionWords + (fun _ _ _ => True) (fun _ _ => 4) := by + have h := Realizes.glueVector (B := .exact) input optionWords (fun _ => none) + (fun _ _ => 0) (fun _ _ => 0) (fun _ => 1) + (fun _ i => by fin_cases i <;> rfl) + (P := fun _ => True) (fun _ => GlueComputes.const 0 (fun _ _ => le_rfl)) + exact h.consequence (fun w _ _ _ _ _ => ⟨trivial, fun _ => fits_zero w⟩) + (fun _ _ => by simp) + +/-- Return a presence tag and unshifted input payload, with both word fits explicit. -/ +theorem returnSome {δ : Type} {k : ℕ} (input : δ ↪ (Fin k → ℕ)) (j : Fin k) : + Realizes .exact (fun d => pure (some (input d j))) input optionWords + (fun w d _ => Fits w 1 ∧ Fits w (input d j)) (fun _ _ => 4) := by + let g : Fin 2 → (Fin k → ℕ) → ℕ := fun i v => if i = 0 then 1 else v j + have hc (i : Fin 2) : GlueComputes .exact (fun _ : Fin k → ℕ => True) (g i) (g i) 1 := by + fin_cases i + · exact GlueComputes.const 1 (fun _ _ => le_rfl) + · exact GlueComputes.proj j (fun _ _ => le_rfl) + have h := Realizes.glueVector input optionWords (fun d => some (input d j)) g g + (fun _ => 1) (fun _ i => by fin_cases i <;> rfl) hc + apply h.consequence (hwork := fun _ _ => by simp) + intro w d s _ _ ⟨h1, hv⟩ + refine ⟨trivial, fun i => ?_⟩ + fin_cases i + · exact h1 + · exact hv + end Realizes end Cslib.WordRAM.Realization From a91fb0ad966b089858414a171317c18256f42bdc Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 21:18:52 -0500 Subject: [PATCH 20/27] feat(WordRAM): realize tactic for memory-client realizations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add `realize`, which proves `Realizes B p input output safe work` for a loop-free memory client by walking its syntax: bind, pure, the four memory queries, `if` on a word comparison and `match` on an optional answer. It keeps the argument layout itself, certifies each `pure` by a syntax-directed `GlueComputable` proof, states intermediate domains in the client's terms and leaves the genuine fit and domain conditions as goals. `Realization/Absorb.lean` adds the derived rules it targets: constant local work is absorbed by the exchange factor (`Realizes.mono_mul`), giving `bind₀`, `branch₀` and `pure₀`, plus `Realizes.congr` and the `WordEncoding` class for intermediate answers. Refs #2205. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 2 + .../WordRAM/Realization/Absorb.lean | 142 +++ Complexity/Tactic/Realize.lean | 867 ++++++++++++++++++ ComplexityTests/WordRAMRealize.lean | 168 ++++ 4 files changed, 1179 insertions(+) create mode 100644 Complexity/Computability/WordRAM/Realization/Absorb.lean create mode 100644 Complexity/Tactic/Realize.lean create mode 100644 ComplexityTests/WordRAMRealize.lean diff --git a/Complexity.lean b/Complexity.lean index cb9df7d950..04e0c69a15 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2556,6 +2556,7 @@ public import Complexity.Computability.WordRAM.Randomized.Law public import Complexity.Computability.WordRAM.Randomized.LoopSegment public import Complexity.Computability.WordRAM.Randomized.Structured public import Complexity.Computability.WordRAM.Randomized.UniformLoopExit +public import Complexity.Computability.WordRAM.Realization.Absorb public import Complexity.Computability.WordRAM.Realization.Basic public import Complexity.Computability.WordRAM.Realization.Bind public import Complexity.Computability.WordRAM.Realization.Branch @@ -3807,6 +3808,7 @@ public import Complexity.Tactic.LogSpace.Init public import Complexity.Tactic.PPT public import Complexity.Tactic.PolyTime public import Complexity.Tactic.PolyTime.Init +public import Complexity.Tactic.Realize public import Complexity.Tactic.Stateful public import Complexity.Tactic.Stateful.Init public import Complexity.Tactic.Synthesis diff --git a/Complexity/Computability/WordRAM/Realization/Absorb.lean b/Complexity/Computability/WordRAM/Realization/Absorb.lean new file mode 100644 index 0000000000..113b8aa708 --- /dev/null +++ b/Complexity/Computability/WordRAM/Realization/Absorb.lean @@ -0,0 +1,142 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Realization.Bind +public import Complexity.Computability.WordRAM.Realization.Branch +public import Complexity.Computability.WordRAM.Realization.GlueVector +public import Complexity.Computability.WordRAM.Realization.Primitive + +/-! +# Realization rules with absorbed constant local work + +`Realizes` bounds native instructions by `C * (memory charge + local work + 1)` with one +existential exchange factor `C`. Local work bounded by a constant multiple of another grading can +therefore be traded for a larger factor (`Realizes.mono_mul`). In particular, a loop-free +composition of glue, binds and branches has the same certificate with local work `0`: its native +cost is `O(memory charge + 1)`. Loop work grows with the iteration count, so loops keep their +graded charge. + +The rules below are the C2 rules `bind`, `branch` and `glueVector` with that absorption applied. +They are the target shapes of the `realize` tactic. Scalar and finite-vector glue takes +`GlueComputable` certificates, whose instruction budgets stay hidden. + +## Main definitions + +* `WordEncoding`: the word encoding `realize` uses for an intermediate answer type. + +## Main results + +* `Realizes.mono_mul`: absorb a local-work grading dominated by a constant multiple. +* `Realizes.congr`: transport a certificate along pointwise equal programs. +* `Realizes.bind₀`, `Realizes.branch₀`: sequencing and branching with absorbed control work. +* `Realizes.pure₀`: finite-result glue from `GlueComputable` coordinates, with a visible domain + and an explicit word-fit obligation. + +These are derived rules over the existing realization kernel; no execution semantics or +certificate is added. +-/ + +@[expose] public section + +namespace Cslib.WordRAM.Realization + +open Cslib.DataStructure + +/-- The faithful word encoding `realize` uses when a bind passes an answer of type `α` to its +continuation. Instances cover words, unit, optional words and Booleans. -/ +class WordEncoding (α : Type) where + /-- The number of words. -/ + width : ℕ + /-- The faithful encoding. -/ + encode : α ↪ (Fin width → ℕ) + +instance : WordEncoding ℕ := ⟨1, (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding⟩ + +instance : WordEncoding Unit := ⟨0, (Equiv.ofUnique Unit (Fin 0 → ℕ)).toEmbedding⟩ + +instance : WordEncoding (Option ℕ) := ⟨2, optionWords⟩ + +instance : WordEncoding Bool := ⟨2, boolWords⟩ + +variable {B : Basis} {δ α β : Type} {k l m : ℕ} + +namespace Realizes + +variable {p : δ → Memory.Prog α} {input : δ ↪ (Fin k → ℕ)} {output : α ↪ (Fin l → ℕ)} + {safe : ℕ → δ → Memory.State → Prop} {work work' : δ → Memory.State → ℕ} + +/-- A local-work grading within a constant multiple of another is absorbed by the exchange +factor. The new factor is `C * (K + 1)`; the emitter and scratch are unchanged. -/ +theorem mono_mul (h : Realizes B p input output safe work) (K : ℕ) + (hwork : ∀ d s, work d s ≤ K * (work' d s + 1)) : + Realizes B p input output safe work' := by + obtain ⟨n, C, emit, he⟩ := h + refine ⟨n, C * (K + 1), emit, fun frontier src tmp hi hd hf hs => ?_⟩ + obtain ⟨hnw, hrun⟩ := he frontier src tmp hi hd hf hs + refine ⟨hnw, fun w d x hx hin hsafe hv => ?_⟩ + obtain ⟨y, hy, hm, hout, hframe⟩ := hrun w d x hx hin hsafe hv + refine ⟨y, hy.mono ?_, hm, hout, hframe⟩ + have hw := hwork d (memoryState frontier x) + rw [Nat.mul_assoc] + apply Nat.mul_le_mul_left + have hexpand : (K + 1) * (memoryCost (p d) (memoryState frontier x) + + work' d (memoryState frontier x) + 1) = + K * (work' d (memoryState frontier x) + 1) + + K * memoryCost (p d) (memoryState frontier x) + + (memoryCost (p d) (memoryState frontier x) + work' d (memoryState frontier x) + 1) := by + ring + omega + +/-- Pointwise equal programs have the same certificates. -/ +theorem congr {p' : δ → Memory.Prog α} (h : Realizes B p input output safe work) + (hp : ∀ d, p d = p' d) : Realizes B p' input output safe work := + funext hp ▸ h + +variable {q : δ × α → Memory.Prog β} {answer : α ↪ (Fin l → ℕ)} {output' : β ↪ (Fin m → ℕ)} + {safeP : ℕ → δ → Memory.State → Prop} {safeQ : ℕ → δ × α → Memory.State → Prop} + +/-- Sequencing with the bind's control unit absorbed. -/ +theorem bind₀ (hP : Realizes B p input answer safeP (fun _ _ => 0)) + (hQ : Realizes B q ((input.prodMap answer).trans (Fin.appendEquiv k l).toEmbedding) output' + safeQ (fun _ _ => 0)) : + Realizes B (fun d => (p d).bind (fun a => q (d, a))) input output' + (fun w d s => safeP w d s ∧ + safeQ w (d, (Memory.spec 0).clientResult s (p d)) ((Memory.spec 0).clientState s (p d))) + (fun _ _ => 0) := + (hP.bind hQ).mono_mul 1 (fun _ _ => le_refl _) + +/-- Branching with the test and jump absorbed. Only the selected alternative's domain is +required. -/ +theorem branch₀ {pos neg : δ → Memory.Prog α} {safePos safeNeg : ℕ → δ → Memory.State → Prop} + (c : Cond k) (hp : Realizes B pos input output safePos (fun _ _ => 0)) + (hn : Realizes B neg input output safeNeg (fun _ _ => 0)) : + Realizes B (fun d => if test c (input d) then pos d else neg d) input output + (fun w d s => if test c (input d) then safePos w d s else safeNeg w d s) (fun _ _ => 0) := + (branch c hp hn).mono_mul 2 (fun _ _ => by split <;> simp) + +/-- Return a computed finite result. Each coordinate has a `GlueComputable` certificate over the +live words with a common domain `P` and value envelope `bound`; `need` is any condition that, +together with the live words' fit and `P`, makes the envelope fit. Native glue cost is absorbed. -/ +theorem pure₀ (input : δ ↪ (Fin k → ℕ)) (output : α ↪ (Fin l → ℕ)) (f : δ → α) + (g : Fin l → (Fin k → ℕ) → ℕ) {P : (Fin k → ℕ) → Prop} {bound : (Fin k → ℕ) → ℕ} + (need : ℕ → (Fin k → ℕ) → Prop) + (hresult : ∀ d i, output (f d) i = g i (input d)) + (hcoord : ∀ i, GlueComputable B (g i) P bound) + (hneed : ∀ w v, (∀ i, Fits w (v i)) → P v → need w v → Fits w (bound v)) : + Realizes B (fun d => pure (f d)) input output + (fun w d _ => P (input d) ∧ need w (input d)) (fun _ _ => 0) := by + choose time htime using hcoord + refine ((glueVector input output f g (fun _ => bound) time hresult htime).consequence + (fun w d _ hwords _ h => ⟨h.1, fun _ => hneed w _ hwords h.1 h.2⟩) + (fun _ _ => le_rfl)).mono_mul ((∑ i, time i) + l) (fun _ _ => ?_) + simp + +end Realizes + +end Cslib.WordRAM.Realization diff --git a/Complexity/Tactic/Realize.lean b/Complexity/Tactic/Realize.lean new file mode 100644 index 0000000000..e759fc086d --- /dev/null +++ b/Complexity/Tactic/Realize.lean @@ -0,0 +1,867 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Realization.Absorb +public import Mathlib.Tactic.FinCases + +/-! +# The `realize` tactic + +`realize` proves a goal `Realizes B p input output safe work` for a loop-free memory client `p`. +It walks the client syntax, unfolding definitions: `bind`, `pure`, the four memory queries and +`if` on a word equality or comparison. It keeps the argument layout itself: after each bind the +live words are the previous ones followed by the answer's encoding (`WordEncoding`), and every +operand is located among them by definitional equality. An operand that is not a live word is +first computed by an inserted `pure`, which leaves the program unchanged up to definitional +equality. + +Each `pure` is certified by a `GlueComputable` proof assembled from the closure rules of +`Complexity.Computability.WordRAM.Glue`, one rule per syntactic form: projections, constants, and +the exact- or standard-basis arithmetic and bitwise operations. Exact subtraction, division and +remainder add their definedness conditions to the visible domain. The common envelope is the +maximum of every intermediate value; `realize` proves that it fits from the fit of the live +words, from the domain, and from the fit of the values it cannot derive, which become the visible +fit conditions. A value is derived when another fitting value dominates it, for example `a - b` +and `b` by `a`, or the summands by a sum. + +Local work is absorbed (`Realizes.mono_mul`): every certificate built by `realize` has local work +`0`, so any local-work grading in the goal is accepted. Intermediate domains are stated in the +client's own terms, with the replies of earlier queries written as `s.mem a`, `s.frontier` and +updated memories. + +After the composition, `realize` introduces `w d s hwords hmem h`: the word width, the +environment, the memory, the fit of the input words, memory well-formedness, and the goal's +domain. It splits the conjunctions and `if`s of the synthesized domain and leaves each remaining +condition as a goal. There is no search: every form has one rule, and an unsupported form is +reported with its subterm. + +## Main definitions + +* `realize`: the tactic. +-/ + +public meta section + +namespace Cslib.Tactic.Realize + +open Lean Meta Elab Tactic Cslib.DataStructure Cslib.WordRAM Cslib.WordRAM.Realization + +/-- The live words of a realization environment. -/ +private structure Env where + /-- The environment type. -/ + ty : Expr + /-- The number of live words. -/ + k : Nat + /-- The encoding `ty ↪ (Fin k → ℕ)`. -/ + input : Expr + /-- `atoms[i]` is `fun d => input d i` in weak-head normal form. -/ + atoms : Array Expr + +/-- A certificate built by `realize`. -/ +private structure Out where + /-- A proof of `Realizes B prog input output safe (fun _ _ => 0)`. -/ + proof : Expr + /-- The output encoding. -/ + output : Expr + /-- The domain `fun w d s => …`, in the client's terms, definitionally equal to the proof's. -/ + safe : Expr + +/-- `(i : Fin n)` as a numeral. -/ +private def finLit (n i : Nat) : MetaM Expr := + mkNumeral (mkApp (mkConst ``Fin) (mkNatLit n)) i + +/-- The number of words of an encoding `α ↪ (Fin l → ℕ)`. -/ +private def encodingWidth (enc : Expr) : MetaM Nat := do + let ty ← instantiateMVars (← inferType enc) + let some (_, tgt) := ty.app2? ``Function.Embedding + | throwError "realize: expected an encoding `α ↪ (Fin l → ℕ)`, got{indentExpr ty}" + let n ← instantiateMVars tgt.bindingDomain!.appArg! + if let some l ← (evalNat n).run then return l + let some l ← (evalNat (← whnf n)).run + | throwError "realize: the encoding width is not a numeral in{indentExpr ty}" + return l + +/-- The atoms of `enc x`, for `x` an expression in the environment variable. -/ +private def encodingAtoms (enc x : Expr) (l : Nat) : MetaM (Array Expr) := + (Array.range l).mapM fun j => do + whnf (mkApp (← mkAppM ``DFunLike.coe #[enc, x]) (← finLit l j)) + +/-- Locate an expression among the live words. -/ +private def findAtom (atoms : Array Expr) (e : Expr) : MetaM (Option Nat) := do + let e' ← whnf e + for h : i in [0:atoms.size] do + if ← withNewMCtxDepth (isDefEq e' atoms[i]) then return some i + return none + +/-- Reduce `(a, b).1` and `(a, b).2` everywhere. -/ +private def reducePairs (e : Expr) : MetaM Expr := + Meta.transform e (post := fun e => do + if e.isAppOfArity ``Prod.fst 3 then + if let some (_, _, a, _) := e.appArg!.app4? ``Prod.mk then return .done a + if e.isAppOfArity ``Prod.snd 3 then + if let some (_, _, _, b) := e.appArg!.app4? ``Prod.mk then return .done b + if let .proj ``Prod i s := e then + if let some (_, _, a, b) := s.app4? ``Prod.mk then return .done (if i == 0 then a else b) + return .continue) + +/-- `φ₀ ∧ (φ₁ ∧ … φₙ)`, or `True`. -/ +private def conj : List Expr → Expr + | [] => mkConst ``True + | [f] => f + | f :: fs => mkAnd f (conj fs) + +/-- Project the conjunct at `pos` out of a proof of `conj` of `size` facts. -/ +private def conjunct (h : Expr) (size pos : Nat) : MetaM Expr := do + let mut h := h + for _ in [0:pos] do h ← mkAppM ``And.right #[h] + if pos + 1 == size then return h + mkAppM ``And.left #[h] + +/-! ### Word glue -/ + +/-- The supported glue operations. -/ +private inductive GOp + | add | sub | mul | div | mod | and | or | xor | shiftLeft | shiftRight + +/-- The glue rule name for an operation in the exact or standard basis. -/ +private def GOp.rule (exact : Bool) : GOp → Name + | .add => if exact then ``GlueComputable.add else ``GlueComputable.add_standard + | .sub => if exact then ``GlueComputable.sub else ``GlueComputable.sub_standard + | .mul => if exact then ``GlueComputable.mul else ``GlueComputable.mul_standard + | .div => if exact then ``GlueComputable.div else ``GlueComputable.div_standard + | .mod => if exact then ``GlueComputable.mod else ``GlueComputable.mod_standard + | .and => if exact then ``GlueComputable.and else ``GlueComputable.and_standard + | .or => if exact then ``GlueComputable.or else ``GlueComputable.or_standard + | .xor => if exact then ``GlueComputable.xor else ``GlueComputable.xor_standard + | .shiftLeft => + if exact then ``GlueComputable.shiftLeft else ``GlueComputable.shiftLeft_standard + | .shiftRight => + if exact then ``GlueComputable.shiftRight else ``GlueComputable.shiftRight_standard + +/-- The operator's heterogeneous-operation function. -/ +private def GOp.fn : GOp → Name + | .add => ``HAdd.hAdd | .sub => ``HSub.hSub | .mul => ``HMul.hMul | .div => ``HDiv.hDiv + | .mod => ``HMod.hMod | .and => ``HAnd.hAnd | .or => ``HOr.hOr | .xor => ``HXor.hXor + | .shiftLeft => ``HShiftLeft.hShiftLeft | .shiftRight => ``HShiftRight.hShiftRight + +/-- Whether the result is at most the left operand. -/ +private def GOp.belowLeft : GOp → Bool + | .sub | .div | .mod | .and | .shiftRight => true + | _ => false + +/-- A glue expression over the live words. -/ +private inductive GNode + /-- A live word. -/ + | reg (i : Nat) (user : Expr) + /-- A value independent of the environment. -/ + | const (c : Expr) (zero : Bool) + /-- A binary operation. -/ + | bin (op : GOp) (user : Expr) (a b : GNode) + +/-- The expression in the client's terms. -/ +private def GNode.user : GNode → Expr + | .reg _ u | .const u _ | .bin _ u _ _ => u + +/-- Recognize a binary operation on natural numbers. -/ +private def binOp? (e : Expr) : Option (GOp × Expr × Expr) := + [GOp.add, .sub, .mul, .div, .mod, .and, .or, .xor, .shiftLeft, .shiftRight].findSome? fun op => + if e.isAppOfArity op.fn 6 && (e.getArg! 0).isConstOf ``Nat then + some (op, e.getArg! 4, e.getArg! 5) + else none + +/-- Parse a word-valued expression over the live words. -/ +private partial def analyze (atoms : Array Expr) (d : Expr) (e : Expr) : MetaM GNode := do + if let some i ← findAtom atoms e then return .reg i e + if !e.containsFVar d.fvarId! then + return .const e (← withNewMCtxDepth (isDefEq e (mkNatLit 0))) + if let some (op, a, b) := binOp? e then + return .bin op e (← analyze atoms d a) (← analyze atoms d b) + let e' ← whnfR e + if e' != e then return ← analyze atoms d e' + throwError "realize: cannot compute{indentExpr e}\nfrom the live words{indentD + (MessageData.joinSep (atoms.toList.map fun a => m!"{a}") ", ")}" + +/-- A glue node numbered in depth-first order, with its value over the word vector. -/ +private inductive ANode + /-- A live word. -/ + | reg (idx : Nat) (val : Expr) (i : Nat) + /-- A constant. -/ + | const (idx : Nat) (val : Expr) (zero : Bool) + /-- A binary operation; `dom` numbers its definedness condition, if any. -/ + | bin (idx : Nat) (val : Expr) (op : GOp) (dom : Option Nat) (a b : ANode) + +/-- The depth-first number. -/ +private def ANode.idx : ANode → Nat + | .reg i .. | .const i .. | .bin i .. => i + +/-- The value over the word vector. -/ +private def ANode.val : ANode → Expr + | .reg _ v _ | .const _ v _ | .bin _ v .. => v + +/-- The values of all nodes in depth-first order and the definedness conditions, over the word +vector and in the client's terms. -/ +private structure Annot where + /-- Node values over the word vector. -/ + vals : Array Expr := #[] + /-- Definedness conditions over the word vector. -/ + domV : Array Expr := #[] + /-- Definedness conditions in the client's terms. -/ + domU : Array Expr := #[] + +/-- Number the nodes and compute their values over the word vector `v`. -/ +private partial def annotate (exact : Bool) (k : Nat) (v : Expr) : GNode → StateT Annot MetaM ANode + | .reg i _ => do + let val := mkApp v (← finLit k i) + let idx := (← get).vals.size + modify fun st => { st with vals := st.vals.push val } + return .reg idx val i + | .const c zero => do + let idx := (← get).vals.size + modify fun st => { st with vals := st.vals.push c } + return .const idx c zero + | .bin op _ a b => do + let idx := (← get).vals.size + modify fun st => { st with vals := st.vals.push default } + let a' ← annotate exact k v a + let b' ← annotate exact k v b + let val ← mkAppM op.fn #[a'.val, b'.val] + modify fun st => { st with vals := st.vals.set! idx val } + let dom? ← if exact then + match op with + | .sub => pure (some (← mkAppM ``LE.le #[b'.val, a'.val], + ← mkAppM ``LE.le #[b.user, a.user])) + | .div | .mod => pure (some (← mkAppM ``Ne #[b'.val, mkNatLit 0], + ← mkAppM ``Ne #[b.user, mkNatLit 0])) + | _ => pure none + else pure none + let dom ← match dom? with + | some (fv, fu) => do + let n := (← get).domV.size + modify fun st => { st with domV := st.domV.push fv, domU := st.domU.push fu } + pure (some n) + | none => pure none + return .bin idx val op dom a' b' + +/-- The node's value in the client's terms, by depth-first number. -/ +private partial def userVals (g : GNode) (acc : Array Expr := #[]) : Array Expr := + match g with + | .reg _ u => acc.push u + | .const c _ => acc.push c + | .bin _ u a b => userVals b (userVals a (acc.push u)) + +/-- The nodes whose fit is a visible condition: `covered` says that a dominating value fits. -/ +private partial def needNodes (exact : Bool) (n : ANode) (covered : Bool) + (acc : Array Nat := #[]) : Array Nat := + match n with + | .reg .. => acc + | .const idx _ zero => if zero || covered then acc else acc.push idx + | .bin idx _ op _ a b => + let acc := if op.belowLeft || covered then acc else acc.push idx + match op with + | .add | .or => + needNodes exact b true (needNodes exact a true acc) + | .shiftLeft => needNodes exact b false (needNodes exact a true acc) + | .sub => needNodes exact b exact (needNodes exact a false acc) + | _ => needNodes exact b false (needNodes exact a false acc) + +/-- Fit proofs of all nodes, indexed by depth-first number, mirroring `needNodes`. `self?` is the +node's fit from a dominating value. -/ +private partial def fitProofs (exact : Bool) (k : Nat) (w hregs hdom hneed : Expr) (ndom : Nat) + (needIdx : Array Nat) (n : ANode) (self? : Option Expr) : + StateT (Array Expr) MetaM Expr := do + let fitNeed (idx : Nat) : MetaM Expr := do + let some pos := needIdx.findIdx? (· == idx) | throwError "realize: internal fit error" + conjunct hneed needIdx.size pos + let fitLe (p h : Expr) : MetaM Expr := mkAppM ``Cslib.WordRAM.Fits.of_le #[p, h] + let record (idx : Nat) (p : Expr) : StateT (Array Expr) MetaM Expr := do + modify fun acc => acc.set! idx p + return p + match n with + | .reg idx _ i => record idx (mkApp hregs (← finLit k i)) + | .const idx _ zero => + if zero then + record idx (← mkAppM ``Cslib.WordRAM.fits_zero #[w]) + else match self? with + | some p => record idx p + | none => record idx (← fitNeed idx) + | .bin idx _ op dom a b => + if op.belowLeft then + let pa ← fitProofs exact k w hregs hdom hneed ndom needIdx a none + let pbDom ← match op, dom with + | .sub, some di => pure (some (← fitLe pa (← conjunct hdom ndom di))) + | _, _ => pure none + let _ ← fitProofs exact k w hregs hdom hneed ndom needIdx b pbDom + let le ← match op with + | .sub => mkAppM ``Nat.sub_le #[a.val, b.val] + | .div => mkAppM ``Nat.div_le_self #[a.val, b.val] + | .mod => mkAppM ``Nat.mod_le #[a.val, b.val] + | .and => mkAppOptM ``Nat.and_le_left #[a.val, b.val] + | _ => mkAppM ``Nat.shiftRight_le #[a.val, b.val] + record idx (← fitLe pa le) + else + let self ← match self? with + | some p => pure p + | none => fitNeed idx + match op with + | .add => + let _ ← fitProofs exact k w hregs hdom hneed ndom needIdx a + (some (← fitLe self (← mkAppM ``Nat.le_add_right #[a.val, b.val]))) + let _ ← fitProofs exact k w hregs hdom hneed ndom needIdx b + (some (← fitLe self (← mkAppM ``Nat.le_add_left #[b.val, a.val]))) + | .or => + let _ ← fitProofs exact k w hregs hdom hneed ndom needIdx a + (some (← fitLe self (← mkAppOptM ``Nat.left_le_or #[a.val, b.val]))) + let _ ← fitProofs exact k w hregs hdom hneed ndom needIdx b + (some (← fitLe self (← mkAppOptM ``Nat.right_le_or #[a.val, b.val]))) + | .shiftLeft => + let _ ← fitProofs exact k w hregs hdom hneed ndom needIdx a + (some (← fitLe self (← mkAppOptM ``Nat.le_shiftLeft #[a.val, b.val]))) + let _ ← fitProofs exact k w hregs hdom hneed ndom needIdx b none + | _ => + let _ ← fitProofs exact k w hregs hdom hneed ndom needIdx a none + let _ ← fitProofs exact k w hregs hdom hneed ndom needIdx b none + record idx self + +/-- `max` on natural numbers through the linear order, as in `le_max_left` and `max_lt`. -/ +private def natMax : MetaM Expr := do + let nat := mkConst ``Nat + mkAppOptM ``Max.max #[nat, ← mkAppOptM ``LinearOrder.toMax #[nat, none]] + +/-- `max n₀ (max n₁ … nₘ)` from position `i`. -/ +private partial def maxChain (maxFn : Expr) (vals : Array Expr) (i : Nat := 0) : Expr := + if i + 1 ≥ vals.size then vals[i]! else mkApp2 maxFn vals[i]! (maxChain maxFn vals (i + 1)) + +/-- `vals[pos] ≤ maxChain vals i`. -/ +private partial def memProof (maxFn : Expr) (vals : Array Expr) (pos : Nat) (i : Nat := 0) : + MetaM Expr := do + if i + 1 ≥ vals.size then return ← mkAppM ``le_refl #[vals[i]!] + if pos == i then return ← mkAppM ``le_max_left #[vals[i]!, maxChain maxFn vals (i + 1)] + mkAppOptM ``le_max_of_le_right #[none, none, none, vals[i]!, none, + ← memProof maxFn vals pos (i + 1)] + +/-- `Fits w (maxChain vals i)` from the fit of every value. -/ +private partial def fitChain (fits : Array Expr) (i : Nat := 0) : MetaM Expr := do + if i + 1 ≥ fits.size then return fits[i]! + mkAppM ``max_lt #[fits[i]!, ← fitChain fits (i + 1)] + +/-- The `GlueComputable` certificate of a node, one closure rule per form. -/ +private partial def glueProof (exact : Bool) (B kE P bound : Expr) (k : Nat) + (hb : Nat → MetaM Expr) (hd : Nat → MetaM Expr) : ANode → MetaM Expr + | .reg idx _ i => do + mkAppOptM ``GlueComputable.proj #[B, kE, P, bound, ← finLit k i, ← hb idx] + | .const idx c _ => do + mkAppOptM ``GlueComputable.const #[B, kE, P, bound, c, ← hb idx] + | .bin idx _ op dom a b => do + let ga ← glueProof exact B kE P bound k hb hd a + let gb ← glueProof exact B kE P bound k hb hd b + match dom with + | some di => mkAppM (op.rule exact) #[ga, gb, ← hd di, ← hb idx] + | none => mkAppM (op.rule exact) #[ga, gb, ← hb idx] + +/-- `![x₀, …, xₙ]`. -/ +private def vecOf (xs : List Expr) : MetaM Expr := do + match xs with + | [] => mkAppOptM ``Matrix.vecEmpty #[mkConst ``Nat] + | x :: xs => mkAppM ``Matrix.vecCons #[x, ← vecOf xs] + +/-- `∀ i, GlueComputable B (fun v => ![f₀ v, …] i) P bound` from the coordinate certificates. -/ +private partial def vecCoords (B kE P bound v : Expr) (vals proofs : Array Expr) (j : Nat) : + MetaM Expr := do + let l := vals.size + withLocalDeclD `i (mkApp (mkConst ``Fin) (mkNatLit (l - j))) fun i => do + let f ← mkLambdaFVars #[v] vals[j]! + if j + 1 == l then + return ← mkLambdaFVars #[i] (← mkAppOptM ``GlueComputable.vec_single + #[B, kE, P, bound, f, proofs[j]!, i]) + let g ← mkLambdaFVars #[v] (← vecOf (vals.toList.drop (j + 1))) + let tail ← vecCoords B kE P bound v vals proofs (j + 1) + mkLambdaFVars #[i] (← mkAppOptM ``GlueComputable.vec_cons + #[B, kE, P, bound, f, mkNatLit (l - j - 1), g, proofs[j]!, tail, i]) + +/-- Replace the maximal non-constructor subterms of `e` by local variables, then continue with +the skeleton and the pairs (variable, replaced subterm). -/ +private partial def withSkeleton {β : Type} (e : Expr) (acc : Array (Expr × Expr)) + (k : Expr → Array (Expr × Expr) → MetaM β) : MetaM β := do + let e' ← whnfR e + if let .const c _ := e'.getAppFn then + if let some (.ctorInfo info) := (← getEnv).find? c then + if info.induct != ``Nat then + let args := e'.getAppArgs + let rec go (j : Nat) (newArgs : Array Expr) (acc : Array (Expr × Expr)) : MetaM β := + if h : j < args.size then + if j < info.numParams then go (j + 1) (newArgs.push args[j]) acc + else withSkeleton args[j] acc fun a' acc' => go (j + 1) (newArgs.push a') acc' + else k (mkAppN e'.getAppFn newArgs) acc + return ← go 0 #[] acc + withLocalDeclD `z (← inferType e) fun z => k z (acc.push (z, e)) + +/-- The coordinates of `enc a` in the client's terms. Constructor applications are kept as a +skeleton so that the encoding reduces without unfolding the client's arithmetic. -/ +private def coordinates (enc a : Expr) (l : Nat) : MetaM (Array Expr) := + withSkeleton a #[] fun skel holes => do + let cs ← encodingAtoms enc skel l + return cs.map fun c => c.replaceFVars (holes.map (·.1)) (holes.map (·.2)) + +/-! ### Realization -/ + +/-- The basis of the goal and whether it is the exact basis. -/ +private structure Ctx where + /-- The basis. -/ + basis : Expr + /-- Whether the basis is `.exact`. -/ + exact : Bool + /-- Whether the basis is `.standard`. -/ + standard : Bool + +/-- `fun _ _ => 0`. -/ +private def zeroWork (δ : Expr) : MetaM Expr := + withLocalDeclD `d δ fun d => withLocalDeclD `s (mkConst ``Memory.State) fun s => + mkLambdaFVars #[d, s] (mkNatLit 0) + +/-- `fun _ _ _ => True`. -/ +private def trueSafe (δ : Expr) : MetaM Expr := + withLocalDeclD `w (mkConst ``Nat) fun w => withLocalDeclD `d δ fun d => + withLocalDeclD `s (mkConst ``Memory.State) fun s => mkLambdaFVars #[w, d, s] (mkConst ``True) + +/-- The input encoding of the continuation of a bind. -/ +private def appendInput (env : Env) (answer : Expr) (l : Nat) : MetaM Expr := do + let prod ← mkAppM ``Function.Embedding.prodMap #[env.input, answer] + let app ← mkAppM ``Equiv.toEmbedding + #[← mkAppOptM ``Fin.appendEquiv #[mkConst ``Nat, mkNatLit env.k, mkNatLit l]] + mkAppM ``Function.Embedding.trans #[prod, app] + +/-- The environment of the continuation of a bind whose answer has type `α`. -/ +private def extend (env : Env) (α answer : Expr) (l : Nat) : MetaM Env := do + let ty ← mkAppM ``Prod #[env.ty, α] + let input ← appendInput env answer l + let atoms ← withLocalDeclD `x ty fun x => do + let x1 ← mkAppM ``Prod.fst #[x] + let x2 ← mkAppM ``Prod.snd #[x] + let old ← env.atoms.mapM fun a => whnf (a.beta #[x1]) + let new ← encodingAtoms answer x2 l + (old ++ new).mapM fun a => mkLambdaFVars #[x] a + return { ty, k := env.k + l, input, atoms } + +/-- The reply and next memory of a client from memory `s`, in the client's terms. -/ +private def replyState (m s : Expr) : MetaM (Expr × Expr) := do + let m' ← whnfR m + let generic := do + let spec ← mkAppM ``Memory.spec #[mkNatLit 0] + return (← mkAppM ``StateTransitionSpec.clientResult #[spec, s, m], + ← mkAppM ``StateTransitionSpec.clientState #[spec, s, m]) + let mem ← mkAppM ``Memory.State.mem #[s] + let frontier ← mkAppM ``Memory.State.frontier #[s] + if m'.isAppOfArity ``PFunctor.FreeM.lift 2 then + let op ← whnfR m'.appArg! + if let some a := op.app1? ``Memory.Op.read then return (mkApp mem a, s) + if let some (a, v) := op.app2? ``Memory.Op.write then + return (mkConst ``Unit.unit, + ← mkAppM ``Memory.State.mk #[← mkAppM ``Function.update #[mem, a, v], frontier]) + if let some n := op.app1? ``Memory.Op.alloc then + return (frontier, ← mkAppM ``Memory.State.mk + #[← mkAppM ``Cslib.WordRAM.clear #[mem, frontier, n], ← mkAppM ``HAdd.hAdd #[frontier, n]]) + if let some n := op.app1? ``Memory.Op.allocDirty then + return (frontier, ← mkAppM ``Memory.State.mk #[mem, ← mkAppM ``HAdd.hAdd #[frontier, n]]) + return ← generic + if m'.isAppOfArity ``Pure.pure 4 then return (m'.appArg!, s) + if m'.isAppOfArity ``PFunctor.FreeM.pure 3 then return (m'.appArg!, s) + generic + +/-- The result type of a memory client. -/ +private def resultType (body : Expr) : MetaM Expr := do + let ty ← whnfR (← inferType body) + unless ty.isAppOfArity ``PFunctor.FreeM 2 do + throwError "realize: expected a memory client, got{indentExpr body}\nof type{indentExpr ty}" + whnf ty.appArg! + +/-- The encoding of an intermediate answer type. -/ +private def encodingOf (α : Expr) : MetaM Expr := do + let some inst ← synthInstance? (← mkAppM ``WordEncoding #[α]) + | throwError "realize: no `WordEncoding` instance for the answer type{indentExpr α}" + let e ← mkAppOptM ``WordEncoding.encode #[α, inst] + return (← unfoldProjInst? e).getD e + +/-- `Bind.bind (pure a) cont` in `Memory.Prog`. -/ +private def bindPure (a cont : Expr) : MetaM Expr := do + let prog := mkConst ``Memory.Prog + let pa ← mkAppOptM ``Pure.pure #[prog, none, mkConst ``Nat, a] + mkAppOptM ``Bind.bind #[prog, none, mkConst ``Nat, none, pa, cont] + +/-- Prove `∀ d i, output (f d) i = g i (input d)` coordinate by coordinate. -/ +private def proveResult (ty : Expr) : TermElabM Expr := do + let mvar ← mkFreshExprMVar ty + let gs ← Tactic.run mvar.mvarId! do + evalTactic (← `(tactic| (intro d i; fin_cases i <;> rfl))) + unless gs.isEmpty do throwError "realize: could not match the result encoding:{indentExpr ty}" + instantiateMVars mvar + +/-- Certify `pure a` with the result encoding `out`, for the environment variable `d`. -/ +private def realizePure (ctx : Ctx) (env : Env) (d : Expr) (atoms : Array Expr) (a out : Expr) : + TermElabM Out := do + let l ← encodingWidth out + let coordsU ← coordinates out a l + let gnodes ← coordsU.mapM fun c => liftMetaM (analyze atoms d c) + unless ctx.exact || ctx.standard || gnodes.all (fun g => !(g matches .bin ..)) do + throwError "realize: word arithmetic is certified in the exact and standard bases; the \ + goal's basis is{indentExpr ctx.basis}" + let k := env.k + let kE := mkNatLit k + let B := ctx.basis + let nat := mkConst ``Nat + let finK := mkApp (mkConst ``Fin) kE + let finL := mkApp (mkConst ``Fin) (mkNatLit l) + let maxFn ← natMax + withLocalDeclD `v (← mkArrow finK nat) fun v => do + let (anodes, st) ← (gnodes.mapM (annotate ctx.exact k v)).run {} + let uvals := gnodes.foldl (fun acc g => userVals g acc) #[] + let domTy := conj st.domV.toList + let P ← mkLambdaFVars #[v] domTy + let bound ← mkLambdaFVars #[v] + (if st.vals.isEmpty then mkNatLit 0 else maxChain maxFn st.vals) + let hb (idx : Nat) : MetaM Expr := withLocalDeclD `hv domTy fun hv => do + mkLambdaFVars #[v, hv] (← memProof maxFn st.vals idx) + let hd (di : Nat) : MetaM Expr := withLocalDeclD `hv domTy fun hv => do + mkLambdaFVars #[v, hv] (← conjunct hv st.domV.size di) + let proofs ← anodes.mapM fun n => liftMetaM (glueProof ctx.exact B kE P bound k hb hd n) + let vals := anodes.map (·.val) + let gfun ← withLocalDeclD `i finL fun i => do + mkLambdaFVars #[i, v] (mkApp (← vecOf vals.toList) i) + let hcoord ← if l == 0 then + withLocalDeclD `i finL fun i => do + let ty ← mkAppM ``GlueComputable #[B, mkApp gfun i, P, bound] + mkLambdaFVars #[i] (← mkAppOptM ``Fin.elim0 #[ty, i]) + else vecCoords B kE P bound v vals proofs 0 + withLocalDeclD `w nat fun w => do + let needIdx := anodes.foldl (fun acc n => needNodes ctx.exact n false acc) #[] + let needV ← needIdx.mapM fun i => mkAppM ``Fits #[w, st.vals[i]!] + let needU ← needIdx.mapM fun i => mkAppM ``Fits #[w, uvals[i]!] + let needTy := conj needV.toList + let need ← mkLambdaFVars #[w, v] needTy + let hregsTy ← withLocalDeclD `i finK fun i => do + mkForallFVars #[i] (← mkAppM ``Fits #[w, mkApp v i]) + let hneed ← withLocalDeclD `hregs hregsTy fun hregs => + withLocalDeclD `hP domTy fun hP => withLocalDeclD `hn needTy fun hn => do + let fits ← if st.vals.isEmpty then pure #[← mkAppM ``fits_zero #[w]] else do + let (_, fits) ← (anodes.forM fun n => discard <| + fitProofs ctx.exact k w hregs hP hn st.domV.size needIdx n none).run + (Array.replicate st.vals.size default) + pure fits + mkLambdaFVars #[w, v, hregs, hP, hn] (← fitChain fits) + let f ← mkLambdaFVars #[d] a + let resTy ← withLocalDeclD `d env.ty fun d' => withLocalDeclD `i finL fun i => do + let lhs := mkApp (← mkAppM ``DFunLike.coe #[out, f.beta #[d']]) i + let rhs := mkApp2 gfun i (← mkAppM ``DFunLike.coe #[env.input, d']) + mkForallFVars #[d', i] (← mkEq lhs rhs) + let hresult ← proveResult resTy + let proof ← mkAppM ``Realizes.pure₀ + #[env.input, out, f, gfun, need, hresult, hcoord, hneed] + let safe ← withLocalDeclD `s (mkConst ``Memory.State) fun s => + mkLambdaFVars #[w, d, s] (mkAnd (conj st.domU.toList) (conj needU.toList)) + return { proof, output := out, safe } + +/-- The output encoding of a certificate. -/ +private def outputOf (proof : Expr) : MetaM Expr := do + let ty ← instantiateMVars (← inferType proof) + unless ty.isAppOfArity ``Realizes 10 do throwError "realize: internal error, not a certificate" + return ty.getArg! 7 + +/-- Check a query's output encoding against the required one. -/ +private def checkOutput (proof : Expr) (output? : Option Expr) : MetaM Expr := do + let out ← outputOf proof + if let some o := output? then + unless ← isDefEq out o do + throwError "realize: the query returns its answer encoded by{indentExpr out}\n\ + but the goal requires{indentExpr o}" + return out + +/-- The comparison of a branch condition: equality or `<`, with its operands. -/ +private def parseCond (c : Expr) : MetaM (Name × Expr × Expr) := do + if let some (ty, a, b) := c.eq? then + if (← whnfR ty).isConstOf ``Nat then return (``Cond.eq, a, b) + if c.isAppOfArity ``LT.lt 4 && (c.getArg! 0).isConstOf ``Nat then + return (``Cond.lt, c.getArg! 2, c.getArg! 3) + if c.isAppOfArity ``GT.gt 4 && (c.getArg! 0).isConstOf ``Nat then + return (``Cond.lt, c.getArg! 3, c.getArg! 2) + throwError "realize: a branch condition must be `a = b`, `a < b` or `a > b` on words, \ + got{indentExpr c}" + +/-- Rebuild a branch condition with new operands. -/ +private def rebuildCond (c a b : Expr) : MetaM Expr := do + if c.isEq then mkEq a b + else if c.isAppOfArity ``LT.lt 4 then mkAppM ``LT.lt #[a, b] + else mkAppM ``GT.gt #[b, a] + +/-- Whether the head is a client form `realize` handles directly. -/ +private def isClientForm (e : Expr) : Bool := + e.isAppOfArity ``Bind.bind 6 || e.isAppOfArity ``PFunctor.FreeM.bind 5 || + e.isAppOfArity ``Pure.pure 4 || e.isAppOfArity ``PFunctor.FreeM.pure 3 || + e.isAppOfArity ``PFunctor.FreeM.lift 2 || e.isAppOfArity ``PFunctor.FreeM.liftBind 4 || + e.isAppOfArity ``ite 5 + +/-- Unfold definitions until a client form appears. -/ +private partial def normBody (e : Expr) (fuel : Nat := 64) : MetaM Expr := do + let e ← whnfR e + if isClientForm e || fuel == 0 then return e + if let some e' ← unfoldDefinition? e then return ← normBody e'.headBeta (fuel - 1) + return e + +/-- An optional answer that a `match` in the client inspects: a discriminant of an `Option` type +that depends on the environment variable `d`. -/ +private def optionDiscr? (d body : Expr) : MetaM (Option Expr) := do + let lctx ← getLCtx + let found ← IO.mkRef (none : Option Expr) + let _ ← Meta.transform body (pre := fun e => do + if (← found.get).isSome then return .done e + if let some app ← matchMatcherApp? e (alsoCasesOn := true) then + for r in app.discrs do + if r.containsFVar d.fvarId! && + (r.find? fun x => x.isFVar && !lctx.contains x.fvarId!).isNone then + if (← whnfR (← inferType r)).isAppOfArity ``Option 1 then + found.set (some r) + return .done e + return .continue) + found.get + +/-- Prove `∀ d, rhs = body` by a case split on the optional answer `r`. -/ +private def proveSplit (d r ty : Expr) : MetaM Expr := do + let mvar ← mkFreshExprMVar ty + let (dfv, g) ← mvar.mvarId!.intro1 + let r' := r.replaceFVar d (mkFVar dfv) + let (zs, g) ← g.generalize #[{ expr := r', xName? := `z }] + for sg in ← g.cases zs[0]! do sg.mvarId.refl + instantiateMVars mvar + +mutual + +/-- Certify a client `prog : δ → Memory.Prog α` in the environment `env`. -/ +private partial def realizeProg (ctx : Ctx) (env : Env) (prog : Expr) (output? : Option Expr) : + TermElabM Out := + withLocalDeclD `d env.ty fun d => do + let atoms := env.atoms.map (·.beta #[d]) + let body ← normBody (prog.beta #[d]).headBeta + realizeBody ctx env d atoms body output? + +/-- Certify the body of a client for the environment variable `d`. -/ +private partial def realizeBody (ctx : Ctx) (env : Env) (d : Expr) (atoms : Array Expr) + (body : Expr) (output? : Option Expr) : TermElabM Out := do + let k := env.k + -- sequencing + let bind? : Option (Expr × Expr) := + if body.isAppOfArity ``Bind.bind 6 then some (body.getArg! 4, body.getArg! 5) + else if body.isAppOfArity ``PFunctor.FreeM.bind 5 then some (body.getArg! 3, body.getArg! 4) + else none + if let some (m, f) := bind? then return ← realizeBind ctx env d m f output? + if body.isAppOfArity ``PFunctor.FreeM.liftBind 4 then + let m ← mkAppOptM ``PFunctor.FreeM.lift #[body.getArg! 0, body.getArg! 2] + return ← realizeBind ctx env d m (body.getArg! 3) output? + -- return + let pure? : Option Expr := + if body.isAppOfArity ``Pure.pure 4 then some body.appArg! + else if body.isAppOfArity ``PFunctor.FreeM.pure 3 then some body.appArg! + else none + if let some a := pure? then + let out ← match output? with + | some o => pure o + | none => encodingOf (← resultType body) + return ← realizePure ctx env d atoms a out + -- memory queries + if body.isAppOfArity ``PFunctor.FreeM.lift 2 then + let op ← whnfR body.appArg! + let operands := op.getAppArgs + for h : j in [0:operands.size] do + if (← findAtom atoms operands[j]).isNone then + let rebuilt ← withLocalDeclD `r (mkConst ``Nat) fun r => do + mkLambdaFVars #[r] (mkApp2 (mkConst ``PFunctor.FreeM.lift + body.getAppFn.constLevels!) (body.getArg! 0) (mkAppN op.getAppFn (operands.set j r))) + return ← realizeBody ctx env d atoms (← bindPure operands[j] rebuilt) output? + let idx ← operands.mapM fun a => do + let some i ← findAtom atoms a | throwError "realize: internal error" + finLit k i + let input := env.input + let kE := mkNatLit k + let proof ← match op.getAppFn.constName? with + | some ``Memory.Op.read => + mkAppOptM ``Realizes.read #[ctx.basis, env.ty, kE, input, idx[0]!] + | some ``Memory.Op.write => + mkAppOptM ``Realizes.write #[ctx.basis, env.ty, kE, input, idx[0]!, idx[1]!] + | some ``Memory.Op.alloc => + unless ctx.exact do + throwError "realize: allocation is certified in the exact basis;\n\ + realize there and transfer with `Realizes.mapSimulates`" + mkAppOptM ``Realizes.alloc #[env.ty, kE, input, idx[0]!] + | some ``Memory.Op.allocDirty => + unless ctx.exact do + throwError "realize: allocation is certified in the exact basis;\n\ + realize there and transfer with `Realizes.mapSimulates`" + mkAppOptM ``Realizes.allocDirty #[env.ty, kE, input, idx[0]!] + | _ => throwError "realize: unsupported memory operation{indentExpr op}" + let output ← checkOutput proof output? + return { proof, output, safe := ← trueSafe env.ty } + -- branching + if body.isAppOfArity ``ite 5 then + let c := body.getArg! 1 + let inst := body.getArg! 2 + let t := body.getArg! 3 + let e := body.getArg! 4 + let (kind, a, b) ← parseCond c + let ia? ← findAtom atoms a + let ib? ← findAtom atoms b + let (some ia, some ib) := (ia?, ib?) | do + let x := if ia?.isNone then a else b + let rebuilt ← withLocalDeclD `r (mkConst ``Nat) fun r => do + let c' ← if ia?.isNone then rebuildCond c r b else rebuildCond c a r + mkLambdaFVars #[r] (← mkAppM ``ite #[c', t, e]) + return ← realizeBody ctx env d atoms (← bindPure x rebuilt) output? + let out ← match output? with + | some o => pure o + | none => encodingOf (← resultType body) + let hp ← realizeProg ctx env (← mkLambdaFVars #[d] t) (some out) + let hn ← realizeProg ctx env (← mkLambdaFVars #[d] e) (some out) + let cond ← mkAppM kind #[← finLit k ia, ← finLit k ib] + let proof ← mkAppM ``Realizes.branch₀ #[cond, hp.proof, hn.proof] + let safe ← withLocalDeclD `w (mkConst ``Nat) fun w => + withLocalDeclD `s (mkConst ``Memory.State) fun s => do + mkLambdaFVars #[w, d, s] (mkApp5 (mkConst ``ite [Level.one]) (mkSort Level.zero) c inst + (hp.safe.beta #[w, d, s]) (hn.safe.beta #[w, d, s])) + return { proof, output := out, safe } + -- a `match` on an optional answer becomes a test of its presence tag + let body ← Core.betaReduce body + if let some r ← optionDiscr? d body then + return ← realizeOptionSplit ctx env d atoms body r output? + throwError "realize: unsupported client form{indentExpr body}\n\ + Expected `bind`, `pure`, a memory query, `if` on a word comparison,\n\ + or a `match` on an optional answer. Recursion on a runtime value is a loop\n\ + and needs a loop rule." + +/-- Certify a client that matches on the optional answer `r`: it equals the branch on `r`'s +presence tag, with `r` replaced by `none` and by `some` of its payload. -/ +private partial def realizeOptionSplit (ctx : Ctx) (env : Env) (d : Expr) (atoms : Array Expr) + (body r : Expr) (output? : Option Expr) : TermElabM Out := do + let rTy ← whnfR (← inferType r) + let enc ← encodingOf rTy + let words ← mkAppM ``DFunLike.coe #[enc, r] + let l ← encodingWidth enc + unless l == 2 do throwError "realize: expected a tag and payload encoding for{indentExpr r}" + let tag := mkApp words (← finLit 2 0) + let payload := mkApp words (← finLit 2 1) + let cond ← mkEq tag (← mkNumeral (mkConst ``Nat) 0) + let noneE ← mkAppOptM ``Option.none #[rTy.appArg!] + let someE ← mkAppM ``Option.some #[payload] + let bodyNone := body.replace fun e => if e == r then some noneE else none + let bodySome := body.replace fun e => if e == r then some someE else none + let rhs ← mkAppM ``ite #[cond, bodyNone, bodySome] + let eq ← proveSplit d r (← mkForallFVars #[d] (← mkEq rhs body)) + let out ← realizeBody ctx env d atoms rhs output? + let stmt ← mkAppM ``Realizes #[ctx.basis, ← mkLambdaFVars #[d] rhs, env.input, out.output, + out.safe, ← zeroWork env.ty] + let proof ← mkAppM ``Realizes.congr #[← mkExpectedTypeHint out.proof stmt, eq] + return { out with proof } + +/-- Certify `bind m f`: the first client, then the continuation over the extended environment. -/ +private partial def realizeBind (ctx : Ctx) (env : Env) (d m f : Expr) (output? : Option Expr) : + TermElabM Out := do + let α ← resultType m + let hP ← realizeProg ctx env (← mkLambdaFVars #[d] m) none + let l ← encodingWidth hP.output + let env' ← extend env α hP.output l + let fd ← mkLambdaFVars #[d] f + let q ← withLocalDeclD `x env'.ty fun x => do + let x1 ← mkAppM ``Prod.fst #[x] + let x2 ← mkAppM ``Prod.snd #[x] + mkLambdaFVars #[x] (fd.beta #[x1, x2]).headBeta + let hQ ← realizeProg ctx env' q output? + let proof ← mkAppM ``Realizes.bind₀ #[hP.proof, hQ.proof] + let safe ← withLocalDeclD `w (mkConst ``Nat) fun w => + withLocalDeclD `s (mkConst ``Memory.State) fun s => do + let (r, s') ← replyState m s + let pair ← mkAppM ``Prod.mk #[d, r] + let sq ← reducePairs (hQ.safe.beta #[w, pair, s']) + mkLambdaFVars #[w, d, s] (mkAnd (hP.safe.beta #[w, d, s]) sq) + return { proof, output := hQ.output, safe } + +end + +/-- Split the conjunctions and `if`s of a synthesized domain into separate goals. -/ +private partial def splitDomain (g : MVarId) : MetaM (List MVarId) := g.withContext do + let t ← instantiateMVars (← g.getType) + if t.isConstOf ``True then + g.assign (mkConst ``True.intro) + return [] + if t.isAppOfArity ``And 2 then + let [g₁, g₂] ← g.apply (mkConst ``And.intro) | throwError "realize: internal split error" + return (← splitDomain g₁) ++ (← splitDomain g₂) + if t.isAppOfArity ``ite 5 then + let c := t.getArg! 1 + let (gpos, gneg) ← g.byCases c `hc + let pos ← gpos.mvarId.withContext do + let eq ← mkAppOptM ``if_pos + #[c, t.getArg! 2, mkFVar gpos.fvarId, t.getArg! 0, t.getArg! 3, t.getArg! 4] + gpos.mvarId.replaceTargetEq (t.getArg! 3) eq + let neg ← gneg.mvarId.withContext do + let eq ← mkAppOptM ``if_neg + #[c, t.getArg! 2, mkFVar gneg.fvarId, t.getArg! 0, t.getArg! 3, t.getArg! 4] + gneg.mvarId.replaceTargetEq (t.getArg! 4) eq + return (← splitDomain pos) ++ (← splitDomain neg) + return [g] + +end Cslib.Tactic.Realize + +open Lean Meta Elab Tactic Cslib.WordRAM Cslib.WordRAM.Realization Cslib.Tactic.Realize in +/-- Prove `Realizes B p input output safe work` for a loop-free memory client `p`, leaving the +genuine fit and domain conditions as goals in the context `w d s hwords hmem h`. -/ +elab "realize" : tactic => withMainContext do + let goal ← getMainGoal + let target ← instantiateMVars (← whnfR (← goal.getType)) + unless target.isAppOfArity ``Realizes 10 do + throwError "realize: the goal is not a `Realizes` certificate:{indentExpr target}" + let args := target.getAppArgs + let δ := args[2]! + let B := args[4]! + let p := args[5]! + let input := args[6]! + let output := args[7]! + let k ← encodingWidth input + let exact ← withNewMCtxDepth (isDefEq B (mkConst ``Cslib.WordRAM.Basis.exact)) + let standard ← withNewMCtxDepth (isDefEq B (mkConst ``Cslib.WordRAM.Basis.standard)) + let atoms ← withLocalDeclD `d δ fun d => do + (← encodingAtoms input d k).mapM (mkLambdaFVars #[d] ·) + let env : Env := { ty := δ, k, input, atoms } + let out ← realizeProg { basis := B, exact, standard } env p (some output) + let zero ← zeroWork δ + let stmt ← mkAppM ``Realizes #[B, p, input, output, out.safe, zero] + unless ← isDefEq (← inferType out.proof) stmt do + throwError "realize: internal error, the certificate does not match the client{indentExpr p}" + let h ← mkExpectedTypeHint out.proof stmt + let cons ← mkAppOptM ``Realizes.consequence #[B, args[0]!, args[1]!, δ, args[3]!, p, input, + output, out.safe, zero, args[8]!, args[9]!, h] + let [hsafe, hwork] ← goal.apply cons | throwError "realize: internal error in `consequence`" + setGoals [hwork] + evalTactic (← `(tactic| exact fun _ _ => Nat.zero_le _)) + let (_, g) ← hsafe.introN 6 [`w, `d, `s, `hwords, `hmem, `h] + let g ← g.withContext do + let t ← instantiateMVars (← g.getType) + let g ← g.replaceTargetDefEq (← Core.betaReduce t) + let hdecl := (← getLCtx).lastDecl.get! + g.replaceLocalDeclDefEq hdecl.fvarId (← Core.betaReduce hdecl.type) + setGoals [g] + evalTactic (← `(tactic| try simp only [true_and, and_true, and_self, Nat.add_zero])) + let gs ← getGoals + let gs ← gs.flatMapM fun g => liftMetaM (splitDomain g) + -- A condition repeated in the same context is proved once. + let mut kept : Array MVarId := #[] + for g in gs do + let t ← instantiateMVars (← g.getType) + let lctx := (← g.getDecl).lctx + let dup? ← kept.findM? fun g' => do + return (← g'.getDecl).lctx.decls.size == lctx.decls.size && + (← instantiateMVars (← g'.getType)) == t + match dup? with + | some g' => g.assign (mkMVar g') + | none => kept := kept.push g + setGoals kept.toList diff --git a/ComplexityTests/WordRAMRealize.lean b/ComplexityTests/WordRAMRealize.lean new file mode 100644 index 0000000000..71cf0852aa --- /dev/null +++ b/ComplexityTests/WordRAMRealize.lean @@ -0,0 +1,168 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.WordRAM.Realization.Client.MemoryStack +import Complexity.DataStructure.Queue.Memory +import Complexity.Tactic.Realize + +/-! `realize` on the memory-oracle stack and queue. + +The three stack statements are exactly those of `Realization.MemoryStack` (#2246), including +their local-work gradings; `realize` replaces the hand-assembled bind trees and leaves only the +fit and domain conditions. The queue's enqueue runs through `link` and the pair of stacks. The +failure tests pin the messages for unsupported forms. -/ + +namespace ComplexityTests.WordRAMRealize + +open Cslib.DataStructure Cslib.WordRAM Cslib.WordRAM.Realization +open MemoryStack + +/-! ## The stack methods -/ + +example : Realizes .exact Stack.Memory.size layoutWords + (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding (fun _ _ _ => True) (fun _ _ => 0) := by + realize + +example : Realizes .exact (fun d : Stack.Memory.Layout × ℕ => Stack.Memory.push d.1 d.2) + pushWords (Equiv.ofUnique Unit (Fin 0 → ℕ)).toEmbedding + (fun w d s => d.1.Valid w d.1.items.length ∧ Stack.Memory.Inv d.1 s ∧ + s.mem d.1.count.base < d.1.items.length) (fun _ _ => 10) := by + realize + · exact hmem.fits_of_lt_frontier (h.2.1.item_lt h.2.2) + · exact h.1.capacity_fits.of_le (by omega) + +example : Realizes .exact Stack.Memory.pop layoutWords optionWords + (fun w L s => L.Valid w L.items.length ∧ Stack.Memory.Inv L s) (fun _ _ => 19) := by + realize + · omega + · exact hmem.fits_of_lt_frontier (h.2.item_lt (by have := h.2.count_le; omega)) + · exact h.1.count_length ▸ hwords 1 + +/-! ## The queue's enqueue, through `link` and the pair of stacks -/ + +/-- Both stack layouts, front first. -/ +def queueWords : Stack.Memory.Layout × Stack.Memory.Layout ↪ (Fin 8 → ℕ) := + (layoutWords.prodMap layoutWords).trans (Fin.appendEquiv 4 4).toEmbedding + +/-- Both layouts and the enqueued value. -/ +def enqueueWords : (Stack.Memory.Layout × Stack.Memory.Layout) × ℕ ↪ (Fin 9 → ℕ) := + (queueWords.prodMap (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding).trans + (Fin.appendEquiv 8 1).toEmbedding + +example (w₀ C : ℕ) : Realizes .exact + (fun d : (Stack.Memory.Layout × Stack.Memory.Layout) × ℕ => + (Queue.Memory.memMethods d.1.1 d.1.2 w₀ C (.enqueue d.2) : Memory.Prog Unit)) + enqueueWords (Equiv.ofUnique Unit (Fin 0 → ℕ)).toEmbedding + (fun w d s => d.1.2.Valid w d.1.2.items.length ∧ Stack.Memory.Inv d.1.2 s ∧ + s.mem d.1.2.count.base < d.1.2.items.length) (fun _ _ => 0) := by + realize + · exact hmem.fits_of_lt_frontier (h.2.1.item_lt h.2.2) + · exact h.1.capacity_fits.of_le (by omega) + +/-! ## A `match` on an optional answer -/ + +/-- Pop and return the successor of the top, or zero for an empty stack. -/ +def popSucc (L : Stack.Memory.Layout) : Memory.Prog ℕ := do + match ← Stack.Memory.pop L with + | none => pure 0 + | some v => pure (v + 1) + +/-- The `none` branch needs nothing; the `some` branch needs the successor to fit. -/ +example : Realizes .exact popSucc layoutWords (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + (fun w L s => L.Valid w L.items.length ∧ Stack.Memory.Inv L s ∧ + ∀ a < s.mem L.count.base, Fits w (s.mem (L.items.base + a) + 1)) (fun _ _ => 0) := by + realize + · omega + · exact hmem.fits_of_lt_frontier (h.2.1.item_lt (by have := h.2.1.count_le; omega)) + · exact h.1.count_length ▸ hwords 1 + · by_cases hz : s.mem d.count.base = 0 + · simp [StateTransitionSpec.clientResult, Stack.Memory.runClient_pop_of_eq_zero _ s hz] at hc + · simpa [StateTransitionSpec.clientResult, Stack.Memory.runClient_pop_of_pos _ s hz] using + h.2.2 _ (Nat.sub_lt (Nat.pos_of_ne_zero hz) Nat.one_pos) + +/-! ## The standard basis and computed addresses -/ + +/-- Read the cell after a computed address, then store a product there. -/ +def scaled (d : ℕ × ℕ) : Memory.Prog Unit := do + let x ← Memory.read (d.1 + 1) + Memory.write (d.1 + 1) (x * d.2 - d.2) + +/-- Standard-basis subtraction is total, so only the fit of the sum and the product remain. -/ +example : Realizes .standard scaled + ((Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding.prodMap + (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding |>.trans (Fin.appendEquiv 1 1).toEmbedding) + (Equiv.ofUnique Unit (Fin 0 → ℕ)).toEmbedding + (fun w d s => Fits w (d.1 + 1) ∧ Fits w (s.mem (d.1 + 1) * d.2)) (fun _ _ => 0) := by + realize + · exact h.1 + · exact h.2 + +/-! ## Failure messages -/ + +/-- A power has no glue rule. -/ +def square (n : ℕ) : Memory.Prog ℕ := pure (n ^ 2) + +/-- +error: realize: cannot compute + d ^ 2 +from the live words + d +-/ +#guard_msgs in +example : Realizes .exact square (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding (fun _ _ _ => True) (fun _ _ => 0) := by + realize + +/-- A non-strict comparison is not a native test. -/ +def atMost (n : ℕ) : Memory.Prog ℕ := if n ≤ 3 then pure 0 else pure 1 + +/-- +error: realize: a branch condition must be `a = b`, `a < b` or `a > b` on words, got + d ≤ 3 +-/ +#guard_msgs in +example : Realizes .exact atMost (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding (fun _ _ _ => True) (fun _ _ => 0) := by + realize + +/-- Recursion on a runtime value is a loop. -/ +def readMany : ℕ → Memory.Prog Unit + | 0 => pure () + | n + 1 => do let _ ← Memory.read 0; readMany n + +/-- +error: realize: unsupported client form + readMany d +Expected `bind`, `pure`, a memory query, `if` on a word comparison, +or a `match` on an optional answer. Recursion on a runtime value is a loop +and needs a loop rule. +-/ +#guard_msgs in +example : Realizes .exact readMany (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + (Equiv.ofUnique Unit (Fin 0 → ℕ)).toEmbedding (fun _ _ _ => True) (fun _ _ => 0) := by + realize + +/-- Allocation is certified in the exact basis. -/ +def fresh (n : ℕ) : Memory.Prog ℕ := Memory.alloc n + +/-- +error: realize: allocation is certified in the exact basis; +realize there and transfer with `Realizes.mapSimulates` +-/ +#guard_msgs in +example : Realizes .standard fresh (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding (fun _ _ _ => True) (fun _ _ => 0) := by + realize + +/-- +error: realize: the goal is not a `Realizes` certificate: + True +-/ +#guard_msgs in +example : True := by + realize + +end ComplexityTests.WordRAMRealize From 141d112dca468406c10b212b9fa7cc9927bf995a Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 21:25:05 -0500 Subject: [PATCH 21/27] feat(WordRAM): loops whose body issues a query in realize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generalize `Realizes.mono_mul` to local work dominated by the memory charge, and add `Realizes.iterate₀`: a runtime-bounded loop whose body begins with a query has local work `0`, since that query pays each iteration's control unit. `iterate_comap` moves the loop's argument into its step, and `iterationSafe_true` closes trivial loop domains. `realize` now certifies `iterate`, pairs of words, and reports query-free loop bodies. Refs #2205. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../WordRAM/Realization/Absorb.lean | 98 ++++++++++++--- Complexity/Tactic/Realize.lean | 119 ++++++++++++++---- ComplexityTests/WordRAMRealize.lean | 48 ++++++- 3 files changed, 221 insertions(+), 44 deletions(-) diff --git a/Complexity/Computability/WordRAM/Realization/Absorb.lean b/Complexity/Computability/WordRAM/Realization/Absorb.lean index 113b8aa708..cea70b77b0 100644 --- a/Complexity/Computability/WordRAM/Realization/Absorb.lean +++ b/Complexity/Computability/WordRAM/Realization/Absorb.lean @@ -10,20 +10,22 @@ module public import Complexity.Computability.WordRAM.Realization.Bind public import Complexity.Computability.WordRAM.Realization.Branch public import Complexity.Computability.WordRAM.Realization.GlueVector +public import Complexity.Computability.WordRAM.Realization.Loop public import Complexity.Computability.WordRAM.Realization.Primitive /-! -# Realization rules with absorbed constant local work +# Realization rules with absorbed local work `Realizes` bounds native instructions by `C * (memory charge + local work + 1)` with one -existential exchange factor `C`. Local work bounded by a constant multiple of another grading can -therefore be traded for a larger factor (`Realizes.mono_mul`). In particular, a loop-free -composition of glue, binds and branches has the same certificate with local work `0`: its native -cost is `O(memory charge + 1)`. Loop work grows with the iteration count, so loops keep their +existential exchange factor `C`. Local work within a constant multiple of the memory charge plus +another grading can therefore be traded for a larger factor (`Realizes.mono_mul`). In particular, +a composition of glue, binds and branches has the same certificate with local work `0`: its native +cost is `O(memory charge + 1)`. So does a runtime-bounded loop whose body begins with a query, +because that query's charge pays each iteration's control unit; a query-free loop keeps its graded charge. -The rules below are the C2 rules `bind`, `branch` and `glueVector` with that absorption applied. -They are the target shapes of the `realize` tactic. Scalar and finite-vector glue takes +The rules below are the C2 rules `bind`, `branch`, `glueVector` and `iterateRuntime` with that +absorption applied. They are the target shapes of the `realize` tactic. Glue takes `GlueComputable` certificates, whose instruction budgets stay hidden. ## Main definitions @@ -32,11 +34,13 @@ They are the target shapes of the `realize` tactic. Scalar and finite-vector glu ## Main results -* `Realizes.mono_mul`: absorb a local-work grading dominated by a constant multiple. +* `Realizes.mono_mul`: absorb local work dominated by a constant multiple of the memory charge + plus another grading. * `Realizes.congr`: transport a certificate along pointwise equal programs. * `Realizes.bind₀`, `Realizes.branch₀`: sequencing and branching with absorbed control work. * `Realizes.pure₀`: finite-result glue from `GlueComputable` coordinates, with a visible domain and an explicit word-fit obligation. +* `Realizes.iterate₀`, `iterate_comap`: loops whose body begins with a query. These are derived rules over the existing realization kernel; no execution semantics or certificate is added. @@ -64,6 +68,11 @@ instance : WordEncoding (Option ℕ) := ⟨2, optionWords⟩ instance : WordEncoding Bool := ⟨2, boolWords⟩ +/-- A pair is encoded by its two encodings, first component first. -/ +instance {α β : Type} [WordEncoding α] [WordEncoding β] : WordEncoding (α × β) := + ⟨_, ((WordEncoding.encode (α := α)).prodMap (WordEncoding.encode (α := β))).trans + (Fin.appendEquiv _ _).toEmbedding⟩ + variable {B : Basis} {δ α β : Type} {k l m : ℕ} namespace Realizes @@ -71,10 +80,10 @@ namespace Realizes variable {p : δ → Memory.Prog α} {input : δ ↪ (Fin k → ℕ)} {output : α ↪ (Fin l → ℕ)} {safe : ℕ → δ → Memory.State → Prop} {work work' : δ → Memory.State → ℕ} -/-- A local-work grading within a constant multiple of another is absorbed by the exchange -factor. The new factor is `C * (K + 1)`; the emitter and scratch are unchanged. -/ +/-- Local work within a constant multiple of the memory charge plus another grading is absorbed +by the exchange factor. The new factor is `C * (K + 1)`; the emitter and scratch are unchanged. -/ theorem mono_mul (h : Realizes B p input output safe work) (K : ℕ) - (hwork : ∀ d s, work d s ≤ K * (work' d s + 1)) : + (hwork : ∀ d s, work d s ≤ K * (memoryCost (p d) s + work' d s + 1)) : Realizes B p input output safe work' := by obtain ⟨n, C, emit, he⟩ := h refine ⟨n, C * (K + 1), emit, fun frontier src tmp hi hd hf hs => ?_⟩ @@ -87,8 +96,7 @@ theorem mono_mul (h : Realizes B p input output safe work) (K : ℕ) apply Nat.mul_le_mul_left have hexpand : (K + 1) * (memoryCost (p d) (memoryState frontier x) + work' d (memoryState frontier x) + 1) = - K * (work' d (memoryState frontier x) + 1) + - K * memoryCost (p d) (memoryState frontier x) + + K * (memoryCost (p d) (memoryState frontier x) + work' d (memoryState frontier x) + 1) + (memoryCost (p d) (memoryState frontier x) + work' d (memoryState frontier x) + 1) := by ring omega @@ -109,7 +117,7 @@ theorem bind₀ (hP : Realizes B p input answer safeP (fun _ _ => 0)) (fun w d s => safeP w d s ∧ safeQ w (d, (Memory.spec 0).clientResult s (p d)) ((Memory.spec 0).clientState s (p d))) (fun _ _ => 0) := - (hP.bind hQ).mono_mul 1 (fun _ _ => le_refl _) + (hP.bind hQ).mono_mul 1 (fun _ _ => by omega) /-- Branching with the test and jump absorbed. Only the selected alternative's domain is required. -/ @@ -118,7 +126,7 @@ theorem branch₀ {pos neg : δ → Memory.Prog α} {safePos safeNeg : ℕ → (hn : Realizes B neg input output safeNeg (fun _ _ => 0)) : Realizes B (fun d => if test c (input d) then pos d else neg d) input output (fun w d s => if test c (input d) then safePos w d s else safeNeg w d s) (fun _ _ => 0) := - (branch c hp hn).mono_mul 2 (fun _ _ => by split <;> simp) + (branch c hp hn).mono_mul 2 (fun _ _ => by split <;> omega) /-- Return a computed finite result. Each coordinate has a `GlueComputable` certificate over the live words with a common domain `P` and value envelope `bound`; `need` is any condition that, @@ -135,7 +143,65 @@ theorem pure₀ (input : δ ↪ (Fin k → ℕ)) (output : α ↪ (Fin l → ℕ refine ((glueVector input output f g (fun _ => bound) time hresult htime).consequence (fun w d _ hwords _ h => ⟨h.1, fun _ => hneed w _ hwords h.1 h.2⟩) (fun _ _ => le_rfl)).mono_mul ((∑ i, time i) + l) (fun _ _ => ?_) - simp + exact Nat.le_mul_of_pos_right _ (by omega) + +end Realizes + +/-! ### Loops whose body issues a query -/ + +/-- A client that begins with a query has memory charge at least one. -/ +theorem one_le_memoryCost_liftBind (o : Memory.Op) (k : o.Result → Memory.Prog α) + (s : Memory.State) : 1 ≤ memoryCost (PFunctor.FreeM.liftBind o k) s := by + change 1 ≤ Memory.charge s o + _ + cases o <;> simp only [Memory.charge] <;> omega + +/-- When every body issues a query, the loop's control work is at most its memory charge. -/ +theorem iterationWork_zero_le {σ : Type} (body : δ × σ → Memory.Prog σ) + (hquery : ∀ x s, 1 ≤ memoryCost (body x) s) (d : δ) (n : ℕ) (a : σ) (s : Memory.State) : + iterationWork body (fun _ _ => 0) d n a s ≤ memoryCost (iterate body d n a) s := by + induction n generalizing a s with + | zero => simp [iterationWork] + | succ n ih => + have h := iteration_charge_succ body (fun _ _ => 0) d n a s + have h₁ := hquery (d, a) s + have h₂ := ih ((Memory.spec 0).clientResult s (body (d, a))) + ((Memory.spec 0).clientState s (body (d, a))) + simp only [iterationWork] at h ⊢ + omega + +/-- A loop may receive any function of its environment: it is the loop over the whole +environment whose body applies that function. -/ +theorem iterate_comap {ε σ : Type} (g : ε → δ) (body : δ × σ → Memory.Prog σ) (e : ε) (n : ℕ) + (a : σ) : iterate body (g e) n a = iterate (fun x : ε × σ => body (g x.1, x.2)) e n a := by + induction n generalizing a with + | zero => rfl + | succ n ih => exact congrArg (fun k => (body (g e, a)).bind k) (funext ih) + +/-- With trivial body domains, every iteration's domain holds. -/ +@[simp] theorem iterationSafe_true {σ : Type} (body : δ × σ → Memory.Prog σ) (w : ℕ) (d : δ) + (n : ℕ) (a : σ) (s : Memory.State) : + iterationSafe body (fun _ _ _ => True) w d n a s := by + induction n generalizing a s with + | zero => trivial + | succ n ih => exact ⟨trivial, ih _ _⟩ + +namespace Realizes + +/-- A runtime-bounded loop whose body begins with a query has local work `0`: each iteration's +control unit is paid by that query's memory charge. -/ +theorem iterate₀ {σ : Type} (input : δ ↪ (Fin k → ℕ)) (output : σ ↪ (Fin l → ℕ)) + {body : δ × σ → Memory.Prog σ} {safe : ℕ → (δ × σ) → Memory.State → Prop} + (h : Realizes .exact body ((input.prodMap output).trans (Fin.appendEquiv k l).toEmbedding) + output safe (fun _ _ => 0)) + (hquery : ∀ x s, 1 ≤ memoryCost (body x) s) : + Realizes .exact (fun d : (δ × σ) × ℕ => iterate body d.1.1 d.2 d.1.2) + ((((input.prodMap output).trans (Fin.appendEquiv k l).toEmbedding).prodMap + (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding).trans + (Fin.appendEquiv (k + l) 1).toEmbedding) output + (fun w d s => iterationSafe body safe w d.1.1 d.2 d.1.2 s) (fun _ _ => 0) := + (iterateRuntime input output h).mono_mul 1 fun d s => by + have := iterationWork_zero_le body hquery d.1.1 d.2 d.1.2 s + omega end Realizes diff --git a/Complexity/Tactic/Realize.lean b/Complexity/Tactic/Realize.lean index e759fc086d..8b0dbebc3c 100644 --- a/Complexity/Tactic/Realize.lean +++ b/Complexity/Tactic/Realize.lean @@ -13,33 +13,41 @@ public import Mathlib.Tactic.FinCases /-! # The `realize` tactic -`realize` proves a goal `Realizes B p input output safe work` for a loop-free memory client `p`. -It walks the client syntax, unfolding definitions: `bind`, `pure`, the four memory queries and -`if` on a word equality or comparison. It keeps the argument layout itself: after each bind the -live words are the previous ones followed by the answer's encoding (`WordEncoding`), and every -operand is located among them by definitional equality. An operand that is not a live word is -first computed by an inserted `pure`, which leaves the program unchanged up to definitional -equality. - -Each `pure` is certified by a `GlueComputable` proof assembled from the closure rules of -`Complexity.Computability.WordRAM.Glue`, one rule per syntactic form: projections, constants, and -the exact- or standard-basis arithmetic and bitwise operations. Exact subtraction, division and -remainder add their definedness conditions to the visible domain. The common envelope is the -maximum of every intermediate value; `realize` proves that it fits from the fit of the live -words, from the domain, and from the fit of the values it cannot derive, which become the visible -fit conditions. A value is derived when another fitting value dominates it, for example `a - b` -and `b` by `a`, or the summands by a sum. +`realize` proves a goal `Realizes B p input output safe work` for a memory client `p`. It walks +the client syntax, unfolding definitions, with one rule per form: + +* `bind`: `Realizes.bind₀`. After each bind the live words are the previous ones followed by the + answer's encoding (`WordEncoding`); `realize` keeps this layout itself. +* `pure`: `Realizes.pure₀`, with each result word certified by a `GlueComputable` proof assembled + from the closure rules of `Complexity.Computability.WordRAM.Glue`: projections, constants, and + the exact- or standard-basis arithmetic and bitwise operations. +* the four memory queries: `Realizes.read`, `write`, `alloc` and `allocDirty`. +* `if` on `a = b`, `a < b` or `a > b`: `Realizes.branch₀`. +* `match` on an optional answer: the client equals the branch on its presence tag + (`Realizes.congr`, proved by cases). +* `iterate step D n a` whose step begins with a query: `Realizes.iterate₀`, after + `iterate_comap` moves `D` into the step. + +Operands are located among the live words by definitional equality. An operand that is not a +live word is first computed by an inserted `pure`, which leaves the program unchanged up to +definitional equality. + +Exact subtraction, division and remainder add their definedness conditions to the visible +domain. The common glue envelope is the maximum of every intermediate value; `realize` proves +that it fits from the fit of the live words, from the domain, and from the fit of the values it +cannot derive, which become the visible fit conditions. A value is derived when another fitting +value dominates it, for example `a - b` and `b` by `a`, or the summands by a sum. Local work is absorbed (`Realizes.mono_mul`): every certificate built by `realize` has local work `0`, so any local-work grading in the goal is accepted. Intermediate domains are stated in the -client's own terms, with the replies of earlier queries written as `s.mem a`, `s.frontier` and -updated memories. +client's own terms, with the replies of earlier queries written as `s.mem a`, `s.frontier`, +updated memories, or the interpreter's result of a called method; a loop contributes its +`iterationSafe` domain. After the composition, `realize` introduces `w d s hwords hmem h`: the word width, the environment, the memory, the fit of the input words, memory well-formedness, and the goal's domain. It splits the conjunctions and `if`s of the synthesized domain and leaves each remaining -condition as a goal. There is no search: every form has one rule, and an unsupported form is -reported with its subterm. +condition as a goal. There is no search: an unsupported form is reported with its subterm. ## Main definitions @@ -196,10 +204,6 @@ private inductive ANode /-- A binary operation; `dom` numbers its definedness condition, if any. -/ | bin (idx : Nat) (val : Expr) (op : GOp) (dom : Option Nat) (a b : ANode) -/-- The depth-first number. -/ -private def ANode.idx : ANode → Nat - | .reg i .. | .const i .. | .bin i .. => i - /-- The value over the word vector. -/ private def ANode.val : ANode → Expr | .reg _ v _ | .const _ v _ | .bin _ v .. => v @@ -637,6 +641,17 @@ private def proveSplit (d r ty : Expr) : MetaM Expr := do for sg in ← g.cases zs[0]! do sg.mvarId.refl instantiateMVars mvar +/-- `∀ x s, 1 ≤ memoryCost (body x) s` for a loop body that begins with a query. -/ +private def proveQuery (body ty : Expr) : MetaM Expr := + withLocalDeclD `x ty fun x => withLocalDeclD `s (mkConst ``Memory.State) fun s => do + let b ← whnf (mkApp body x) + unless b.isAppOfArity ``PFunctor.FreeM.liftBind 4 do + throwError "realize: a loop body must begin with a memory query, which pays for the \ + iteration;\nthe body begins with{indentExpr b}" + let pf ← mkAppM ``one_le_memoryCost_liftBind #[b.getArg! 2, b.getArg! 3, s] + let goal ← mkAppM ``LE.le #[mkNatLit 1, ← mkAppM ``memoryCost #[mkApp body x, s]] + mkLambdaFVars #[x, s] (← mkExpectedTypeHint pf goal) + mutual /-- Certify a client `prog : δ → Memory.Prog α` in the environment `env`. -/ @@ -730,6 +745,10 @@ private partial def realizeBody (ctx : Ctx) (env : Env) (d : Expr) (atoms : Arra mkLambdaFVars #[w, d, s] (mkApp5 (mkConst ``ite [Level.one]) (mkSort Level.zero) c inst (hp.safe.beta #[w, d, s]) (hn.safe.beta #[w, d, s])) return { proof, output := out, safe } + -- a runtime-bounded loop + if body.isAppOfArity ``iterate 6 then + return ← realizeIterate ctx env d atoms (body.getArg! 1) (body.getArg! 2) (body.getArg! 3) + (body.getArg! 4) (body.getArg! 5) output? -- a `match` on an optional answer becomes a test of its presence tag let body ← Core.betaReduce body if let some r ← optionDiscr? d body then @@ -763,6 +782,51 @@ private partial def realizeOptionSplit (ctx : Ctx) (env : Env) (d : Expr) (atoms let proof ← mkAppM ``Realizes.congr #[← mkExpectedTypeHint out.proof stmt, eq] return { out with proof } +/-- Certify `iterate step D n a`: copy the initial state and the fuel, then run the loop over the +whole environment (`iterate_comap`). The body must begin with a query (`Realizes.iterate₀`). -/ +private partial def realizeIterate (ctx : Ctx) (env : Env) (d : Expr) (atoms : Array Expr) + (σ step D n a : Expr) (output? : Option Expr) : TermElabM Out := do + unless ctx.exact do throwError "realize: loops are certified in the exact basis" + let out ← match output? with + | some o => pure o + | none => encodingOf σ + let l ← encodingWidth out + let envB ← extend env σ out l + -- the body over the whole environment + let body ← if D == d then pure step else + withLocalDeclD `x envB.ty fun x => do + let x1 ← mkAppM ``Prod.fst #[x] + let x2 ← mkAppM ``Prod.snd #[x] + let Dx := (← mkLambdaFVars #[d] D).beta #[x1] + mkLambdaFVars #[x] (mkApp step (← mkAppM ``Prod.mk #[Dx, x2])) + let hB ← realizeProg ctx envB body (some out) + let hq ← proveQuery body envB.ty + let hLoop ← mkAppM ``Realizes.iterate₀ #[env.input, out, hB.proof, hq] + -- the fuel, after the state + let hN ← withLocalDeclD `x envB.ty fun x => do + let x1 ← mkAppM ``Prod.fst #[x] + let nx := (← mkLambdaFVars #[d] n).beta #[x1] + let word ← encodingOf (mkConst ``Nat) + let atomsX := envB.atoms.map (·.beta #[x]) + let o ← realizePure ctx envB x atomsX nx word + pure o + let hA ← realizePure ctx env d atoms a out + let h2 ← mkAppM ``Realizes.bind₀ #[hN.proof, hLoop] + let proof ← mkAppM ``Realizes.bind₀ #[hA.proof, h2] + let safe ← withLocalDeclD `w (mkConst ``Nat) fun w => + withLocalDeclD `s (mkConst ``Memory.State) fun s => do + let pair ← mkAppM ``Prod.mk #[d, a] + let sn ← reducePairs (hN.safe.beta #[w, pair, s]) + let sl ← mkAppM ``iterationSafe #[body, hB.safe, w, d, n, a, s] + mkLambdaFVars #[w, d, s] (mkAnd (hA.safe.beta #[w, d, s]) (mkAnd sn sl)) + let progR ← mkLambdaFVars #[d] (← mkAppM ``iterate #[body, d, n, a]) + let stmt ← mkAppM ``Realizes #[ctx.basis, progR, env.input, out, safe, ← zeroWork env.ty] + let proof ← mkExpectedTypeHint proof stmt + if D == d then return { proof, output := out, safe } + let g ← mkLambdaFVars #[d] D + let eq ← mkLambdaFVars #[d] (← mkEqSymm (← mkAppM ``iterate_comap #[g, step, d, n, a])) + return { proof := ← mkAppM ``Realizes.congr #[proof, eq], output := out, safe } + /-- Certify `bind m f`: the first client, then the continuation over the extended environment. -/ private partial def realizeBind (ctx : Ctx) (env : Env) (d m f : Expr) (output? : Option Expr) : TermElabM Out := do @@ -813,8 +877,8 @@ private partial def splitDomain (g : MVarId) : MetaM (List MVarId) := g.withCont end Cslib.Tactic.Realize open Lean Meta Elab Tactic Cslib.WordRAM Cslib.WordRAM.Realization Cslib.Tactic.Realize in -/-- Prove `Realizes B p input output safe work` for a loop-free memory client `p`, leaving the -genuine fit and domain conditions as goals in the context `w d s hwords hmem h`. -/ +/-- Prove `Realizes B p input output safe work` for a memory client `p`, leaving the genuine fit +and domain conditions as goals in the context `w d s hwords hmem h`. -/ elab "realize" : tactic => withMainContext do let goal ← getMainGoal let target ← instantiateMVars (← whnfR (← goal.getType)) @@ -850,7 +914,8 @@ elab "realize" : tactic => withMainContext do let hdecl := (← getLCtx).lastDecl.get! g.replaceLocalDeclDefEq hdecl.fvarId (← Core.betaReduce hdecl.type) setGoals [g] - evalTactic (← `(tactic| try simp only [true_and, and_true, and_self, Nat.add_zero])) + evalTactic (← `(tactic| + try simp only [true_and, and_true, and_self, Nat.add_zero, iterationSafe_true])) let gs ← getGoals let gs ← gs.flatMapM fun g => liftMetaM (splitDomain g) -- A condition repeated in the same context is proved once. diff --git a/ComplexityTests/WordRAMRealize.lean b/ComplexityTests/WordRAMRealize.lean index 71cf0852aa..7d66fe0fcb 100644 --- a/ComplexityTests/WordRAMRealize.lean +++ b/ComplexityTests/WordRAMRealize.lean @@ -8,7 +8,7 @@ import Complexity.Computability.WordRAM.Realization.Client.MemoryStack import Complexity.DataStructure.Queue.Memory import Complexity.Tactic.Realize -/-! `realize` on the memory-oracle stack and queue. +/-! `realize` on the memory-oracle stack and queue, matches, loops and failures. The three stack statements are exactly those of `Realization.MemoryStack` (#2246), including their local-work gradings; `realize` replaces the hand-assembled bind trees and leaves only the @@ -100,6 +100,38 @@ example : Realizes .standard scaled · exact h.1 · exact h.2 +/-! ## Loops -/ + +/-- Two words. -/ +abbrev pairWords : ℕ × ℕ ↪ (Fin 2 → ℕ) := + ((Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding.prodMap + (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding).trans (Fin.appendEquiv 1 1).toEmbedding + +/-- Follow `n` pointers from `start`. -/ +def chase (d : ℕ × ℕ) : Memory.Prog ℕ := + iterate (fun x : ℕ × ℕ => Memory.read x.2) d.1 d.2 d.1 + +/-- The loop receives a component of the environment; its domains are trivial. -/ +example : Realizes .exact chase pairWords (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + (fun _ _ _ => True) (fun _ _ => 0) := by + realize + +/-- Add the cell at `base + i` and advance. -/ +def sumStep (x : ℕ × (ℕ × ℕ)) : Memory.Prog (ℕ × ℕ) := do + let v ← Memory.read (x.1 + x.2.1) + pure (x.2.1 + 1, x.2.2 + v) + +/-- Sum `n` cells from `base`, returning the final index and the sum. -/ +def sumCells (d : ℕ × ℕ) : Memory.Prog (ℕ × ℕ) := iterate sumStep d.1 d.2 (0, 0) + +/-- The loop's domain is the step's fit conditions along the iterations. -/ +example : Realizes .exact sumCells pairWords pairWords + (fun w d s => iterationSafe (fun x => sumStep (x.1.1, x.2)) + (fun w x s => Fits w (x.1.1 + x.2.1) ∧ Fits w (x.2.1 + 1) ∧ + Fits w (x.2.2 + s.mem (x.1.1 + x.2.1))) w d d.2 (0, 0) s) (fun _ _ => 0) := by + realize + exact h + /-! ## Failure messages -/ /-- A power has no glue rule. -/ @@ -145,6 +177,20 @@ example : Realizes .exact readMany (Equiv.funUnique (Fin 1) ℕ).symm.toEmbeddin (Equiv.ofUnique Unit (Fin 0 → ℕ)).toEmbedding (fun _ _ _ => True) (fun _ _ => 0) := by realize +/-- A loop without a query has no memory charge to pay for its iterations. -/ +def countUp (d : ℕ × ℕ) : Memory.Prog ℕ := + iterate (fun x : (ℕ × ℕ) × ℕ => pure (x.2 + 1)) d d.2 0 + +/-- +error: realize: a loop body must begin with a memory query, which pays for the iteration; +the body begins with + PFunctor.FreeM.pure (x.2 + 1) +-/ +#guard_msgs in +example : Realizes .exact countUp pairWords (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + (fun _ _ _ => True) (fun _ _ => 0) := by + realize + /-- Allocation is certified in the exact basis. -/ def fresh (n : ℕ) : Memory.Prog ℕ := Memory.alloc n From de22695548ae044e5b2f9b86f4ba4556e129b581 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 21:33:37 -0500 Subject: [PATCH 22/27] test(WordRAM): a whole stack client to a flat program via realize Add `blockWords` and its `WordEncoding` instance, so binds may return blocks and layouts. `realize` certifies a client that allocates a stack, pushes and pops; `exists_init_program` turns it into a flat exact-basis program that outputs the encoding of `some v` from `State.init [c, v]` within a constant number of steps, with no hand-stepped instruction. Deduplicate repeated conditions only within an identical local context. Refs #2205. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../WordRAM/Realization/Absorb.lean | 15 +++- Complexity/Tactic/Realize.lean | 4 +- ComplexityTests/WordRAMRealize.lean | 73 +++++++++++++++++++ 3 files changed, 89 insertions(+), 3 deletions(-) diff --git a/Complexity/Computability/WordRAM/Realization/Absorb.lean b/Complexity/Computability/WordRAM/Realization/Absorb.lean index cea70b77b0..d69fdbdcf6 100644 --- a/Complexity/Computability/WordRAM/Realization/Absorb.lean +++ b/Complexity/Computability/WordRAM/Realization/Absorb.lean @@ -31,6 +31,7 @@ absorption applied. They are the target shapes of the `realize` tactic. Glue tak ## Main definitions * `WordEncoding`: the word encoding `realize` uses for an intermediate answer type. +* `blockWords`: a block as its base address and length. ## Main results @@ -53,7 +54,7 @@ namespace Cslib.WordRAM.Realization open Cslib.DataStructure /-- The faithful word encoding `realize` uses when a bind passes an answer of type `α` to its -continuation. Instances cover words, unit, optional words and Booleans. -/ +continuation. Instances cover words, unit, optional words, Booleans, blocks and pairs. -/ class WordEncoding (α : Type) where /-- The number of words. -/ width : ℕ @@ -68,6 +69,18 @@ instance : WordEncoding (Option ℕ) := ⟨2, optionWords⟩ instance : WordEncoding Bool := ⟨2, boolWords⟩ +/-- A block is encoded by its base address and its length. -/ +def blockWords : Block ↪ (Fin 2 → ℕ) where + toFun b := ![b.base, b.length] + inj' := by + rintro ⟨a, b⟩ ⟨a', b'⟩ h + have ha := congrFun h 0 + have hb := congrFun h 1 + simp only [Matrix.cons_val_zero, Matrix.cons_val_one] at ha hb + rw [ha, hb] + +instance : WordEncoding Block := ⟨2, blockWords⟩ + /-- A pair is encoded by its two encodings, first component first. -/ instance {α β : Type} [WordEncoding α] [WordEncoding β] : WordEncoding (α × β) := ⟨_, ((WordEncoding.encode (α := α)).prodMap (WordEncoding.encode (α := β))).trans diff --git a/Complexity/Tactic/Realize.lean b/Complexity/Tactic/Realize.lean index 8b0dbebc3c..42ddc42df3 100644 --- a/Complexity/Tactic/Realize.lean +++ b/Complexity/Tactic/Realize.lean @@ -918,13 +918,13 @@ elab "realize" : tactic => withMainContext do try simp only [true_and, and_true, and_self, Nat.add_zero, iterationSafe_true])) let gs ← getGoals let gs ← gs.flatMapM fun g => liftMetaM (splitDomain g) - -- A condition repeated in the same context is proved once. + -- A condition repeated in the same local context is proved once. let mut kept : Array MVarId := #[] for g in gs do let t ← instantiateMVars (← g.getType) let lctx := (← g.getDecl).lctx let dup? ← kept.findM? fun g' => do - return (← g'.getDecl).lctx.decls.size == lctx.decls.size && + return (← g'.getDecl).lctx.getFVarIds == lctx.getFVarIds && (← instantiateMVars (← g'.getType)) == t match dup? with | some g' => g.assign (mkMVar g') diff --git a/ComplexityTests/WordRAMRealize.lean b/ComplexityTests/WordRAMRealize.lean index 7d66fe0fcb..5030864328 100644 --- a/ComplexityTests/WordRAMRealize.lean +++ b/ComplexityTests/WordRAMRealize.lean @@ -5,6 +5,7 @@ Authors: Samuel Schlesinger -/ import Complexity.Computability.WordRAM.Realization.Client.MemoryStack +import Complexity.Computability.WordRAM.Realization.Program import Complexity.DataStructure.Queue.Memory import Complexity.Tactic.Realize @@ -132,6 +133,78 @@ example : Realizes .exact sumCells pairWords pairWords realize exact h +/-! ## A whole stack client, to a flat Word-RAM program + +The client allocates a stack, pushes and pops. `realize` certifies it, allocation included; the +only work left is evaluating the allocation. `exists_init_program` then gives a flat program on the +native list input whose output is the encoding of `some v`, within a constant number of steps. +No instruction is stepped by hand. -/ + +/-- The four layout words, for a bind that returns a layout. -/ +instance : WordEncoding Stack.Memory.Layout := ⟨4, layoutWords⟩ + +/-- Allocate a stack of capacity `c`, push `v`, and pop. -/ +def roundTrip (d : ℕ × ℕ) : Memory.Prog (Option ℕ) := do + let L ← Stack.Memory.allocLayout d.1 + Stack.Memory.push L d.2 + Stack.Memory.pop L + +/-- The certificate. -/ +theorem roundTrip_realizes : Realizes .exact roundTrip pairWords optionWords + (fun w d s => Fits w (s.frontier + 1 + d.1)) (fun _ _ => 0) := by + realize + all_goals + try simp only [show (Memory.spec 0).clientResult s (Stack.Memory.allocLayout d.1) = + ⟨⟨s.frontier, 1⟩, ⟨s.frontier + 1, d.1⟩⟩ from rfl, + show (Memory.spec 0).clientState s (Stack.Memory.allocLayout d.1) = + ⟨clear s.mem s.frontier 1, s.frontier + 1 + d.1⟩ from rfl, + StateTransitionSpec.clientState, Stack.Memory.runClient_push, + clear_of_owns (show (Block.mk s.frontier 1).Owns s.frontier from ⟨le_rfl, by simp⟩), + Function.update_self] at * + all_goals first | exact h.of_le (by omega) | omega + +/-- From `State.init [c, v]`, the flat program outputs the encoding of `some v` within +`10 C + 1` steps, for one exchange factor `C`. -/ +theorem roundTrip_program : ∃ n C : ℕ, + ∃ emit : ∀ {r : ℕ}, (frontier : Fin r) → (Fin 2 → Fin r) → (Fin (2 + n + 1) → Fin r) → + Code .exact r, + ∀ {r : ℕ} (frontier : Fin r) (src : Fin 2 → Fin r) (tmp : Fin (2 + n + 1) → Fin r), + Function.Injective src → Function.Injective tmp → (∀ i j, src i ≠ tmp j) → + (∀ j, tmp j ≠ frontier) → (∀ i, src i ≠ frontier) → + ∀ (i : Fin 2) (w c v : ℕ), 1 ≤ c → Fits w (c + 4) → Fits w v → + ∃ t, Executes w ((Code.seq (inputPrefix frontier (tmp (Fin.last (2 + n))) src) + (emit frontier src tmp)).program (tmp (Fin.castAdd (n + 1) i))) + (State.init [c, v]) (optionWords (some v) i) t ∧ t ≤ C * 10 + 1 := by + obtain ⟨n, C, emit, he⟩ := roundTrip_realizes.exists_init_program (fun d => [d.1, d.2]) + (by intro d i; fin_cases i <;> rfl) + refine ⟨n, C, emit, fun frontier src tmp hsrc htmp hd hf hs i w c v hc1 hc hv => ?_⟩ + have h3 : Fits w 3 := hc.of_le (by omega) + have hvalid : (Memory.spec w).ValidHistory (inputState [c, v]) + ((Memory.spec w).clientHistory (inputState [c, v]) (roundTrip (c, v))) := by + change (Memory.spec w).ValidHistory (inputState [c, v]) + [.alloc 1, .allocDirty c, .read 3, .write 4 v, .write 3 1, .read 3, .read 4, .write 3 0] + refine ⟨?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, trivial⟩ + · change Fits w (3 + 1) + exact hc.of_le (by omega) + · change Fits w (3 + 1 + c) + exact hc.of_le (by omega) + · change 3 < 3 + 1 + c + omega + · exact ⟨show 4 < 3 + 1 + c by omega, hv⟩ + · exact ⟨show 3 < 3 + 1 + c by omega, h3.of_le (by omega)⟩ + · change 3 < 3 + 1 + c + omega + · change 4 < 3 + 1 + c + omega + · exact ⟨show 3 < 3 + 1 + c by omega, fits_zero w⟩ + obtain ⟨t, ht, hle⟩ := he frontier src tmp hsrc htmp hd hf hs i w (c, v) le_rfl h3 + ⟨h3.of_le (by simp), fun x hx => by + simp only [List.mem_cons, List.not_mem_nil, or_false] at hx + rcases hx with rfl | rfl + exacts [hc.of_le (by omega), hv]⟩ + (hc.of_le (by simp [inputState]; omega)) hvalid + exact ⟨t, ht, hle.trans (by rfl)⟩ + /-! ## Failure messages -/ /-- A power has no glue rule. -/ From 0a8975c8c4826b62be577c296ec5d440782f3fdd Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 22:31:56 -0500 Subject: [PATCH 23/27] =?UTF-8?q?fix(WordRAM):=20destructure=20the=20loop?= =?UTF-8?q?=20argument=20in=20Realizes.iterate=E2=82=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #2246's iterateRuntime now binds its argument by a destructuring lambda, so iterate₀ matches the components before omega. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity/Computability/WordRAM/Realization/Absorb.lean | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Complexity/Computability/WordRAM/Realization/Absorb.lean b/Complexity/Computability/WordRAM/Realization/Absorb.lean index d69fdbdcf6..744295ad41 100644 --- a/Complexity/Computability/WordRAM/Realization/Absorb.lean +++ b/Complexity/Computability/WordRAM/Realization/Absorb.lean @@ -212,8 +212,9 @@ theorem iterate₀ {σ : Type} (input : δ ↪ (Fin k → ℕ)) (output : σ ↪ (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding).trans (Fin.appendEquiv (k + l) 1).toEmbedding) output (fun w d s => iterationSafe body safe w d.1.1 d.2 d.1.2 s) (fun _ _ => 0) := - (iterateRuntime input output h).mono_mul 1 fun d s => by - have := iterationWork_zero_le body hquery d.1.1 d.2 d.1.2 s + (iterateRuntime input output h).mono_mul 1 fun ((d, a), fuel) s => by + have := iterationWork_zero_le body hquery d fuel a s + dsimp only omega end Realizes From 4d7e97c80db9dc140e5d3dcffaacf07b442c2960 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 22:41:19 -0500 Subject: [PATCH 24/27] feat(WordRAM): native backends for memory refinements, one realized body per constructor Realization.Backend generalizes MemoryBackend: from a Refinement D (Memory.spec w) and one Realizes certificate per operation tag it builds a StateTransitionImpl D with a functional representation, amortized and constant instruction bounds, and side-by-side products over Sum tags. Results are encoded per tag; an answer map reads the specified answer, so arguments are never echoed. Realizes.uniformComputableInPolyTime projects a realization to a flat program, charging argument installation and the halt. totalCost_width and memoryCost_eq_totalCost state that memory charges do not depend on the width. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 2 + .../WordRAM/Realization/Backend.lean | 418 ++++++++++++++++++ .../WordRAM/Realization/Function.lean | 87 ++++ .../WordRAMRealizationBackend.lean | 140 ++++++ 4 files changed, 647 insertions(+) create mode 100644 Complexity/Computability/WordRAM/Realization/Backend.lean create mode 100644 Complexity/Computability/WordRAM/Realization/Function.lean create mode 100644 ComplexityTests/WordRAMRealizationBackend.lean diff --git a/Complexity.lean b/Complexity.lean index 04e0c69a15..a0ac901d5f 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2557,10 +2557,12 @@ public import Complexity.Computability.WordRAM.Randomized.LoopSegment public import Complexity.Computability.WordRAM.Randomized.Structured public import Complexity.Computability.WordRAM.Randomized.UniformLoopExit public import Complexity.Computability.WordRAM.Realization.Absorb +public import Complexity.Computability.WordRAM.Realization.Backend public import Complexity.Computability.WordRAM.Realization.Basic public import Complexity.Computability.WordRAM.Realization.Bind public import Complexity.Computability.WordRAM.Realization.Branch public import Complexity.Computability.WordRAM.Realization.Client.MemoryStack +public import Complexity.Computability.WordRAM.Realization.Function public import Complexity.Computability.WordRAM.Realization.Glue public import Complexity.Computability.WordRAM.Realization.GlueVector public import Complexity.Computability.WordRAM.Realization.Grade diff --git a/Complexity/Computability/WordRAM/Realization/Backend.lean b/Complexity/Computability/WordRAM/Realization/Backend.lean new file mode 100644 index 0000000000..9be6c0686b --- /dev/null +++ b/Complexity/Computability/WordRAM/Realization/Backend.lean @@ -0,0 +1,418 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Realization.Witness +public import Complexity.DataStructure.StateTransition.Refinement +public import Complexity.DataStructure.StateTransition.Resource + +/-! +# Native implementations of refinements, one realized body per operation constructor + +`MemoryBackend` runs each memory operation as the static body of a primitive realization, chosen +by the operation's constructor. `Backend` does the same for any specification `D` refined to the +memory oracle by a `Refinement D (Memory.spec w)`. One `Realizes` certificate per constructor tag +supplies the body; the refinement supplies correctness and memory charges. + +A `Certificate` bundles one realization with its program, encodings, domain and local work. A +family `C : ι → Certificate B` over a finite type of tags fixes one register file, one body per +tag and one exchange factor (`Backend.registers`, `Backend.code`, `Backend.factor`), before every +width, refinement, runtime layout and argument. A `Backend D C` says which tag each operation of +`D` selects (`tag`), which arguments the tag's program receives (`arg`, which may include the +structure's runtime layout), and how the program's result gives the specified answer (`answer`). +The result encoding belongs to the tag, so an operation's arguments are never encoded in its +result; the native code never dispatches on the operation. + +`Backend.Implements` connects a backend to a refinement: each method runs the selected program +and maps its result by `answer`, and from an admissible state the arguments are words and the +program's domain holds. Then `Backend.implementation` is a `StateTransitionImpl D` with a +functional representation: the memory satisfies the refinement's invariant and abstracts to the +state, and every register and cell holds a word. Instruction counts follow from the refinement's +amortized memory charges (`Backend.amortized`), and are constant per operation when those charges +are constant and need no potential (`Backend.bound`). Backends of two specifications run their +product side by side (`Backend.sum`), with tags in a sum type: the operation's `Sum` constructor +selects the family, and no native code dispatches on it. + +As in `MemoryBackend`, the selected tag's arguments are installed in registers before its body +runs, uncharged, as a calling convention; decoding the answer executes no instruction. Bounds +count the body's instructions only. + +Memory charges do not depend on the word width (`totalCost_width`, `memoryCost_eq_totalCost`), +so `memoryCost`, which is taken at width zero, is the charge that refinements state at width `w`. + +## Main definitions + +* `Certificate`: a realization certificate with its program, encodings, domain and work. +* `Backend.registers`, `Backend.frontier`, `Backend.code`, `Backend.factor`: the register file, + the body of each tag and one exchange factor. +* `Backend`: the tag, arguments and answer of each operation. +* `Backend.Implements`: a backend runs the methods of a refinement. +* `Backend.sum`: two backends side by side, for a product specification. +* `Backend.exec`, `Backend.implementation`: native execution, and the native implementation. + +## Main results + +* `totalCost_width`, `clientHistory_width`, `memoryCost_eq_totalCost`: memory charges do not + depend on the word width. +* `Backend.Implements.prod`: side-by-side backends run a product of refinements. +* `Backend.exec_iff`, `Backend.rep_iff`: the execution relation and representation, unfolded. +* `Backend.exec_memoryState`: an operation leaves its method's final memory. +* `Backend.amortized`, `Backend.bound`: amortized and worst-case instruction bounds. +-/ + +public section + +namespace Cslib.WordRAM.Realization + +open Cslib.DataStructure + +/-! ## Memory charges and the word width -/ + +/-- Memory charges along a history do not depend on the word width. -/ +theorem totalCost_width (w : ℕ) (s : Memory.State) (os : List Memory.Op) : + (Memory.spec w).totalCost Memory.charge s os = + (Memory.spec 0).totalCost Memory.charge s os := by + induction os generalizing s with + | nil => rfl + | cons o os ih => exact congrArg (Memory.charge s o + ·) (ih _) + +/-- The memory history of a client does not depend on the word width. -/ +theorem clientHistory_width {α : Type} (w : ℕ) (p : Memory.Prog α) (s : Memory.State) : + (Memory.spec w).clientHistory s p = (Memory.spec 0).clientHistory s p := + congrArg (fun t => t.2.2) (runClient_width w p s) + +/-- `memoryCost` is the memory charge of the client's history at every word width. -/ +theorem memoryCost_eq_totalCost {α : Type} (w : ℕ) (p : Memory.Prog α) (s : Memory.State) : + memoryCost p s = + (Memory.spec w).totalCost Memory.charge s ((Memory.spec w).clientHistory s p) := by + rw [totalCost_width, clientHistory_width] + rfl + +/-! ## Certificates and backends -/ + +/-- A uniform realization certificate, bundled with the program it realizes, its argument and +result encodings, its entry domain and its local work. -/ +structure Certificate (B : Basis) where + /-- The argument type. -/ + {Arg : Type} + /-- The result type. -/ + {Res : Type} + /-- The number of argument words. -/ + {k : ℕ} + /-- The number of result words. -/ + {l : ℕ} + /-- The realized memory program. -/ + {prog : Arg → Memory.Prog Res} + /-- The faithful argument encoding. -/ + {input : Arg ↪ (Fin k → ℕ)} + /-- The faithful result encoding. -/ + {output : Res ↪ (Fin l → ℕ)} + /-- The entry domain. -/ + {safe : ℕ → Arg → Memory.State → Prop} + /-- The certified local work. -/ + {work : Arg → Memory.State → ℕ} + /-- The realization. -/ + realizes : Realizes B prog input output safe work + +variable {B : Basis} {ι : Type} + +/-- How the operations of `D` run on the bodies of `C`: each operation selects a tag, passes +arguments to the tag's program and reads its answer from that program's result. Only the tag +selects code. -/ +structure Backend (D : StateTransitionSpec.{0, 0, 0}) (C : ι → Certificate B) where + /-- The tag of each operation, typically its constructor. -/ + tag : D.Op → ι + /-- The arguments of the selected program, including any runtime layout. -/ + arg : ∀ o, (C (tag o)).Arg + /-- The specified answer, read off the selected program's result. -/ + answer : ∀ o, (C (tag o)).Res → D.Result o + +namespace Backend + +section Registers + +variable [Fintype ι] (C : ι → Certificate B) + +/-- Registers for the longest argument vector. -/ +noncomputable def argWords : ℕ := Finset.univ.sup fun i => (C i).k + +/-- Registers for the largest result and scratch bank. -/ +noncomputable def scratchWords : ℕ := + Finset.univ.sup fun i => (C i).l + (C i).realizes.scratchSize + 1 + +/-- One register count, chosen before every width, refinement, layout and argument. -/ +noncomputable def registers : ℕ := argWords C + scratchWords C + 1 + +/-- Register zero holds the allocation frontier. -/ +def frontier : Fin (registers C) := ⟨0, by unfold registers; omega⟩ + +/-- The argument registers of tag `i`, from register one. -/ +def src (i : ι) (j : Fin (C i).k) : Fin (registers C) := + ⟨j.val + 1, by + have := Finset.le_sup (f := fun i => (C i).k) (Finset.mem_univ i) + have := j.isLt + unfold registers argWords at * + omega⟩ + +/-- The result and scratch registers of tag `i`, after all argument registers. -/ +noncomputable def tmp (i : ι) (j : Fin ((C i).l + (C i).realizes.scratchSize + 1)) : + Fin (registers C) := + ⟨argWords C + 1 + j.val, by + have := Finset.le_sup (f := fun i => (C i).l + (C i).realizes.scratchSize + 1) + (Finset.mem_univ i) + have := j.isLt + unfold registers scratchWords at * + omega⟩ + +/-- The static body of tag `i`. -/ +noncomputable def code (i : ι) : Code B (registers C) := + (C i).realizes.code (frontier C) (src C i) (tmp C i) + +/-- One exchange factor for every tag. -/ +noncomputable def factor : ℕ := Finset.univ.sup fun i => (C i).realizes.factor + +/-- Each tag's exchange factor is at most the common one. -/ +theorem factor_le (i : ι) : (C i).realizes.factor ≤ factor C := + Finset.le_sup (f := fun i => (C i).realizes.factor) (Finset.mem_univ i) + +private theorem layout (i : ι) : + Function.Injective (tmp C i) ∧ (∀ a b, src C i a ≠ tmp C i b) ∧ + (∀ b, tmp C i b ≠ frontier C) ∧ ∀ a, src C i a ≠ frontier C := by + have hk := Finset.le_sup (f := fun i => (C i).k) (Finset.mem_univ i) + refine ⟨fun a b h => Fin.ext ?_, fun a b h => ?_, fun b h => ?_, fun a h => ?_⟩ <;> + have := congrArg Fin.val h <;> simp only [src, tmp, frontier, argWords] at * <;> omega + +end Registers + +variable {C : ι → Certificate B} {D : StateTransitionSpec.{0, 0, 0}} (S : Backend D C) + +/-- A backend runs the methods of `R`: each method is the selected program with its result +mapped by `answer`, and from an admissible state the arguments are words and the program's +domain holds. -/ +structure Implements {w : ℕ} (R : Refinement D (Memory.spec w)) : Prop where + /-- Each method runs the selected program and answers through `answer`. -/ + run : ∀ o s, (Memory.spec w).runClient s (R.methods o) = + Prod.map (S.answer o) id ((Memory.spec w).runClient s ((C (S.tag o)).prog (S.arg o))) + /-- From an admissible state, the arguments fit words and the program's domain holds. -/ + entry : ∀ {e} o, Memory.WellFormed w e → R.inv e → D.valid (R.abs e) o → + (∀ j, Fits w ((C (S.tag o)).input (S.arg o) j)) ∧ (C (S.tag o)).safe w (S.arg o) e + +/-! ### Products -/ + +section Sum + +variable {ι' : Type} {C' : ι' → Certificate B} {D' : StateTransitionSpec.{0, 0, 0}} + +/-- Two backends side by side run the product specification: a left operation selects a left +tag and a right operation a right tag. -/ +def sum (S' : Backend D' C') : Backend (product D D') (Sum.elim C C') where + tag := Sum.map S.tag S'.tag + arg + | .inl o => S.arg o + | .inr o => S'.arg o + answer + | .inl o => S.answer o + | .inr o => S'.answer o + +/-- Side-by-side backends run the product of two non-interfering refinements. -/ +theorem Implements.prod {w : ℕ} {S : Backend D C} {S' : Backend D' C'} + {R : Refinement D (Memory.spec w)} {R' : Refinement D' (Memory.spec w)} + {frame₁ : ∀ {e} o {a e' tr}, R.inv e → R'.inv e → D.valid (R.abs e) o → + (Memory.spec w).ClientRun e (R.methods o) a e' tr → R'.abs e' = R'.abs e ∧ R'.inv e'} + {frame₂ : ∀ {e} o {a e' tr}, R.inv e → R'.inv e → D'.valid (R'.abs e) o → + (Memory.spec w).ClientRun e (R'.methods o) a e' tr → R.abs e' = R.abs e ∧ R.inv e'} + (hS : S.Implements R) (hS' : S'.Implements R') : + (S.sum S').Implements (R.prod R' frame₁ frame₂) where + run + | .inl o, s => hS.run o s + | .inr o, s => hS'.run o s + entry + | .inl o, hw, hi, hv => hS.entry o hw hi.1 hv + | .inr o, hw, hi, hv => hS'.entry o hw hi.2 hv + +end Sum + +/-! ### Native execution -/ + +variable [Fintype ι] + +/-- Install the selected program's arguments in its argument registers. Installation is +uncharged and leaves memory and the frontier unchanged. -/ +def prepare (o : D.Op) (x : Data (registers C)) : Data (registers C) where + regs j := if h : 0 < j.val ∧ j.val ≤ (C (S.tag o)).k then + (C (S.tag o)).input (S.arg o) ⟨j.val - 1, by omega⟩ + else x.regs j + mem := x.mem + +/-- An operation runs the body of its tag from the prepared data, in the counted number of +instructions, and its answer is read off the encoded result. -/ +def exec (w : ℕ) : Execution D (Data (registers C)) ℕ := + fun x o a y n => Exec w (code C (S.tag o)) (S.prepare o x) y n ∧ + ∃ b, a = S.answer o b ∧ ∀ j, y.regs (tmp C (S.tag o) + (Fin.castAdd ((C (S.tag o)).realizes.scratchSize + 1) j)) = (C (S.tag o)).output b j + +/-- The execution relation, unfolded. -/ +theorem exec_iff (w : ℕ) (x y : Data (registers C)) (o : D.Op) (a : D.Result o) (n : ℕ) : + S.exec w x o a y n ↔ Exec w (code C (S.tag o)) (S.prepare o x) y n ∧ + ∃ b, a = S.answer o b ∧ ∀ j, y.regs (tmp C (S.tag o) + (Fin.castAdd ((C (S.tag o)).realizes.scratchSize + 1) j)) = + (C (S.tag o)).output b j := Iff.rfl + +variable {S} {w : ℕ} {R : Refinement D (Memory.spec w)} + +private theorem prepare_memoryState (o : D.Op) (x : Data (registers C)) : + memoryState (frontier C) (S.prepare o x) = memoryState (frontier C) x := by + apply Memory.State.ext rfl + simp [memoryState, prepare, frontier] + +private theorem prepare_src (o : D.Op) (x : Data (registers C)) : + (S.prepare o x).regs ∘ src C (S.tag o) = (C (S.tag o)).input (S.arg o) := by + funext j + simp [prepare, src] + +private theorem prepare_bounded {o : D.Op} {x : Data (registers C)} (hx : x.Bounded w) + (hin : ∀ j, Fits w ((C (S.tag o)).input (S.arg o) j)) : (S.prepare o x).Bounded w := by + refine ⟨fun j => ?_, hx.2⟩ + simp only [prepare] + split + · exact hin _ + · exact hx.1 j + +/-- One operation from represented data: its body's execution, instruction count, memory, +encoded result and answer. -/ +private theorem exists_body (hS : S.Implements R) (o : D.Op) {x : Data (registers C)} + (hi : R.inv (memoryState (frontier C) x)) (hx : x.Bounded w) + (hv : D.valid (R.abs (memoryState (frontier C) x)) o) : + ∃ y n, Exec w (code C (S.tag o)) (S.prepare o x) y n ∧ + n ≤ factor C * ((Memory.spec w).totalCost Memory.charge (memoryState (frontier C) x) + ((Memory.spec w).clientHistory (memoryState (frontier C) x) (R.methods o)) + + (C (S.tag o)).work (S.arg o) (memoryState (frontier C) x) + 1) ∧ + memoryState (frontier C) y = + (Memory.spec w).clientState (memoryState (frontier C) x) (R.methods o) ∧ + (∀ j, y.regs (tmp C (S.tag o) (Fin.castAdd ((C (S.tag o)).realizes.scratchSize + 1) j)) = + (C (S.tag o)).output + ((Memory.spec w).clientResult (memoryState (frontier C) x) + ((C (S.tag o)).prog (S.arg o))) j) ∧ + S.answer o ((Memory.spec w).clientResult (memoryState (frontier C) x) + ((C (S.tag o)).prog (S.arg o))) = + (Memory.spec w).clientResult (memoryState (frontier C) x) (R.methods o) ∧ + y.Bounded w ∧ + (Memory.spec w).ValidHistory (memoryState (frontier C) x) + ((Memory.spec w).clientHistory (memoryState (frontier C) x) (R.methods o)) := by + have hrun := hS.run o (memoryState (frontier C) x) + have hvalid := ((Memory.spec w).demonicValid_iff_validHistory_runClient _ _).mp + (R.valid o hi hv) + obtain ⟨hin, hsafe⟩ := hS.entry o (wellFormed_memoryState hx) hi hv + have hb := prepare_bounded hx hin + have hp := prepare_memoryState (S := S) o x + obtain ⟨hi', hd, hf, hs⟩ := layout C (S.tag o) + have hhist : (Memory.spec w).clientHistory (memoryState (frontier C) x) (R.methods o) = + (Memory.spec w).clientHistory (memoryState (frontier C) x) + ((C (S.tag o)).prog (S.arg o)) := congrArg (fun t => t.2.2) hrun + obtain ⟨y, ⟨n, hn, he⟩, hm, hout, -⟩ := + ((C (S.tag o)).realizes.code_spec (frontier C) (src C _) (tmp C _) hi' hd hf hs).2 w + (S.arg o) _ hb (prepare_src o x) (hp ▸ hsafe) (hp ▸ hhist ▸ hvalid) + rw [hp] at hn hm hout + refine ⟨y, n, he, hn.trans ?_, hm.trans (congrArg (fun t => t.2.1) hrun).symm, hout, + (congrArg Prod.fst hrun).symm, he.bounded hb, hvalid⟩ + rw [memoryCost_eq_totalCost w, ← hhist] + exact Nat.mul_le_mul_right _ (factor_le C _) + +/-- The answer of an execution is the method's result. -/ +private theorem answer_eq (hS : S.Implements R) {o : D.Op} {x y : Data (registers C)} + {a : D.Result o} {n : ℕ} (he : S.exec w x o a y n) + (hout : ∀ j, y.regs (tmp C (S.tag o) (Fin.castAdd ((C (S.tag o)).realizes.scratchSize + 1) j)) = + (C (S.tag o)).output + ((Memory.spec w).clientResult (memoryState (frontier C) x) + ((C (S.tag o)).prog (S.arg o))) j) : + a = (Memory.spec w).clientResult (memoryState (frontier C) x) (R.methods o) := by + obtain ⟨-, b, rfl, hb⟩ := he + rw [(C (S.tag o)).output.injective (funext fun j => (hb j).symm.trans (hout j))] + exact (congrArg Prod.fst (hS.run o _)).symm + +/-- The native implementation: represented data holds a memory that satisfies the refinement's +invariant and abstracts to the state, and every register and cell holds a word. -/ +noncomputable def implementation (hS : S.Implements R) : StateTransitionImpl D (S.exec w) where + rep s x := R.inv (memoryState (frontier C) x) ∧ R.abs (memoryState (frontier C) x) = s ∧ + x.Bounded w + total o hr hv := by + obtain ⟨hi, rfl, hx⟩ := hr + obtain ⟨y, n, he, -, -, hout, -⟩ := exists_body hS o hi hx hv + exact ⟨_, y, n, he, _, rfl, hout⟩ + correct {s x o a y n} hr hv he := by + obtain ⟨hi, rfl, hx⟩ := hr + obtain ⟨y', n', he', -, hm, hout, -, hb, hvalid⟩ := exists_body hS o hi hx hv + obtain ⟨rfl, rfl⟩ := he.1.unique he' + have ha := answer_eq hS he hout + obtain ⟨hstep, hi'⟩ := R.correct o hi hv ((Memory.spec w).clientRun_canonical _ _ hvalid) + exact ⟨_, ha ▸ hstep, hm ▸ hi', by rw [hm], hb⟩ + +/-- The representation, unfolded. -/ +theorem rep_iff (hS : S.Implements R) (s : D.State) (x : Data (registers C)) : + (implementation hS).rep s x ↔ R.inv (memoryState (frontier C) x) ∧ + R.abs (memoryState (frontier C) x) = s ∧ x.Bounded w := Iff.rfl + +/-- An operation leaves the memory and frontier of its method. -/ +theorem exec_memoryState (hS : S.Implements R) {s : D.State} {x y : Data (registers C)} + {o : D.Op} {a : D.Result o} {n : ℕ} (hr : (implementation hS).rep s x) (hv : D.valid s o) + (he : S.exec w x o a y n) : + memoryState (frontier C) y = + (Memory.spec w).clientState (memoryState (frontier C) x) (R.methods o) := by + obtain ⟨hi, rfl, hx⟩ := hr + obtain ⟨y', n', he', -, hm, -⟩ := exists_body hS o hi hx hv + obtain ⟨rfl, -⟩ := he.1.unique he' + exact hm + +/-- **Amortized instructions.** Memory charges with a potential, plus local work at most `c`, +give native instruction counts with the potential scaled by the exchange factor. -/ +theorem amortized (hS : S.Implements R) {upper : D.State → D.Op → ℕ} + {Φ : Memory.State → ℕ} (A : R.Amortized Memory.charge upper Φ) (c : ℕ) + (hwork : ∀ {e} o, R.inv e → D.valid (R.abs e) o → (C (S.tag o)).work (S.arg o) e ≤ c) : + DataStructure.Amortized (implementation hS) id + (fun _ x => factor C * Φ (memoryState (frontier C) x)) + (fun s o => factor C * (upper s o + c + 1)) where + step {s x o a y n} hr hv he := by + obtain ⟨hi, rfl, hx⟩ := hr + obtain ⟨y', n', he', hn, hm, hout, -, hb, hvalid⟩ := exists_body hS o hi hx hv + obtain ⟨rfl, rfl⟩ := he.1.unique he' + have ha := answer_eq hS he hout + have hrun := (Memory.spec w).clientRun_canonical _ _ hvalid + obtain ⟨hstep, hi'⟩ := R.correct o hi hv hrun + have hA := A.step o hi hv hrun + rw [StateTransitionSpec.totalCostTranscript_canonicalTranscript, ← hm] at hA + have hw := hwork o hi hv + refine ⟨_, ha ▸ hstep, ⟨hm ▸ hi', by rw [hm], hb⟩, ?_⟩ + change n + _ ≤ _ + calc n + factor C * Φ (memoryState (frontier C) y) + ≤ factor C * ((Memory.spec w).totalCost Memory.charge (memoryState (frontier C) x) + ((Memory.spec w).clientHistory (memoryState (frontier C) x) (R.methods o)) + + (C (S.tag o)).work (S.arg o) (memoryState (frontier C) x) + 1) + + factor C * Φ (memoryState (frontier C) y) := Nat.add_le_add_right hn _ + _ = factor C * ((Memory.spec w).totalCost Memory.charge (memoryState (frontier C) x) + ((Memory.spec w).clientHistory (memoryState (frontier C) x) (R.methods o)) + + Φ (memoryState (frontier C) y) + + (C (S.tag o)).work (S.arg o) (memoryState (frontier C) x) + 1) := by ring + _ ≤ factor C * (upper (R.abs (memoryState (frontier C) x)) o + + Φ (memoryState (frontier C) x) + c + 1) := Nat.mul_le_mul_left _ (by omega) + _ = factor C * (upper (R.abs (memoryState (frontier C) x)) o + c + 1) + + factor C * Φ (memoryState (frontier C) x) := by ring + +/-- **Worst-case instructions.** Memory charges at most `upper` without a potential, plus local +work at most `c`, bound every operation's instructions by `factor C * (upper + c + 1)`. With a +constant `upper` this is one constant for every width and refinement. -/ +theorem bound (hS : S.Implements R) {upper : D.State → D.Op → ℕ} + (A : R.Amortized Memory.charge upper fun _ => 0) (c : ℕ) + (hwork : ∀ {e} o, R.inv e → D.valid (R.abs e) o → (C (S.tag o)).work (S.arg o) e ≤ c) : + Bound (implementation hS) id fun s o => factor C * (upper s o + c + 1) := by + intro s x o a y n hr hv he + obtain ⟨-, -, -, hcost⟩ := (amortized hS A c hwork).step hr hv he + simpa only [id, Nat.mul_zero, Nat.add_zero] using hcost + +end Backend + +end Cslib.WordRAM.Realization diff --git a/Complexity/Computability/WordRAM/Realization/Function.lean b/Complexity/Computability/WordRAM/Realization/Function.lean new file mode 100644 index 0000000000..2e583cc370 --- /dev/null +++ b/Complexity/Computability/WordRAM/Realization/Function.lean @@ -0,0 +1,87 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Function +public import Complexity.Computability.WordRAM.MapOp +public import Complexity.Computability.WordRAM.Realization.Program +public import Complexity.Computability.WordRAM.Realization.Witness + +/-! +# Functions computed by realized memory clients + +`Realizes.uniformComputableInPolyTime` turns one realization certificate into +`UniformComputableInPolyTime`. The flat program is the certificate's emitted body, preceded by +the charged argument-installation prefix `inputPrefix` and followed by a halt on one result +coordinate; it is chosen before every width and input. The client supplies the input layout and +an entry width and, at every admitted width, the program's domain, its admissible history, its +answer and a polynomial bound on its memory charge plus local work. A basis simulation carries +the exact-basis code to the target basis. + +## Main results + +* `Realizes.uniformComputableInPolyTime`: uniform polynomial time from a realization. +-/ + +public section + +namespace Cslib.WordRAM.Realization.Realizes + +open Cslib.DataStructure + +variable {δ α : Type} {k l : ℕ} {p : δ → Memory.Prog α} {input : δ ↪ (Fin k → ℕ)} + {output : α ↪ (Fin l → ℕ)} {safe : ℕ → δ → Memory.State → Prop} + {work : δ → Memory.State → ℕ} + +/-- **Uniform polynomial time from a realization.** If the first `k` input words encode the +argument, and at every width admitted by `logWidth c` the input fits, the program's domain and +admissible history hold, result coordinate `i` is the function's value, and the memory charge +plus local work is at most `T (size d)` for a polynomially bounded `T`, then one flat program +computes the function in polynomial time. Argument installation and the halt are charged. -/ +theorem uniformComputableInPolyTime {B : Basis} (φ : Basis.exact.Simulates B) + (h : Realizes .exact p input output safe work) (i : Fin l) + {f : δ → ℕ} {encode : ℕ → δ → List ℕ} {size : δ → ℕ} {T : ℕ → ℕ} + (hT : PolynomiallyBounded T) (c : ℕ) (hc : 0 < c) + (hlayout : ∀ w d (j : Fin k), (encode w d).getD j.val 0 = input d j) + (hspec : ∀ d w, logWidth c (size d) ≤ w → InputFits w (encode w d) → + k ≤ (encode w d).length ∧ Fits w ((encode w d).length + 1) ∧ + safe w d (inputState (encode w d)) ∧ + (Memory.spec w).ValidHistory (inputState (encode w d)) + ((Memory.spec w).clientHistory (inputState (encode w d)) (p d)) ∧ + output ((Memory.spec w).clientResult (inputState (encode w d)) (p d)) i = f d ∧ + memoryCost (p d) (inputState (encode w d)) + work d (inputState (encode w d)) ≤ + T (size d)) : + UniformComputableInPolyTime B f encode size := by + let r := k + (l + h.scratchSize + 1) + 1 + let fr : Fin r := ⟨0, by omega⟩ + let src : Fin k → Fin r := fun j => ⟨j.val + 1, by omega⟩ + let tmp : Fin (l + h.scratchSize + 1) → Fin r := fun j => ⟨k + 1 + j.val, by omega⟩ + have hsrc : Function.Injective src := fun a b hab => + Fin.ext (by have := congrArg Fin.val hab; simp only [src] at this; omega) + have htmp : Function.Injective tmp := fun a b hab => + Fin.ext (by have := congrArg Fin.val hab; simp only [tmp] at this; omega) + have hd : ∀ a b, src a ≠ tmp b := fun a b hab => by + have := congrArg Fin.val hab; simp only [src, tmp] at this; omega + have hf : ∀ b, tmp b ≠ fr := fun b hb => by + have := congrArg Fin.val hb; simp only [tmp, fr] at this; omega + have hs : ∀ a, src a ≠ fr := fun a ha => by + have := congrArg Fin.val ha; simp only [src, fr] at this; omega + refine UniformComputableInPolyTime.of_code + ((Code.seq (inputPrefix fr (tmp (Fin.last _)) src) (h.code fr src tmp)).mapSimulates φ) + (tmp (Fin.castAdd (h.scratchSize + 1) i)) (T := fun n => 2 * k + 5 + h.factor * (T n + 1)) + (by fun_prop) c hc fun d w hw hin => ?_ + obtain ⟨hk, hfit, hsafe, hvalid, hout, hcost⟩ := hspec d w hw hin + obtain ⟨x, ex, hx, hm, hwords, -⟩ := inputPrefix_exec fr (tmp (Fin.last _)) src (encode w d) + hsrc (hf _) (fun j => hd j _) hs hk hfit hin + obtain ⟨y, ey, -, hy, -⟩ := (h.code_spec fr src tmp htmp hd hf hs).2 w d x hx + (funext fun j => (hwords j).trans (hlayout w d j)) (hm ▸ hsafe) (hm ▸ hvalid) + rw [hm] at ey hy + exact ⟨y, ((ex.seq ey).mapSimulates φ).mono + (Nat.add_le_add_left (Nat.mul_le_mul_left _ (by omega)) _), (hy i).trans hout⟩ + +end Cslib.WordRAM.Realization.Realizes diff --git a/ComplexityTests/WordRAMRealizationBackend.lean b/ComplexityTests/WordRAMRealizationBackend.lean new file mode 100644 index 0000000000..7a89dec273 --- /dev/null +++ b/ComplexityTests/WordRAMRealizationBackend.lean @@ -0,0 +1,140 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +-- upstream: candidate + +import Complexity.Computability.WordRAM.Realization.Backend +import Complexity.Computability.WordRAM.Realization.Client.MemoryStack +import Complexity.Computability.WordRAM.Realization.Function + +/-! The per-constructor backend on the array stack. Three tags select the bodies of +`MemoryStack.realizes_push`, `realizes_pop` and `realizes_size`. The push carries a value and +answers `()`; its result encoding has no words, so the value is not echoed. The layout is a +runtime argument of every tag. Correctness and a constant instruction bound come from +`Stack.Memory.refinement` and `Stack.Memory.amortized`. -/ + +open Cslib Cslib.DataStructure Cslib.WordRAM Cslib.WordRAM.Realization + Cslib.WordRAM.Realization.MemoryStack Cslib.DataStructure.Queue + +namespace ComplexityTests.WordRAMRealizationBackend + +/-- The stack's operation constructors. -/ +inductive Tag + | push + | pop + | size + deriving DecidableEq + +instance : Fintype Tag := Fintype.ofList [.push, .pop, .size] fun t => by cases t <;> simp + +/-- One realization per constructor. -/ +noncomputable def certificates : Tag → Certificate .exact + | .push => ⟨realizes_push⟩ + | .pop => ⟨realizes_pop⟩ + | .size => ⟨realizes_size⟩ + +/-- The stack on layout `L`: the push value and the layout are the only arguments. -/ +noncomputable def backend (w C : ℕ) (L : Stack.Memory.Layout) : + Backend (Stack.WordRAM.spec w C) certificates where + tag + | .push _ => .push + | .pop => .pop + | .size => .size + arg + | .push v => (L, v) + | .pop => L + | .size => L + answer + | .push _ => id + | .pop => id + | .size => id + +theorem layoutWords_fits {w : ℕ} {L : Stack.Memory.Layout} {e : Memory.State} + (hw : Memory.WellFormed w e) (hi : Stack.Memory.Inv L e) (j : Fin 4) : + Fits w (layoutWords L j) := by + have h₁ : L.count.base + L.count.length ≤ e.frontier := hi.count_allocated + have h₂ : L.items.base + L.items.length ≤ e.frontier := hi.items_allocated + refine hw.frontier_fits.of_le ?_ + fin_cases j <;> simp [layoutWords] <;> omega + +theorem implements {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C) : + (backend w C L).Implements (Stack.Memory.refinement hL) where + run o s := by cases o <;> rfl + entry {e} o hw hi hv := by + have hC := hL.items_length + cases o with + | push v => + obtain ⟨hlen, hv⟩ := hv + change (Stack.Memory.abs L e).length < C at hlen + rw [Stack.Memory.length_abs] at hlen + refine ⟨?_, (hC ▸ hL : L.Valid w L.items.length), hi, (by omega : e.mem L.count.base < L.items.length)⟩ + change ∀ j : Fin (4 + 1), Fits w (pushWords (L, v) j) + refine Fin.addCases (fun j => ?_) (fun j => ?_) + · simpa [pushWords, appendWords, Fin.appendEquiv] using layoutWords_fits hw hi j + · simpa [pushWords, appendWords, Fin.appendEquiv] using hv + | pop => exact ⟨layoutWords_fits hw hi, hC ▸ hL, hi⟩ + | size => exact ⟨layoutWords_fits hw hi, trivial⟩ + +/-- The native stack on layout `L`. -/ +noncomputable def implementation {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C) : + StateTransitionImpl (Stack.WordRAM.spec w C) ((backend w C L).exec w) := + Backend.implementation (implements hL) + +/-- One constant bounds every operation, before every width, capacity and layout. -/ +theorem instruction_bound : ∃ K, ∀ {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C), + Bound (implementation hL) id fun _ _ => K := + ⟨Backend.factor certificates * 23, fun hL => + Backend.bound (implements hL) (Stack.Memory.amortized hL) 19 fun o _ _ => by + cases o <;> exact Nat.le_of_ble_eq_true rfl⟩ + +/-- Adaptive clients run one operation at a time, within `K` instructions per operation. -/ +theorem run_client : ∃ K, ∀ {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C) {α : Type} + (p : (Stack.WordRAM.spec w C).Client α) {vs : List ℕ} {x : Data (Backend.registers certificates)}, + (implementation hL).rep vs x → + (Stack.WordRAM.spec w C).ValidHistory vs ((Stack.WordRAM.spec w C).clientHistory vs p) → + ∃ y traces, Executes ((backend w C L).exec w) p x + ((Stack.WordRAM.spec w C).clientResult vs p) y traces ∧ + (implementation hL).rep ((Stack.WordRAM.spec w C).clientState vs p) y ∧ + total id traces ≤ ((Stack.WordRAM.spec w C).clientHistory vs p).length * K := by + obtain ⟨K, hK⟩ := instruction_bound + exact ⟨K, fun hL _ p _ _ hr hv => Bound.exists_executes_const (hK hL) p hr hv⟩ + +-- The push value never selects code: every push runs the body of the `push` tag. +example (w C : ℕ) (L : Stack.Memory.Layout) : + Backend.code certificates ((backend w C L).tag (.push 3)) = + Backend.code certificates ((backend w C L).tag (.push 4)) := rfl + +-- A push answers `()` through an empty result encoding; its value is not echoed. +example : (certificates .push).l = 0 := rfl + +-- Every native execution from a represented stack takes the specified step, and its memory is +-- the method's final memory. +example {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C) {vs : List ℕ} + {x y : Data (Backend.registers certificates)} {o : StackOp ℕ} + {a : (Stack.WordRAM.spec w C).Result o} {n : ℕ} (hr : (implementation hL).rep vs x) + (hv : (Stack.WordRAM.spec w C).valid vs o) (he : (backend w C L).exec w x o a y n) : + a = ((Stack.WordRAM.spec w C).stepFn vs o).2 ∧ + memoryState (Backend.frontier certificates) y = (Memory.spec w).clientState + (memoryState (Backend.frontier certificates) x) (Stack.Memory.methods L w C o) := + ⟨((implementation hL).correct_ofFn hr hv he).1, + Backend.exec_memoryState (implements hL) hr hv he⟩ + +-- Two stacks with disjoint layouts: product operations select tags of `Tag ⊕ Tag`. +example {w C : ℕ} {L₁ L₂ : Stack.Memory.Layout} (h₁ : L₁.Valid w C) (h₂ : L₂.Valid w C) + (hd : Disjoint L₁.footprint L₂.footprint) : + ((backend w C L₁).sum (backend w C L₂)).Implements (Stack.Memory.pairRefinement h₁ h₂ hd) := + (implements h₁).prod (implements h₂) + +-- Doubling at every width of at least `2 * (log₂ n + 1)` bits, as one flat program. +example : UniformComputableInPolyTime .standard (fun n => n + n) (fun _ n => [n]) id := + (Realizes.addWords (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding 0 0).uniformComputableInPolyTime + .exactStandard 0 (.const 3) 2 (by decide) (fun _ _ j => by fin_cases j; rfl) + fun n w hw _ => by + have h2 : Fits w 2 := (show Fits 2 2 by decide).mono (le_trans (by simp [logWidth]) hw) + have hn : Fits w (n + n) := (fits_of_lt_pow (k := 2) (n := n) (by nlinarith)).mono hw + exact ⟨le_rfl, h2, hn, trivial, rfl, le_rfl⟩ + +end ComplexityTests.WordRAMRealizationBackend From f51bcd8465935fcff46fb0020102b9ae4f0a0e67 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 22:43:03 -0500 Subject: [PATCH 25/27] style(tests): wrap long lines in the backend test Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- ComplexityTests/WordRAMRealizationBackend.lean | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ComplexityTests/WordRAMRealizationBackend.lean b/ComplexityTests/WordRAMRealizationBackend.lean index 7a89dec273..501f0d5104 100644 --- a/ComplexityTests/WordRAMRealizationBackend.lean +++ b/ComplexityTests/WordRAMRealizationBackend.lean @@ -70,7 +70,8 @@ theorem implements {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C) : obtain ⟨hlen, hv⟩ := hv change (Stack.Memory.abs L e).length < C at hlen rw [Stack.Memory.length_abs] at hlen - refine ⟨?_, (hC ▸ hL : L.Valid w L.items.length), hi, (by omega : e.mem L.count.base < L.items.length)⟩ + refine ⟨?_, (hC ▸ hL : L.Valid w L.items.length), hi, + (by omega : e.mem L.count.base < L.items.length)⟩ change ∀ j : Fin (4 + 1), Fits w (pushWords (L, v) j) refine Fin.addCases (fun j => ?_) (fun j => ?_) · simpa [pushWords, appendWords, Fin.appendEquiv] using layoutWords_fits hw hi j @@ -92,7 +93,8 @@ theorem instruction_bound : ∃ K, ∀ {w C : ℕ} {L : Stack.Memory.Layout} (hL /-- Adaptive clients run one operation at a time, within `K` instructions per operation. -/ theorem run_client : ∃ K, ∀ {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C) {α : Type} - (p : (Stack.WordRAM.spec w C).Client α) {vs : List ℕ} {x : Data (Backend.registers certificates)}, + (p : (Stack.WordRAM.spec w C).Client α) {vs : List ℕ} + {x : Data (Backend.registers certificates)}, (implementation hL).rep vs x → (Stack.WordRAM.spec w C).ValidHistory vs ((Stack.WordRAM.spec w C).clientHistory vs p) → ∃ y traces, Executes ((backend w C L).exec w) p x From 7d6ef377766b5bc9cee4c9dec3e03e83ebbbf070 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 22:50:29 -0500 Subject: [PATCH 26/27] feat(WordRAM): Backend.data, rep_data and fits_appendWords Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../WordRAM/Realization/Backend.lean | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/Complexity/Computability/WordRAM/Realization/Backend.lean b/Complexity/Computability/WordRAM/Realization/Backend.lean index 9be6c0686b..63d9680b93 100644 --- a/Complexity/Computability/WordRAM/Realization/Backend.lean +++ b/Complexity/Computability/WordRAM/Realization/Backend.lean @@ -50,6 +50,7 @@ so `memoryCost`, which is taken at width zero, is the charge that refinements st * `Certificate`: a realization certificate with its program, encodings, domain and work. * `Backend.registers`, `Backend.frontier`, `Backend.code`, `Backend.factor`: the register file, the body of each tag and one exchange factor. +* `Backend.data`: the native data holding a memory state. * `Backend`: the tag, arguments and answer of each operation. * `Backend.Implements`: a backend runs the methods of a refinement. * `Backend.sum`: two backends side by side, for a product specification. @@ -59,8 +60,10 @@ so `memoryCost`, which is taken at width zero, is the charge that refinements st * `totalCost_width`, `clientHistory_width`, `memoryCost_eq_totalCost`: memory charges do not depend on the word width. +* `fits_appendWords`: appended argument words fit when both parts do. * `Backend.Implements.prod`: side-by-side backends run a product of refinements. * `Backend.exec_iff`, `Backend.rep_iff`: the execution relation and representation, unfolded. +* `Backend.rep_data`: the data holding an invariant state represents its abstraction. * `Backend.exec_memoryState`: an operation leaves its method's final memory. * `Backend.amortized`, `Backend.bound`: amortized and worst-case instruction bounds. -/ @@ -93,6 +96,13 @@ theorem memoryCost_eq_totalCost {α : Type} (w : ℕ) (p : Memory.Prog α) (s : rw [totalCost_width, clientHistory_width] rfl +/-- Appended argument words fit when both parts do. -/ +theorem fits_appendWords {δ α : Type} {k l w : ℕ} (input : δ ↪ (Fin k → ℕ)) + (output : α ↪ (Fin l → ℕ)) {d : δ} {a : α} (h₁ : ∀ i, Fits w (input d i)) + (h₂ : ∀ i, Fits w (output a i)) : ∀ i, Fits w (appendWords input output (d, a) i) := + Fin.addCases (fun i => by simpa [appendWords, Fin.appendEquiv] using h₁ i) + (fun i => by simpa [appendWords, Fin.appendEquiv] using h₂ i) + /-! ## Certificates and backends -/ /-- A uniform realization certificate, bundled with the program it realizes, its argument and @@ -180,6 +190,25 @@ noncomputable def factor : ℕ := Finset.univ.sup fun i => (C i).realizes.factor theorem factor_le (i : ι) : (C i).realizes.factor ≤ factor C := Finset.le_sup (f := fun i => (C i).realizes.factor) (Finset.mem_univ i) +/-- The native data holding the memory state `s`: its cells, the frontier in its register, and +zero in every other register. A calling-convention state, not an allocation. -/ +noncomputable def data (s : Memory.State) : Data (registers C) where + regs i := if i = frontier C then s.frontier else 0 + mem := s.mem + +/-- The data holding `s` abstracts to `s`. -/ +@[simp] theorem memoryState_data (s : Memory.State) : memoryState (frontier C) (data C s) = s := + Memory.State.ext rfl (by simp [memoryState, data]) + +/-- The data holding a well-formed memory state is word-bounded. -/ +theorem bounded_data {w : ℕ} {s : Memory.State} (hs : Memory.WellFormed w s) : + (data C s).Bounded w := by + refine ⟨fun i => ?_, hs.mem_fits⟩ + simp only [data] + split + · exact hs.frontier_fits + · exact fits_zero w + private theorem layout (i : ι) : Function.Injective (tmp C i) ∧ (∀ a b, src C i a ≠ tmp C i b) ∧ (∀ b, tmp C i b ≠ frontier C) ∧ ∀ a, src C i a ≠ frontier C := by @@ -357,6 +386,13 @@ theorem rep_iff (hS : S.Implements R) (s : D.State) (x : Data (registers C)) : (implementation hS).rep s x ↔ R.inv (memoryState (frontier C) x) ∧ R.abs (memoryState (frontier C) x) = s ∧ x.Bounded w := Iff.rfl +/-- The data holding a well-formed memory state that satisfies the invariant represents its +abstraction. -/ +theorem rep_data (hS : S.Implements R) {e : Memory.State} {s : D.State} + (hw : Memory.WellFormed w e) (hi : R.inv e) (ha : R.abs e = s) : + (implementation hS).rep s (data C e) := by + refine (rep_iff hS s _).mpr ⟨?_, ?_, bounded_data C hw⟩ <;> rw [memoryState_data] <;> assumption + /-- An operation leaves the memory and frontier of its method. -/ theorem exec_memoryState (hS : S.Implements R) {s : D.State} {x y : Data (registers C)} {o : D.Op} {a : D.Result o} {n : ℕ} (hr : (implementation hS).rep s x) (hv : D.valid s o) From fb0bf09414a9cfbf4c04a6a9f84ae5a2b6abcaae Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 22:50:59 -0500 Subject: [PATCH 27/27] test(WordRAM): backend test uses fits_appendWords and Backend.data Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- ComplexityTests/WordRAMRealizationBackend.lean | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/ComplexityTests/WordRAMRealizationBackend.lean b/ComplexityTests/WordRAMRealizationBackend.lean index 501f0d5104..9012d398d6 100644 --- a/ComplexityTests/WordRAMRealizationBackend.lean +++ b/ComplexityTests/WordRAMRealizationBackend.lean @@ -70,12 +70,9 @@ theorem implements {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C) : obtain ⟨hlen, hv⟩ := hv change (Stack.Memory.abs L e).length < C at hlen rw [Stack.Memory.length_abs] at hlen - refine ⟨?_, (hC ▸ hL : L.Valid w L.items.length), hi, - (by omega : e.mem L.count.base < L.items.length)⟩ - change ∀ j : Fin (4 + 1), Fits w (pushWords (L, v) j) - refine Fin.addCases (fun j => ?_) (fun j => ?_) - · simpa [pushWords, appendWords, Fin.appendEquiv] using layoutWords_fits hw hi j - · simpa [pushWords, appendWords, Fin.appendEquiv] using hv + exact ⟨fits_appendWords (d := L) (a := v) layoutWords + (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding (layoutWords_fits hw hi) fun _ => hv, + hC ▸ hL, hi, (by omega : e.mem L.count.base < L.items.length)⟩ | pop => exact ⟨layoutWords_fits hw hi, hC ▸ hL, hi⟩ | size => exact ⟨layoutWords_fits hw hi, trivial⟩ @@ -124,6 +121,13 @@ example {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C) {vs : List ℕ} ⟨((implementation hL).correct_ofFn hr hv he).1, Backend.exec_memoryState (implements hL) hr hv he⟩ +-- An empty stack of capacity two at address three, held in zero memory with the frontier at six, +-- is represented by the native data holding that memory state. +example : (implementation (Stack.Memory.valid_allocLayout (w := 4) (f := 3) (C := 2) + (by decide))).rep [] (Backend.data certificates ⟨fun _ => 0, 6⟩) := + Backend.rep_data _ ⟨by decide, fun _ => fits_zero 4⟩ ⟨le_of_lt (by decide), le_rfl, by simp⟩ + (by simp [Stack.Memory.refinement, Stack.Memory.abs]) + -- Two stacks with disjoint layouts: product operations select tags of `Tag ⊕ Tag`. example {w C : ℕ} {L₁ L₂ : Stack.Memory.Layout} (h₁ : L₁.Valid w C) (h₂ : L₂.Valid w C) (hd : Disjoint L₁.footprint L₂.footprint) :