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/40] 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/40] =?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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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 72dc1e61ff8794108c129ee11540eb9e564c0f47 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 22:21:47 -0500 Subject: [PATCH 23/40] refactor(Stack): migrate the stack to its realized implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Delete the hand-stepped `Stack/WordRAM/{Basic,Controller}` and re-derive their statements from `Stack.Memory`, C2's `MemoryStack.realizes_*` and `realize`: - `Stack.Spec`: the bounded stack, moved from `Stack.WordRAM.spec`. - `Stack.Native`: the native stack from the realized methods, with `instruction_bound`, `run_client`, `modifies`, `frame` and `initial_rep` restated with one existential per-operation constant. - `Stack.Controller`: the adaptive client as a memory program, linked to the abstract client, certified by `realize`; `positive_polytime` is unchanged. - Chan–He traversal keeps the stepped dispatcher it uses as `Traversal.WordStack` (push and nonempty pop, exact counts unchanged). - The controller and dynamic-program tests move to the exchange cell. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 6 +- .../ThreeSum/ChanHe/TraversalChildren.lean | 20 +- .../ChanHe/TraversalEmissionFrame.lean | 10 +- .../ThreeSum/ChanHe/TraversalExec.lean | 38 +- .../ThreeSum/ChanHe/TraversalInvariant.lean | 14 +- .../ThreeSum/ChanHe/TraversalLive.lean | 6 +- .../ThreeSum/ChanHe/TraversalLoop.lean | 4 +- .../ThreeSum/ChanHe/TraversalPending.lean | 14 +- .../ThreeSum/ChanHe/TraversalProgram.lean | 8 +- .../ThreeSum/ChanHe/TraversalRoot.lean | 28 +- .../ThreeSum/ChanHe/TraversalSkip.lean | 4 +- .../ThreeSum/ChanHe/TraversalStack.lean | 126 +++--- .../ThreeSum/ChanHe/TraversalWordStack.lean | 159 +++++++ Complexity/DataStructure/Queue/Memory.lean | 2 +- .../DataStructure/Stack/Controller.lean | 245 +++++++++++ Complexity/DataStructure/Stack/Memory.lean | 22 +- Complexity/DataStructure/Stack/Native.lean | 392 ++++++++++++++++++ Complexity/DataStructure/Stack/Spec.lean | 43 ++ .../DataStructure/Stack/WordRAM/Basic.lean | 361 ---------------- .../Stack/WordRAM/Controller.lean | 234 ----------- ComplexityTests/DataStructureStack.lean | 46 ++ ComplexityTests/NativeChanHeTraversal.lean | 16 +- ComplexityTests/WordRAMController.lean | 242 ++++++----- ComplexityTests/WordRAMDynamic.lean | 55 +-- ROADMAP.md | 6 +- UPSTREAM.md | 7 +- 26 files changed, 1172 insertions(+), 936 deletions(-) create mode 100644 Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalWordStack.lean create mode 100644 Complexity/DataStructure/Stack/Controller.lean create mode 100644 Complexity/DataStructure/Stack/Native.lean create mode 100644 Complexity/DataStructure/Stack/Spec.lean delete mode 100644 Complexity/DataStructure/Stack/WordRAM/Basic.lean delete mode 100644 Complexity/DataStructure/Stack/WordRAM/Controller.lean create mode 100644 ComplexityTests/DataStructureStack.lean diff --git a/Complexity.lean b/Complexity.lean index 04e0c69a15..0ee29ba8ed 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -497,6 +497,7 @@ public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.TraversalStack public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.TraversalStep public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.TraversalTime public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.TraversalVisit +public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.TraversalWordStack public import Complexity.Algorithms.WordRAM.ThreeSum.Decision public import Complexity.Algorithms.WordRAM.ThreeSum.Decision.Body public import Complexity.Algorithms.WordRAM.ThreeSum.Decision.Boundary @@ -2931,9 +2932,10 @@ 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.Controller public import Complexity.DataStructure.Stack.Memory -public import Complexity.DataStructure.Stack.WordRAM.Basic -public import Complexity.DataStructure.Stack.WordRAM.Controller +public import Complexity.DataStructure.Stack.Native +public import Complexity.DataStructure.Stack.Spec public import Complexity.DataStructure.StateTransition public import Complexity.DataStructure.StateTransition.Amortized public import Complexity.DataStructure.StateTransition.Composition diff --git a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalChildren.lean b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalChildren.lean index edc5328811..912081e456 100644 --- a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalChildren.lean +++ b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalChildren.lean @@ -32,7 +32,7 @@ The native stack controller and proofs are original. namespace Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Traversal -open Cslib.WordRAM Cslib.DataStructure +open Cslib.WordRAM /-- Save both descriptors needed for restoration and prepare the third child. -/ def thirdState (x : Data 128) : Data 128 := @@ -112,27 +112,27 @@ top-first stack contains first, second, third, followed by the original pending theorem pushChildren_exec {w base capacity : ℕ} (x : Data 128) (vs : List ℕ) (hx : x.Bounded w) (hone : x.regs one = 1) (h2 : Fits w 2) (hcap : Fits w (base + capacity)) - (hr : Stack.WordRAM.Rep w base capacity vs (x.comap registers)) + (hr : WordStack.Rep w base capacity vs (x.comap registers)) (hcapacity : 21 + vs.length ≤ capacity) : ∃ y, Exec w pushChildren x y 207 ∧ - Stack.WordRAM.Rep w base capacity (childWords x 0 ++ childWords x 1 ++ childWords x 2 ++ vs) + WordStack.Rep w base capacity (childWords x 0 ++ childWords x 1 ++ childWords x 2 ++ vs) (y.comap registers) ∧ - Modifies (Stack.WordRAM.footprint base capacity) x.mem y.mem ∧ + Modifies (WordStack.footprint base capacity) x.mem y.mem ∧ ∀ i, ((71 ≤ i.val ∧ i.val < 96) ∨ 109 ≤ i.val) → y.regs i = x.regs i := by let a := thirdState x have ha := prepareThird_exec x hx hone - have hra : Stack.WordRAM.Rep w base capacity vs (a.comap registers) := by + have hra : WordStack.Rep w base capacity vs (a.comap registers) := by simpa only [a, thirdState_comap] using hr obtain ⟨b, hb, hrb, hfb, hpb⟩ := pushFrame_exec a vs h2 hcap hra (fun i => (ha.bounded hx).1 (slot i)) (by omega) have hwa : words a = childWords x 2 := by simp +decide [words, a, thirdState, childWords, Data.set, slot, original, heavyBase, heavyLength] - change Stack.WordRAM.Rep w base capacity (words a ++ vs) (b.comap registers) at hrb + change WordStack.Rep w base capacity (words a ++ vs) (b.comap registers) at hrb rw [hwa] at hrb let c := secondState b have hc := prepareSecond_exec (w := w) b - have hrc : Stack.WordRAM.Rep w base capacity (childWords x 2 ++ vs) (c.comap registers) := by + have hrc : WordStack.Rep w base capacity (childWords x 2 ++ vs) (c.comap registers) := by simpa only [c, secondState_comap] using hrb obtain ⟨d, hd, hrd, hfd, hpd⟩ := pushFrame_exec c (childWords x 2 ++ vs) h2 hcap hrc (fun i => (hc.bounded (hb.bounded (ha.bounded hx))).1 (slot i)) @@ -140,12 +140,12 @@ theorem pushChildren_exec {w base capacity : ℕ} (x : Data 128) (vs : List ℕ) have hwc : words c = childWords x 1 := by simp +decide [words, c, secondState, hpb, Outside, a, thirdState, childWords, Data.set, slot, original, heavyBase, heavyLength] - change Stack.WordRAM.Rep w base capacity (words c ++ (childWords x 2 ++ vs)) + change WordStack.Rep w base capacity (words c ++ (childWords x 2 ++ vs)) (d.comap registers) at hrd rw [hwc] at hrd let e := firstState d have he := prepareFirst_exec (w := w) d - have hre : Stack.WordRAM.Rep w base capacity (childWords x 1 ++ (childWords x 2 ++ vs)) + have hre : WordStack.Rep w base capacity (childWords x 1 ++ (childWords x 2 ++ vs)) (e.comap registers) := by simpa only [e, firstState_comap] using hrd obtain ⟨z, hz, hrz, hfz, _⟩ := pushFrame_exec e (childWords x 1 ++ (childWords x 2 ++ vs)) @@ -155,7 +155,7 @@ theorem pushChildren_exec {w base capacity : ℕ} (x : Data 128) (vs : List ℕ) have hwe : words e = childWords x 0 := by simp +decide [words, e, firstState, hpd, Outside, c, secondState, hpb, a, thirdState, childWords, Data.set, slot, original, heavyBase, heavyLength] - change Stack.WordRAM.Rep w base capacity (words e ++ (childWords x 1 ++ (childWords x 2 ++ vs))) + change WordStack.Rep w base capacity (words e ++ (childWords x 1 ++ (childWords x 2 ++ vs))) (z.comap registers) at hrz rw [hwe] at hrz have hrun : Exec w pushChildren x z 207 := ha.seq (hb.seq (hc.seq (hd.seq (he.seq hz)))) diff --git a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalEmissionFrame.lean b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalEmissionFrame.lean index ab5cd89902..e26de80c8e 100644 --- a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalEmissionFrame.lean +++ b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalEmissionFrame.lean @@ -30,7 +30,7 @@ These memory-frame and representation proofs are original. namespace Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Traversal -open Cslib.WordRAM Cslib.DataStructure +open Cslib.WordRAM /-- An actual emission preserves each old-prefix cell except its one explicit table store. -/ theorem Emission.prefix_preserved {Q : Finset ℕ} {Λ : ℕ} {e : Entry} {x y : Data 128} @@ -196,16 +196,16 @@ length registers; its sole external memory store lies above the stack reservatio theorem Emission.stack {Q : Finset ℕ} {Λ w : ℕ} {e : Entry} {x y : Data 128} (h : Emission Q Λ e x y) {a : Arena} (ha : a.Separated) (hc : a.Controls x) (hy : y.Bounded w) (vs : List ℕ) - (hr : Stack.WordRAM.Rep w a.stack a.capacity vs (x.comap Traversal.registers)) + (hr : WordStack.Rep w a.stack a.capacity vs (x.comap Traversal.registers)) (htable : x.regs outputs + x.regs outputCount < x.regs heap) : - Stack.WordRAM.Rep w a.stack a.capacity vs (y.comap Traversal.registers) := by + WordStack.Rep w a.stack a.capacity vs (y.comap Traversal.registers) := by have hregs (i : Fin 9) : y.regs (Traversal.registers i) = x.regs (Traversal.registers i) := by apply h.registers <;> first | (dsimp [Traversal.registers]; omega) | (intro he; have := congrArg Fin.val he; dsimp [Traversal.registers] at this; omega) have ht : x.regs outputs = a.table := hc.2.2.2.2.2.2.1 - refine ⟨⟨fun i => hy.1 (Traversal.registers i), hy.2⟩, (hregs Stack.WordRAM.ptr).trans hr.pointer, - (hregs Stack.WordRAM.len).trans hr.length_eq, hr.capacity_le, ?_⟩ + refine ⟨⟨fun i => hy.1 (Traversal.registers i), hy.2⟩, (hregs WordStack.ptr).trans hr.pointer, + (hregs WordStack.len).trans hr.length_eq, hr.capacity_le, ?_⟩ intro i have hi : i.val < a.capacity := lt_of_lt_of_le i.isLt hr.capacity_le change y.mem (a.stack + i.val) = _ diff --git a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalExec.lean b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalExec.lean index e2034fcbf6..7a3f7da3f7 100644 --- a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalExec.lean +++ b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalExec.lean @@ -29,7 +29,7 @@ proved in `TraversalRoot.root_exec`; complete initialized-input preprocessing is namespace Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Traversal -open Cslib.WordRAM Cslib.DataStructure +open Cslib.WordRAM /-- Even zero fuel executes all native setup and stack work. No node is emitted, and the root record is recovered exactly. Existing output arrays and all non-stack memory are preserved. -/ @@ -37,39 +37,39 @@ theorem oneRoot_zero {w capacity : ℕ} (x : Data 128) (hx : x.Bounded w) (h2 : Fits w 2) (hcap : Fits w (x.regs stackBase + capacity)) (hcapacity : 7 ≤ capacity) (hf : x.regs (slot 0) = 0) : ∃ y, Exec w oneRoot x y 173 ∧ - Stack.WordRAM.Rep w (x.regs stackBase) capacity [] (y.comap registers) ∧ + WordStack.Rep w (x.regs stackBase) capacity [] (y.comap registers) ∧ (∀ i, y.regs (slot i) = x.regs (slot i)) ∧ y.regs outputCount = x.regs outputCount ∧ y.regs heap = x.regs heap ∧ - Modifies (Stack.WordRAM.footprint (x.regs stackBase) capacity) x.mem y.mem := by - let a := (((x.set zero 0).set one 1).set (registers Stack.WordRAM.ptr) (x.regs stackBase)).set - (registers Stack.WordRAM.len) 0 + Modifies (WordStack.footprint (x.regs stackBase) capacity) x.mem y.mem := by + let a := (((x.set zero 0).set one 1).set (registers WordStack.ptr) (x.regs stackBase)).set + (registers WordStack.len) 0 have h1 : Fits w 1 := h2.of_le (by decide) have ha : Exec (B := .standard) w (.seq (.op (.const zero 0)) (.seq (.op (.const one 1)) - (.seq (.op (.copy (registers Stack.WordRAM.ptr) stackBase)) - (.op (.const (registers Stack.WordRAM.len) 0))))) x a 4 := by - ram_eval [a, zero, one, registers, Stack.WordRAM.ptr, Stack.WordRAM.len, stackBase, h1] + (.seq (.op (.copy (registers WordStack.ptr) stackBase)) + (.op (.const (registers WordStack.len) 0))))) x a 4 := by + ram_eval [a, zero, one, registers, WordStack.ptr, WordStack.len, stackBase, h1] have haslots (i : Fin 7) : a.regs (slot i) = x.regs (slot i) := by fin_cases i <;> - simp +decide [a, slot, registers, Stack.WordRAM.ptr, Stack.WordRAM.len, zero, one] - have har : Stack.WordRAM.Rep w (x.regs stackBase) capacity [] (a.comap registers) := by + simp +decide [a, slot, registers, WordStack.ptr, WordStack.len, zero, one] + have har : WordStack.Rep w (x.regs stackBase) capacity [] (a.comap registers) := by refine ⟨⟨fun i => (ha.bounded hx).1 (registers i), (ha.bounded hx).2⟩, ?_, ?_, Nat.zero_le _, fun i => Fin.elim0 i⟩ - · simp +decide [a, registers, Stack.WordRAM.ptr, Stack.WordRAM.len, zero, one] - · simp +decide [a, registers, Stack.WordRAM.ptr, Stack.WordRAM.len, zero, one] + · simp +decide [a, registers, WordStack.ptr, WordStack.len, zero, one] + · simp +decide [a, registers, WordStack.ptr, WordStack.len, zero, one] obtain ⟨b, hb, hrb, hfb, hpb⟩ := pushFrame_exec a [] h2 hcap har (fun i => (ha.bounded hx).1 (slot i)) (by simpa using hcapacity) obtain ⟨c, hc, hrc, hcs, hfc, hpc⟩ := popFrame_exec b [] (fun i => a.regs (slot i)) h2 hcap hrb have hbzero : b.regs zero = 0 := by rw [hpb zero (Or.inl (by decide))] - simp +decide [a, registers, Stack.WordRAM.ptr, Stack.WordRAM.len, zero, one] + simp +decide [a, registers, WordStack.ptr, WordStack.len, zero, one] have hczero : c.regs zero = 0 := by rw [hpc zero (Or.inl (by decide)) (by decide), hbzero] have hcf : c.regs (slot 0) = 0 := (hcs 0).trans ((haslots 0).trans hf) - have hblen : b.regs (registers Stack.WordRAM.len) = 7 := by + have hblen : b.regs (registers WordStack.len) = 7 := by simpa only [Data.comap_regs, List.length_append, List.length_cons, List.length_nil, Nat.reduceAdd] using hrb.length_eq - have hclen : c.regs (registers Stack.WordRAM.len) = 0 := hrc.length_eq + have hclen : c.regs (registers WordStack.len) = 0 := hrc.length_eq have hcopy : Exec (B := .standard) w (.op (.copy (slot 0) (slot 0))) c c 1 := by convert (Exec.copy (B := Basis.standard) (w := w) (x := c) (d := slot 0) (s := slot 0)) using 1 @@ -78,17 +78,17 @@ theorem oneRoot_zero {w capacity : ℕ} (x : Data 128) (hx : x.Bounded w) exact hc.seq (Exec.branch_pos (by change c.regs (slot 0) = c.regs zero; omega) hcopy) have hl : Exec w loop b c 105 := by apply Exec.loop_once - · change b.regs zero < b.regs (registers Stack.WordRAM.len) + · change b.regs zero < b.regs (registers WordStack.len) omega · exact hbody - · change ¬c.regs zero < c.regs (registers Stack.WordRAM.len) + · change ¬c.regs zero < c.regs (registers WordStack.len) omega refine ⟨c, ha.seq (hb.seq hl), hrc, fun i => (hcs i).trans (haslots i), ?_, ?_, ?_⟩ · rw [hpc outputCount (Or.inl (by decide)) (by decide), hpb outputCount (Or.inl (by decide))] - simp +decide [a, registers, Stack.WordRAM.ptr, Stack.WordRAM.len, outputCount, zero, one] + simp +decide [a, registers, WordStack.ptr, WordStack.len, outputCount, zero, one] · rw [hpc heap (Or.inl (by decide)) (by decide), hpb heap (Or.inl (by decide))] - simp +decide [a, registers, Stack.WordRAM.ptr, Stack.WordRAM.len, heap, zero, one] + simp +decide [a, registers, WordStack.ptr, WordStack.len, heap, zero, one] · intro p hp exact (hfc p hp).trans (hfb p hp) diff --git a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalInvariant.lean b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalInvariant.lean index 0caa35d86b..60bcc9d209 100644 --- a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalInvariant.lean +++ b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalInvariant.lean @@ -33,7 +33,7 @@ These proof invariants are original and do not add a machine model or operationa namespace Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Traversal -open Cslib.WordRAM Cslib.DataStructure +open Cslib.WordRAM open Cslib.Algorithms.ThreeSum.ChanHe (Node) /-- Fixed parameters and concrete memory reservations of a complete native root traversal. -/ @@ -122,7 +122,7 @@ structure Cursor.Invariant (a : Arena) (w : ℕ) (target : List Node) /-- The actual bitmap still represents all candidate primes. -/ bitmap : a.Bitmap x.mem /-- The actual word stack represents exactly the pending entries. -/ - stack : Stack.WordRAM.Rep (w + 2) a.stack a.capacity + stack : WordStack.Rep (w + 2) a.stack a.capacity (s.pending.flatMap Entry.words) (x.comap registers) /-- The existing finite depth invariant bounds pending entries. -/ depth : Math.Bounded a.fuel (s.pending.map Entry.task) @@ -156,24 +156,24 @@ structure Cursor.Invariant (a : Arena) (w : ℕ) (target : List Node) /-- A live loop has at least one pending entry, since each entry occupies seven actual words. -/ theorem Cursor.Invariant.nonempty {a : Arena} {w : ℕ} {target : List Node} {s : Cursor} {x : Data 128} (h : s.Invariant a w target x) - (hc : (Cond.lt zero (registers Stack.WordRAM.len)).Holds x) : s.pending ≠ [] := by + (hc : (Cond.lt zero (registers WordStack.len)).Holds x) : s.pending ≠ [] := by intro he have hz := h.controls.2.2.2.2.2.2.2.2.2.1 have hl := h.stack.length_eq - change x.regs zero < x.regs (registers Stack.WordRAM.len) at hc + change x.regs zero < x.regs (registers WordStack.len) at hc simp only [he, List.flatMap_nil, List.length_nil, Data.comap_regs] at hl omega /-- A finished actual loop has emitted the complete target, with every array already read back. -/ theorem Cursor.Invariant.finished {a : Arena} {w : ℕ} {target : List Node} {s : Cursor} {x : Data 128} (h : s.Invariant a w target x) - (hc : ¬(Cond.lt zero (registers Stack.WordRAM.len)).Holds x) : + (hc : ¬(Cond.lt zero (registers WordStack.len)).Holds x) : s.pending = [] ∧ s.emitted.map Prod.snd = target := by have hz := h.controls.2.2.2.2.2.2.2.2.2.1 have hl := h.stack.length_eq rw [length_serialized] at hl - change ¬x.regs zero < x.regs (registers Stack.WordRAM.len) at hc - change x.regs (registers Stack.WordRAM.len) = 7 * s.pending.length at hl + change ¬x.regs zero < x.regs (registers WordStack.len) at hc + change x.regs (registers WordStack.len) = 7 * s.pending.length at hl have he : s.pending = [] := List.eq_nil_of_length_eq_zero (by omega) refine ⟨he, ?_⟩ simpa only [he, List.map_nil, Math.output, List.flatMap_nil, List.append_nil] using h.exactOutput diff --git a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalLive.lean b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalLive.lean index f464b565c7..bc490ba1cd 100644 --- a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalLive.lean +++ b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalLive.lean @@ -30,7 +30,7 @@ These native composition and invariant proofs are original. namespace Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Traversal -open Cslib.WordRAM Cslib.DataStructure +open Cslib.WordRAM open Cslib.Algorithms.ThreeSum.ChanHe (Node) /-- Execute and verify the whole live native worklist transition. -/ @@ -54,7 +54,7 @@ theorem live_transition {a : Arena} {w : ℕ} {target : List Node} exact preg i (by omega) (by omega)) have frame (q : ℕ) (hq : q < a.stack ∨ a.stack + a.capacity ≤ q) : p.mem q = x.mem q := by apply hframe - simp only [Stack.WordRAM.footprint, Block.mem_footprint, Block.Owns] + simp only [WordStack.footprint, Block.mem_footprint, Block.Owns] omega have gap : a.capacity ≤ a.floor - a.stack := by have ht := ha.2.1 @@ -128,7 +128,7 @@ theorem live_transition {a : Arena} {w : ℕ} {target : List Node} y.regs i = v.regs i := hyr i (Or.inl ⟨hi, hi'⟩) have yframe (q : ℕ) (hq : q < a.stack ∨ a.stack + a.capacity ≤ q) : y.mem q = v.mem q := by apply hyframe - simp only [Stack.WordRAM.footprint, Block.mem_footprint, Block.Owns] + simp only [WordStack.footprint, Block.mem_footprint, Block.Owns] omega have hbody := body_live hp hl hc.2.2.2.2.2.2.2.2.2.1 hf hn hv hy have vsStored := em.stored ha hc (oldStored e (by simp)) (h.around e (by simp)) diff --git a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalLoop.lean b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalLoop.lean index d41a5ca425..df54a4c05c 100644 --- a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalLoop.lean +++ b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalLoop.lean @@ -32,14 +32,14 @@ composition is a separate obligation. namespace Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Traversal -open Cslib.WordRAM Cslib.DataStructure +open Cslib.WordRAM open Cslib.Algorithms.ThreeSum.ChanHe (Node) /-- Every actual live loop header admits a native body execution with the same finite program. -/ theorem transition {a : Arena} {w : ℕ} {target : List Node} (ha : a.Separated) (hw : a.Ready w) (h2 : Fits (w + 2) 2) (hcap : Fits (w + 2) (a.stack + a.capacity)) (s : Cursor) (x : Data 128) - (h : s.Invariant a w target x) (hc : (Cond.lt zero (registers Stack.WordRAM.len)).Holds x) : + (h : s.Invariant a w target x) (hc : (Cond.lt zero (registers WordStack.len)).Holds x) : ∃ (s' : Cursor) (y : Data 128), ExecWithin (w + 2) body x y (visitTime a.n a.m a.N + 314) ∧ s'.Invariant a w target y ∧ s'.work < s.work := by diff --git a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalPending.lean b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalPending.lean index db92d7a49c..4518570c57 100644 --- a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalPending.lean +++ b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalPending.lean @@ -33,7 +33,7 @@ These memory-layout and composition proofs are original. namespace Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Traversal -open Cslib.WordRAM Cslib.DataStructure +open Cslib.WordRAM /-- Every column lies entirely on one side of the reserved stack interval. -/ def Entry.Around (e : Entry) (base capacity : ℕ) : Prop := @@ -50,11 +50,11 @@ theorem Entry.Around.mono_capacity {e : Entry} {base small large : ℕ} /-- A concrete stack frame cannot alter any live column around its reserved interval. -/ theorem Entry.Stored.of_stack_frame {e : Entry} {before after : ℕ → ℕ} {base capacity : ℕ} (h : e.Stored before) (hg : e.Around base capacity) - (hf : Modifies (Stack.WordRAM.footprint base capacity) before after) : e.Stored after := by + (hf : Modifies (WordStack.footprint base capacity) before after) : e.Stored after := by apply h.congr intro j i hi apply hf - simp only [Stack.WordRAM.footprint, Block.mem_footprint, Block.Owns] + simp only [WordStack.footprint, Block.mem_footprint, Block.Owns] rcases hg j with hj | hj <;> omega /-- A newly allocated heavy block remains above the stack; untouched columns retain their gap. -/ @@ -71,14 +71,14 @@ theorem Entry.Around.child {e : Entry} {base capacity p : ℕ} (h : e.Around bas changes memory only inside the reserved stack interval. -/ theorem pop_entry {w base capacity : ℕ} (x : Data 128) (e : Entry) (es : List Entry) (h2 : Fits w 2) (hcap : Fits w (base + capacity)) - (hr : Stack.WordRAM.Rep w base capacity ((e :: es).flatMap Entry.words) (x.comap registers)) : + (hr : WordStack.Rep w base capacity ((e :: es).flatMap Entry.words) (x.comap registers)) : ∃ y, Exec w popFrame x y 99 ∧ - Stack.WordRAM.Rep w base capacity (es.flatMap Entry.words) (y.comap registers) ∧ - e.Loaded y ∧ Modifies (Stack.WordRAM.footprint base capacity) x.mem y.mem ∧ + WordStack.Rep w base capacity (es.flatMap Entry.words) (y.comap registers) ∧ + e.Loaded y ∧ Modifies (WordStack.footprint base capacity) x.mem y.mem ∧ ∀ j, Outside j → (j.val < 64 ∨ 71 ≤ j.val) → y.regs j = x.regs j := by let a : Fin 7 → ℕ := ![e.fuel, e.source 0, (e.data 0).length, e.source 1, (e.data 1).length, e.source 2, (e.data 2).length] - have hrep : Stack.WordRAM.Rep w base capacity + have hrep : WordStack.Rep w base capacity ([a 0, a 1, a 2, a 3, a 4, a 5, a 6] ++ es.flatMap Entry.words) (x.comap registers) := by simpa +decide [a, List.flatMap_cons, Entry.words] using hr diff --git a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalProgram.lean b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalProgram.lean index 83c1c1a20f..f3c76c36af 100644 --- a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalProgram.lean +++ b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalProgram.lean @@ -37,7 +37,7 @@ The native code is original and reuses the existing finite Chan–He constructio namespace Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Traversal -open Cslib.WordRAM Cslib.DataStructure +open Cslib.WordRAM /-- The complete node kernels occupy registers 0–63. -/ def kernel (i : Fin 64) : Fin 128 := ⟨i.val, by omega⟩ @@ -212,13 +212,13 @@ def body : Code .standard 128 := (.branch (.eq n₂ zero) skip (.seq visit pushChildren))))) /-- The actual pending-word stack, including zero-fuel and empty entries, controls the loop. -/ -def loop : Code .standard 128 := .loop (.lt zero (registers Stack.WordRAM.len)) body +def loop : Code .standard 128 := .loop (.lt zero (registers WordStack.len)) body /-- The complete finite native controller for one supplied root. It appends to the caller's output table, retaining the accumulated count and advancing the fresh allocation base. -/ def oneRoot : Code .standard 128 := - let ptr := registers Stack.WordRAM.ptr - let len := registers Stack.WordRAM.len + let ptr := registers WordStack.ptr + let len := registers WordStack.len .seq (ram[ zero ← #0; one ← #1; ptr ← stackBase; len ← #0 ]) (.seq pushFrame loop) diff --git a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalRoot.lean b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalRoot.lean index 7056e207b7..f23535910a 100644 --- a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalRoot.lean +++ b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalRoot.lean @@ -32,7 +32,7 @@ logarithmic-width/subquadratic-cost composition remain separate obligations. namespace Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Traversal -open Cslib.WordRAM Cslib.DataStructure +open Cslib.WordRAM open Cslib.Algorithms.ThreeSum.ChanHe (Node) /-- Concrete caller obligations before the complete native root program initializes its stack. -/ @@ -95,21 +95,21 @@ theorem root_exec {a : Arena} {w : ℕ} (e : Entry) (ns : List (ℕ × Node)) (x have ht := ha.2.1 have hgap := ha.2.2.1 omega) - let p := (((x.set zero 0).set one 1).set (registers Stack.WordRAM.ptr) (x.regs stackBase)).set - (registers Stack.WordRAM.len) 0 - let ptr := registers Stack.WordRAM.ptr - let len := registers Stack.WordRAM.len + let p := (((x.set zero 0).set one 1).set (registers WordStack.ptr) (x.regs stackBase)).set + (registers WordStack.len) 0 + let ptr := registers WordStack.ptr + let len := registers WordStack.len have h1 : Fits (w + 2) 1 := h2.of_le (by decide) have hp : Exec (B := .standard) (w + 2) (ram[ zero ← #0; one ← #1; ptr ← stackBase; len ← #0 ]) x p 4 := by - ram_eval [p, ptr, len, zero, one, registers, Stack.WordRAM.ptr, Stack.WordRAM.len, + ram_eval [p, ptr, len, zero, one, registers, WordStack.ptr, WordStack.len, stackBase, h1] have preg (i : Fin 128) (hi : i.val < 96) : p.regs i = ((x.set zero 0).set one 1).regs i := by - have hp' : i ≠ registers Stack.WordRAM.ptr := by - intro he; have := congrArg Fin.val he; norm_num [registers, Stack.WordRAM.ptr] at this; omega - have hl' : i ≠ registers Stack.WordRAM.len := by - intro he; have := congrArg Fin.val he; norm_num [registers, Stack.WordRAM.len] at this; omega + have hp' : i ≠ registers WordStack.ptr := by + intro he; have := congrArg Fin.val he; norm_num [registers, WordStack.ptr] at this; omega + have hl' : i ≠ registers WordStack.len := by + intro he; have := congrArg Fin.val he; norm_num [registers, WordStack.len] at this; omega simp only [p, Data.set_regs, Function.update_of_ne hp', Function.update_of_ne hl'] have pmeta (i : Fin 128) (hi : i.val < 96) : p.regs i = x.regs i := by rw [preg i hi] @@ -130,12 +130,12 @@ theorem root_exec {a : Arena} {w : ℕ} (e : Entry) (ns : List (ℕ × Node)) (x exact (pmeta _ (by dsimp [arraySource, slot]; omega)).trans (h.loaded.2.1 j) · intro j exact (pmeta _ (by dsimp [arrayLength, slot]; omega)).trans (h.loaded.2.2 j) - have pstack : Stack.WordRAM.Rep (w + 2) a.stack a.capacity [] (p.comap registers) := by + have pstack : WordStack.Rep (w + 2) a.stack a.capacity [] (p.comap registers) := by refine ⟨⟨fun i => (hp.bounded h.bounded).1 (registers i), (hp.bounded h.bounded).2⟩, ?_, ?_, Nat.zero_le _, fun i => Fin.elim0 i⟩ - · simpa +decide [p, Data.comap, Data.set, registers, Stack.WordRAM.ptr, Stack.WordRAM.len, + · simpa +decide [p, Data.comap, Data.set, registers, WordStack.ptr, WordStack.len, zero, one] using h.controls.2.2.2.2.2.2.2.1 - · simp +decide [p, Data.comap, Data.set, registers, Stack.WordRAM.ptr, Stack.WordRAM.len, + · simp +decide [p, Data.comap, Data.set, registers, WordStack.ptr, WordStack.len, zero, one] obtain ⟨b, hb, hstack, hframe, hbr⟩ := pushFrame_exec p [] h2 hcap pstack (fun i => (hp.bounded h.bounded).1 (slot i)) (by @@ -146,7 +146,7 @@ theorem root_exec {a : Arena} {w : ℕ} (e : Entry) (ns : List (ℕ × Node)) (x (hbr i (Or.inl hi)).trans (pmeta i hi) have bframe (q : ℕ) (hq : q < a.stack ∨ a.stack + a.capacity ≤ q) : b.mem q = x.mem q := by apply hframe - simp only [Stack.WordRAM.footprint, Block.mem_footprint, Block.Owns] + simp only [WordStack.footprint, Block.mem_footprint, Block.Owns] omega let s : Cursor := ⟨[e], ns⟩ have work : s.work = Math.work e.fuel := by diff --git a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalSkip.lean b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalSkip.lean index 8e0ba23d90..f5cbd61dd1 100644 --- a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalSkip.lean +++ b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalSkip.lean @@ -29,7 +29,7 @@ These native composition and invariant proofs are original. namespace Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Traversal -open Cslib.WordRAM Cslib.DataStructure +open Cslib.WordRAM open Cslib.Algorithms.ThreeSum.ChanHe (Node) /-- Popping a skipped entry executes the complete native body and preserves every part of the @@ -56,7 +56,7 @@ theorem skip_transition {a : Arena} {w : ℕ} {target : List Node} have hbody := body_skip hy hloaded hcontrols.2.2.2.2.2.2.2.2.2.1 hskip have hmem (p : ℕ) (hp : p < a.stack ∨ a.stack + a.capacity ≤ p) : y.mem p = x.mem p := by apply hframe - simp only [Stack.WordRAM.footprint, Block.mem_footprint, Block.Owns] + simp only [WordStack.footprint, Block.mem_footprint, Block.Owns] omega have hstep : Math.step (Nat.primesLE a.m) a.Λ e.task (es.map Entry.task) = (es.map Entry.task, none) := by diff --git a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalStack.lean b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalStack.lean index ac293a1d2d..4793a0bb45 100644 --- a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalStack.lean +++ b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalStack.lean @@ -7,25 +7,24 @@ Authors: Erik Demaine module -- upstream: candidate -public import Complexity.DataStructure.Stack.WordRAM.Basic +public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.TraversalWordStack import Mathlib.Tactic.FinCases /-! # Native stack calls for Chan–He traversal records -Traversal keeps a seven-word record in registers 64–70 and the existing bounded stack in +Traversal keeps a seven-word record in registers 64–70 and its word stack in registers 96–104. Registers below 64 remain available to the complete modulus and node programs. Each record contains fuel followed by three base/length pairs. The stack stores words, so a record is pushed in reverse order and popped in forward order. -The dispatcher, representation and memory frame are reused from -`Cslib.DataStructure.Stack.WordRAM`. +The dispatcher, representation and memory frame are those of `Traversal.WordStack`. Argument delivery and result copies here are actual native instructions and are charged. ## Main definitions * `slot`: the seven persistent traversal-record registers. -* `registers`: the existing nine-register stack dispatcher inside the 128-register controller. +* `registers`: the nine-register stack dispatcher inside the 128-register controller. * `pushSlot`: native argument delivery followed by the complete bounded-stack dispatcher. * `popSlot`: native dispatch and result delivery to one traversal-record register. @@ -42,7 +41,7 @@ The native controller and its composition proofs are original. namespace Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Traversal -open Cslib.WordRAM Cslib.DataStructure +open Cslib.WordRAM /-- Fuel and the three base/length pairs, retained across both 64-register native kernels. -/ def slot (i : Fin 7) : Fin 128 := ⟨64 + i.val, by omega⟩ @@ -82,48 +81,45 @@ private theorem extend_outside (a : Data 9) (x : Data 128) (i : Fin 128) (hi : O /-- Deliver the push tag and actual record word, then execute the existing stack program. -/ def pushSlot (i : Fin 7) : Code .standard 128 := - let tag := registers Stack.WordRAM.tag - let arg := registers Stack.WordRAM.arg + let tag := registers WordStack.tag + let arg := registers WordStack.arg let src := slot i - .seq (ram[ tag ← #0; arg ← src ]) (Stack.WordRAM.program.rename registers) + .seq (ram[ tag ← #0; arg ← src ]) (WordStack.program.rename registers) /-- Each native push includes both argument instructions, not a host-level stack operation. Every register outside 96–104 and every memory cell outside the reserved stack is preserved. -/ theorem pushSlot_exec {w base capacity : ℕ} (i : Fin 7) (x : Data 128) (vs : List ℕ) (h2 : Fits w 2) (hcap : Fits w (base + capacity)) - (hr : Stack.WordRAM.Rep w base capacity vs (x.comap registers)) + (hr : WordStack.Rep w base capacity vs (x.comap registers)) (hw : Fits w (x.regs (slot i))) (hv : vs.length < capacity) : ∃ y, Exec w (pushSlot i) x y 9 ∧ - Stack.WordRAM.Rep w base capacity (x.regs (slot i) :: vs) (y.comap registers) ∧ - Modifies (Stack.WordRAM.footprint base capacity) x.mem y.mem ∧ + WordStack.Rep w base capacity (x.regs (slot i) :: vs) (y.comap registers) ∧ + Modifies (WordStack.footprint base capacity) x.mem y.mem ∧ ∀ j, Outside j → y.regs j = x.regs j := by let q := - (x.set (registers Stack.WordRAM.tag) 0).set (registers Stack.WordRAM.arg) (x.regs (slot i)) - have hs : slot i ≠ registers Stack.WordRAM.tag := outside_ne _ (Or.inl (by dsimp [slot]; omega)) _ + (x.set (registers WordStack.tag) 0).set (registers WordStack.arg) (x.regs (slot i)) + have hs : slot i ≠ registers WordStack.tag := outside_ne _ (Or.inl (by dsimp [slot]; omega)) _ have hp : Exec (B := .standard) w - (.seq (.op (.const (registers Stack.WordRAM.tag) 0)) - (.op (.copy (registers Stack.WordRAM.arg) (slot i)))) x q 2 := by + (.seq (.op (.const (registers WordStack.tag) 0)) + (.op (.copy (registers WordStack.arg) (slot i)))) x q 2 := by ram_eval [q, hs] - have hq : q.comap registers = - (Stack.WordRAM.implementation w capacity).prepare (.push (x.regs (slot i))) - (x.comap registers) := by + have hq : q.comap registers = WordStack.prepare 0 (x.regs (slot i)) (x.comap registers) := by ext j · fin_cases j <;> - simp +decide [q, Data.comap, Data.set, registers, Stack.WordRAM.implementation, - DynamicProgram.prepare, Stack.WordRAM.tag, Stack.WordRAM.arg] + simp +decide [q, Data.comap, Data.set, registers, WordStack.prepare, WordStack.tag, + WordStack.arg] · rfl - obtain ⟨a, ha, _, hra, hframe⟩ := - Stack.WordRAM.execute h2 hcap hr (.push (x.regs (slot i))) ⟨hv, hw⟩ - have hcall : Exec w Stack.WordRAM.program (q.comap registers) a 7 := by - simpa only [hq, Stack.WordRAM.bodyCost] using ha + obtain ⟨a, ha, hra, hframe⟩ := WordStack.exec_push h2 hcap hr hv hw + have hcall : Exec w WordStack.program (q.comap registers) a 7 := by + simpa only [hq] using ha let y := a.extend registers q refine ⟨y, ?_, ?_, hframe, ?_⟩ · exact hp.seq (hcall.rename registers_injective) · simpa only [y, Data.comap_extend registers_injective] using hra · intro j hj rw [extend_outside a q j hj] - simp only [q, Data.set_regs, Function.update_of_ne (outside_ne j hj Stack.WordRAM.arg), - Function.update_of_ne (outside_ne j hj Stack.WordRAM.tag)] + simp only [q, Data.set_regs, Function.update_of_ne (outside_ne j hj WordStack.arg), + Function.update_of_ne (outside_ne j hj WordStack.tag)] private def pushSlots : List (Fin 7) → Code .standard 128 | [] => .op (.copy (slot 0) (slot 0)) @@ -131,12 +127,12 @@ private def pushSlots : List (Fin 7) → Code .standard 128 private theorem pushSlots_exec {w base capacity : ℕ} (is : List (Fin 7)) (x : Data 128) (vs : List ℕ) (h2 : Fits w 2) (hcap : Fits w (base + capacity)) - (hr : Stack.WordRAM.Rep w base capacity vs (x.comap registers)) + (hr : WordStack.Rep w base capacity vs (x.comap registers)) (hw : ∀ i, Fits w (x.regs (slot i))) (hv : is.length + vs.length ≤ capacity) : ∃ y, Exec w (pushSlots is) x y (9 * is.length + 1) ∧ - Stack.WordRAM.Rep w base capacity (is.reverse.map (fun i => x.regs (slot i)) ++ vs) + WordStack.Rep w base capacity (is.reverse.map (fun i => x.regs (slot i)) ++ vs) (y.comap registers) ∧ - Modifies (Stack.WordRAM.footprint base capacity) x.mem y.mem ∧ + Modifies (WordStack.footprint base capacity) x.mem y.mem ∧ ∀ j, Outside j → y.regs j = x.regs j := by induction is generalizing x vs with | nil => @@ -172,13 +168,13 @@ def pushFrame : Code .standard 128 := There is no host-level record write, and the exact seven-word sequence is at the stack top. -/ theorem pushFrame_exec {w base capacity : ℕ} (x : Data 128) (vs : List ℕ) (h2 : Fits w 2) (hcap : Fits w (base + capacity)) - (hr : Stack.WordRAM.Rep w base capacity vs (x.comap registers)) + (hr : WordStack.Rep w base capacity vs (x.comap registers)) (hw : ∀ i, Fits w (x.regs (slot i))) (hv : 7 + vs.length ≤ capacity) : ∃ y, Exec w pushFrame x y 64 ∧ - Stack.WordRAM.Rep w base capacity + WordStack.Rep w base capacity ([x.regs (slot 0), x.regs (slot 1), x.regs (slot 2), x.regs (slot 3), x.regs (slot 4), x.regs (slot 5), x.regs (slot 6)] ++ vs) (y.comap registers) ∧ - Modifies (Stack.WordRAM.footprint base capacity) x.mem y.mem ∧ + Modifies (WordStack.footprint base capacity) x.mem y.mem ∧ ∀ j, Outside j → y.regs j = x.regs j := by simpa only [pushFrame, pushSlots, List.length_cons, List.length_nil, List.reverse_cons, List.reverse_nil, List.nil_append, List.cons_append, List.append_assoc, List.singleton_append, @@ -195,61 +191,53 @@ private theorem comap_set_slot (x : Data 128) (i : Fin 7) (v : ℕ) : /-- Deliver the pop tag, execute the complete dispatcher, and copy its answer to the record. -/ def popSlot (i : Fin 7) : Code .standard 128 := - let tag := registers Stack.WordRAM.tag - let arg := registers Stack.WordRAM.arg + let tag := registers WordStack.tag + let arg := registers WordStack.arg .seq (ram[ tag ← #1; arg ← #0 ]) - (.seq (Stack.WordRAM.program.rename registers) - (.op (.copy (slot i) (registers Stack.WordRAM.out)))) + (.seq (WordStack.program.rename registers) + (.op (.copy (slot i) (registers WordStack.out)))) /-- A nonempty native pop includes both argument instructions and the actual result copy. Only the selected record register, stack workspace and reserved stack memory may change. -/ theorem popSlot_exec {w base capacity : ℕ} (i : Fin 7) (x : Data 128) (v : ℕ) (vs : List ℕ) (h2 : Fits w 2) (hcap : Fits w (base + capacity)) - (hr : Stack.WordRAM.Rep w base capacity (v :: vs) (x.comap registers)) : + (hr : WordStack.Rep w base capacity (v :: vs) (x.comap registers)) : ∃ y, Exec w (popSlot i) x y 14 ∧ - Stack.WordRAM.Rep w base capacity vs (y.comap registers) ∧ y.regs (slot i) = v ∧ - Modifies (Stack.WordRAM.footprint base capacity) x.mem y.mem ∧ + WordStack.Rep w base capacity vs (y.comap registers) ∧ y.regs (slot i) = v ∧ + Modifies (WordStack.footprint base capacity) x.mem y.mem ∧ ∀ j, Outside j → j ≠ slot i → y.regs j = x.regs j := by - let q := (x.set (registers Stack.WordRAM.tag) 1).set (registers Stack.WordRAM.arg) 0 + let q := (x.set (registers WordStack.tag) 1).set (registers WordStack.arg) 0 have h1 : Fits w 1 := h2.of_le (by decide) have hp : Exec (B := .standard) w - (.seq (.op (.const (registers Stack.WordRAM.tag) 1)) - (.op (.const (registers Stack.WordRAM.arg) 0))) x q 2 := by + (.seq (.op (.const (registers WordStack.tag) 1)) + (.op (.const (registers WordStack.arg) 0))) x q 2 := by ram_eval [q, h1] - have hq : q.comap registers = - (Stack.WordRAM.implementation w capacity).prepare .pop (x.comap registers) := by + have hq : q.comap registers = WordStack.prepare 1 0 (x.comap registers) := by ext j · fin_cases j <;> - simp +decide [q, Data.comap, Data.set, registers, Stack.WordRAM.implementation, - DynamicProgram.prepare, Stack.WordRAM.tag, Stack.WordRAM.arg] + simp +decide [q, Data.comap, Data.set, registers, WordStack.prepare, WordStack.tag, + WordStack.arg] · rfl - obtain ⟨a, ha, hans, hra, hframe⟩ := Stack.WordRAM.execute h2 hcap hr .pop trivial - have hcall : Exec w Stack.WordRAM.program (q.comap registers) a 11 := by - simpa only [hq, Stack.WordRAM.bodyCost, List.cons_ne_nil, ↓reduceIte] using ha - have hout : a.regs Stack.WordRAM.out = v := by - change some (if a.regs Stack.WordRAM.present = 0 then none - else some (a.regs Stack.WordRAM.out)) = - some (some v) at hans - split at hans - · cases hans - · exact Option.some.inj (Option.some.inj hans) - have hread : (a.extend registers q).regs (registers Stack.WordRAM.out) = v := by - change Function.extend registers a.regs q.regs (registers Stack.WordRAM.out) = v + obtain ⟨a, ha, hout, hra, hframe⟩ := WordStack.exec_pop h2 hcap hr + have hcall : Exec w WordStack.program (q.comap registers) a 11 := by + simpa only [hq] using ha + have hread : (a.extend registers q).regs (registers WordStack.out) = v := by + change Function.extend registers a.regs q.regs (registers WordStack.out) = v rw [registers_injective.extend_apply, hout] let y := (a.extend registers q).set (slot i) v have hcopy : Exec (B := .standard) w - (.op (.copy (slot i) (registers Stack.WordRAM.out))) (a.extend registers q) y 1 := by + (.op (.copy (slot i) (registers WordStack.out))) (a.extend registers q) y 1 := by simpa only [y, hread] using (Exec.copy (B := Basis.standard) (w := w) (x := a.extend registers q) - (d := slot i) (s := registers Stack.WordRAM.out)) + (d := slot i) (s := registers WordStack.out)) refine ⟨y, hp.seq ((hcall.rename registers_injective).seq hcopy), ?_, ?_, hframe, ?_⟩ · simpa only [y, comap_set_slot, Data.comap_extend registers_injective, List.tail_cons] using hra · exact Function.update_self .. · intro j hj hji change Function.update (a.extend registers q).regs (slot i) v j = x.regs j rw [Function.update_of_ne hji, extend_outside a q j hj] - simp only [q, Data.set_regs, Function.update_of_ne (outside_ne j hj Stack.WordRAM.arg), - Function.update_of_ne (outside_ne j hj Stack.WordRAM.tag)] + simp only [q, Data.set_regs, Function.update_of_ne (outside_ne j hj WordStack.arg), + Function.update_of_ne (outside_ne j hj WordStack.tag)] private def popSlots : List (Fin 7) → Code .standard 128 | [] => .op (.copy (slot 0) (slot 0)) @@ -275,11 +263,11 @@ private theorem restore_congr_at (is : List (Fin 7)) (vs : List ℕ) private theorem popSlots_exec {w base capacity : ℕ} (is : List (Fin 7)) (ws : List ℕ) (x : Data 128) (vs : List ℕ) (h2 : Fits w 2) (hcap : Fits w (base + capacity)) - (hr : Stack.WordRAM.Rep w base capacity (ws ++ vs) (x.comap registers)) + (hr : WordStack.Rep w base capacity (ws ++ vs) (x.comap registers)) (hlen : ws.length = is.length) : ∃ y, Exec w (popSlots is) x y (14 * is.length + 1) ∧ - Stack.WordRAM.Rep w base capacity vs (y.comap registers) ∧ - Modifies (Stack.WordRAM.footprint base capacity) x.mem y.mem ∧ + WordStack.Rep w base capacity vs (y.comap registers) ∧ + Modifies (WordStack.footprint base capacity) x.mem y.mem ∧ ∀ j, Outside j → y.regs j = restore is ws x.regs j := by induction is generalizing ws x with | nil => @@ -317,11 +305,11 @@ def popFrame : Code .standard 128 := The seven restored words have their original order; other caller registers are preserved. -/ theorem popFrame_exec {w base capacity : ℕ} (x : Data 128) (vs : List ℕ) (a : Fin 7 → ℕ) (h2 : Fits w 2) (hcap : Fits w (base + capacity)) - (hr : Stack.WordRAM.Rep w base capacity + (hr : WordStack.Rep w base capacity ([a 0, a 1, a 2, a 3, a 4, a 5, a 6] ++ vs) (x.comap registers)) : - ∃ y, Exec w popFrame x y 99 ∧ Stack.WordRAM.Rep w base capacity vs (y.comap registers) ∧ + ∃ y, Exec w popFrame x y 99 ∧ WordStack.Rep w base capacity vs (y.comap registers) ∧ (∀ i, y.regs (slot i) = a i) ∧ - Modifies (Stack.WordRAM.footprint base capacity) x.mem y.mem ∧ + Modifies (WordStack.footprint base capacity) x.mem y.mem ∧ ∀ j, Outside j → (j.val < 64 ∨ 71 ≤ j.val) → y.regs j = x.regs j := by obtain ⟨y, hy, hry, hfy, hpy⟩ := popSlots_exec [0, 1, 2, 3, 4, 5, 6] [a 0, a 1, a 2, a 3, a 4, a 5, a 6] diff --git a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalWordStack.lean b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalWordStack.lean new file mode 100644 index 0000000000..6b6736ffd0 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalWordStack.lean @@ -0,0 +1,159 @@ +/- +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.Allocation +public import Complexity.Computability.WordRAM.Heap + +/-! +# The word stack inside Chan–He traversal + +Traversal keeps its pending records on a bounded stack of words, run by a fixed nine-register +program that dispatches on an operation tag: push = 0, pop = 1, size = 2. The stack occupies a +fixed consecutive memory block, with its top at the highest live address; a pop leaves the old +cell in place. This file proves the two calls traversal makes, a push with spare capacity and a +pop from a nonempty stack, instruction by instruction: traversal's time bound adds up their exact +instruction counts. + +The library's bounded stack is `Stack.Memory`, which `Stack.Native` runs on the word RAM. This +dispatcher is part of the hand-stepped traversal and goes with it. + +## Main definitions + +* `tag`, `arg`, `len`, `ptr`, `addr`, `out`, `zero`, `one`, `present`: the nine registers. +* `program`: the fixed dispatcher. +* `prepare`: argument delivery of an operation tag and a value. +* `Rep`, `footprint`: the top-first list representation and the reserved memory block. + +## Main results + +* `exec_push`: a push in seven instructions. +* `exec_pop`: a nonempty pop in eleven instructions. + +## 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.Algorithms.WordRAM.ThreeSum.ChanHe.Traversal.WordStack + +open Cslib.WordRAM + +/-- Encoded operation tag: push = 0, pop = 1, size = 2. -/ +abbrev tag : Fin 9 := 0 +/-- Caller-supplied push value. -/ +abbrev arg : Fin 9 := 1 +/-- Persistent number of live entries. -/ +abbrev len : Fin 9 := 2 +/-- Persistent base address of the reserved region. -/ +abbrev ptr : Fin 9 := 3 +/-- Temporary cell address. -/ +abbrev addr : Fin 9 := 4 +/-- Returned value. -/ +abbrev out : Fin 9 := 5 +/-- Constant zero. -/ +abbrev zero : Fin 9 := 6 +/-- Constant one. -/ +abbrev one : Fin 9 := 7 +/-- Presence bit distinguishing `none` from a returned zero word. -/ +abbrev present : Fin 9 := 8 + +/-- Fixed operation dispatch. No code is generated from an operation or its payload. -/ +def program : Code .standard 9 := ram[ + zero ← #0; one ← #1; + if tag = zero then [ + ⟪writeAt addr ptr len arg⟫; len ← len + one; out ← zero + ] else [ + if tag = one then [ + if len = zero then [ out ← zero; present ← zero ] else [ + len ← len - one; ⟪readAt addr out ptr len⟫; present ← one + ] + ] else [ out ← len ] + ] ] + +/-- Deliver an operation tag and a push value. -/ +def prepare (t v : ℕ) (x : Data 9) : Data 9 := (x.set tag t).set arg v + +/-- Persistent register, bounded-word, and live array invariants. Cells above the live prefix +may contain old popped values; they are part of the reserved footprint. -/ +structure Rep (w base capacity : ℕ) (vs : List ℕ) (x : Data 9) : Prop where + bounded : x.Bounded w + pointer : x.regs ptr = base + length_eq : x.regs len = vs.length + capacity_le : vs.length ≤ capacity + contents : ∀ i : Fin vs.length, x.mem (base + i.val) = vs.reverse[i.val] + +/-- Reserved memory footprint; it stays fixed through pushes and pops. -/ +def footprint (base capacity : ℕ) : Finset ℕ := + (Block.mk base capacity).footprint + +/-- A push with spare capacity writes the value above the top in seven instructions. -/ +theorem exec_push {w base capacity : ℕ} (h2 : Fits w 2) (hcap : Fits w (base + capacity)) + {vs : List ℕ} {x : Data 9} {v : ℕ} (hr : Rep w base capacity vs x) + (hv : vs.length < capacity) (hw : Fits w v) : + ∃ y, Exec w program (prepare 0 v x) y 7 ∧ + Rep w base capacity (v :: vs) y ∧ Modifies (footprint base capacity) x.mem y.mem := by + have h1 : Fits w 1 := h2.of_le (by decide) + have haddr : Fits w (base + vs.length) := hcap.of_le (by omega) + have hinc : Fits w (vs.length + 1) := hcap.of_le (by omega) + let y := (((((prepare 0 v x).set zero 0).set one 1).set + addr (base + vs.length)).write (base + vs.length) v |>.set len (vs.length + 1)).set out 0 + have he : Exec w program (prepare 0 v x) y 7 := by + ram_eval [program, prepare, writeAt, y, hr.pointer, hr.length_eq, h1, haddr, hinc] + refine ⟨y, he, ⟨?_, ?_, ?_, ?_, ?_⟩, ?_⟩ + · exact he.bounded ((hr.bounded.set (fits_zero w)).set hw) + · simp +decide [y, Data.set, Data.write, prepare, hr.pointer] + · simp +decide [y, Data.set, Data.write, prepare] + · simpa using hv + · intro i + by_cases hi : i.val < vs.length + · have hne : base + i.val ≠ base + vs.length := by omega + simpa [y, Data.set, Data.write, prepare, List.reverse_cons, List.getElem_append, hi, + Function.update_of_ne hne] using hr.contents ⟨i.val, hi⟩ + · have hi' : i.val = vs.length := by + have := i.isLt + simp only [List.length_cons] at this + omega + simp [y, Data.set, Data.write, prepare, List.reverse_cons, hi'] + · exact Modifies.write_of_owns ⟨Nat.le_add_right _ _, Nat.add_lt_add_left hv _⟩ v + +/-- A pop from a nonempty stack returns its top in eleven instructions and writes no memory. -/ +theorem exec_pop {w base capacity : ℕ} (h2 : Fits w 2) (hcap : Fits w (base + capacity)) + {vs : List ℕ} {x : Data 9} {v : ℕ} (hr : Rep w base capacity (v :: vs) x) : + ∃ y, Exec w program (prepare 1 0 x) y 11 ∧ y.regs out = v ∧ + Rep w base capacity vs y ∧ Modifies (footprint base capacity) x.mem y.mem := by + have h1 : Fits w 1 := h2.of_le (by decide) + have hl : x.regs len = vs.length + 1 := hr.length_eq + have hsub : Fits w vs.length := hcap.of_le (by have := hr.capacity_le; simp_all; omega) + have ha : Fits w (base + vs.length) := hcap.of_le (by have := hr.capacity_le; simp_all; omega) + have hread : x.mem (base + vs.length) = v := by + have h := hr.contents ⟨vs.length, by simp⟩ + simpa [List.reverse_cons] using h + let y := (((((prepare 1 0 x).set zero 0).set one 1).set + len vs.length).set addr (base + vs.length)).set out v |>.set present 1 + have he : Exec w program (prepare 1 0 x) y 11 := by + ram_eval [program, prepare, readAt, y, hr.pointer, hl, h1, ha, hsub, Nat.add_sub_cancel, + Nat.add_eq_zero_iff, and_false, hread] + refine ⟨y, he, by simp +decide [y, Data.set], ⟨?_, ?_, ?_, ?_, ?_⟩, Modifies.refl _ _⟩ + · exact he.bounded ((hr.bounded.set (h2.of_le (by decide))).set (fits_zero w)) + · simp +decide [y, Data.set, prepare, hr.pointer] + · simp +decide [y, Data.set] + · have := hr.capacity_le + simp only [List.length_cons] at this + omega + · intro i + have hi : i.val < (v :: vs).length := by + simp only [List.length_cons] + omega + have h := hr.contents ⟨i.val, hi⟩ + simpa [y, Data.set, prepare, List.reverse_cons, List.getElem_append, i.isLt] using h + +end Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Traversal.WordStack diff --git a/Complexity/DataStructure/Queue/Memory.lean b/Complexity/DataStructure/Queue/Memory.lean index d3fa6805cf..f7361e7ced 100644 --- a/Complexity/DataStructure/Queue/Memory.lean +++ b/Complexity/DataStructure/Queue/Memory.lean @@ -69,7 +69,7 @@ open Cslib.WordRAM Cslib.DataStructure Cslib.DataStructure.Memory Cslib.DataStru /-- 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) + product (Stack.spec w C) (Stack.spec w C) /-- A FIFO list within capacity `C` whose entries fit a `w`-bit word. -/ structure Inv (w C : ℕ) (q : List ℕ) : Prop where diff --git a/Complexity/DataStructure/Stack/Controller.lean b/Complexity/DataStructure/Stack/Controller.lean new file mode 100644 index 0000000000..c1c7fbc70e --- /dev/null +++ b/Complexity/DataStructure/Stack/Controller.lean @@ -0,0 +1,245 @@ +/- +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.Client.MemoryStack +public import Complexity.Computability.WordRAM.Realization.Program +import Complexity.Tactic.Realize +import Complexity.Tactic.WordRAM + +/-! +# An adaptive stack client compiled to one word-RAM program + +The client pushes its input and pops it; if the answer is zero, it pushes one and pops again. It +computes `max n 1`. Over the memory oracle it is `program`: allocate a stack of capacity one, then +run the client's calls through `Stack.Memory`'s methods (`run_eq_link`). The refinement of +`Stack.Memory` transfers the abstract client's answer, admissibility and cost to `program` +(`program_spec`). `realize` certifies `program` as one uniform native emitter +(`realizes_program`), and the argument-installation prefix with a halt makes it a flat program. +No instruction is stepped by hand. + +## Main definitions + +* `client`: the adaptive client over the bounded stack of capacity one. +* `run`, `program`: its calls on a stack's layout, and the allocating memory program. +* `positive`: the word-valued function `max n 1`. + +## Main results + +* `run_eq_link`: `run` is the client linked to the stack's methods. +* `program_spec`: the answer, the admissible memory history and the memory cost. +* `realizes_program`: the native certificate. +* `positive_polytime`: one flat program computes `positive` in constant, hence polynomial, time. + +## References + +The stack is `Stack.Memory`. This example is a composition test, not a new stack algorithm. +-/ + +namespace Cslib.DataStructure.Stack.Controller + +open Cslib.WordRAM Cslib.WordRAM.Realization Cslib.WordRAM.Realization.MemoryStack + Cslib.DataStructure.Queue + +/-- Push the input and pop it; replace a zero answer by another push and pop of one. -/ +public def client (w n : ℕ) : (Stack.spec w 1).Client ℕ := + .liftBind (.push n) fun _ => .liftBind .pop fun + | none => .pure 0 + | some v => if v = 0 then + .liftBind (.push 1) fun _ => .liftBind .pop fun + | none => .pure 0 + | some u => .pure u + else .pure v + +/-- The client's calls on the stack at `L`. -/ +public def run (L : Stack.Memory.Layout) (n : ℕ) : Memory.Prog ℕ := do + Stack.Memory.push L n + match ← Stack.Memory.pop L with + | none => pure 0 + | some v => + if v = 0 then + (do + Stack.Memory.push L 1 + match ← Stack.Memory.pop L with + | none => pure 0 + | some u => pure u) + else pure v + +/-- Allocate a stack of capacity one and run the client on it. -/ +public def program (n : ℕ) : Memory.Prog ℕ := do + let L ← Stack.Memory.allocLayout 1 + run L n + +/-- `run` is the client linked to the stack's methods. -/ +public theorem run_eq_link (L : Stack.Memory.Layout) (w n : ℕ) : + run L n = link (Stack.Memory.methods L w 1) (client w n) := by + refine congrArg (Stack.Memory.push L n >>= ·) (funext fun _ => ?_) + refine congrArg (Stack.Memory.pop L >>= ·) (funext fun a => ?_) + rcases a with _ | v + · rfl + · by_cases hv : v = 0 + · simp only [hv, ↓reduceIte] + refine congrArg (Stack.Memory.push L 1 >>= ·) (funext fun _ => ?_) + refine congrArg (Stack.Memory.pop L >>= ·) (funext fun b => ?_) + cases b <;> rfl + · simp only [hv, ↓reduceIte] + rfl + +/-- The word-valued function computed by the client. -/ +public def positive (n : ℕ) : ℕ := max n 1 + +local instance : WordEncoding Stack.Memory.Layout := ⟨4, layoutWords⟩ + +private theorem realizes_alloc : Realizes .exact (fun _ : ℕ => Stack.Memory.allocLayout 1) + (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding layoutWords + (fun w _ s => Fits w (s.frontier + 2)) (fun _ _ => 0) := by + realize + exact h.of_le (by omega) + +private theorem realizes_run : Realizes .exact (fun d : ℕ × Stack.Memory.Layout => run d.2 d.1) + (((Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding.prodMap layoutWords).trans + (Fin.appendEquiv 1 4).toEmbedding) (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + (fun w d s => d.2.Valid w 1 ∧ Stack.Memory.Inv d.2 s ∧ s.mem d.2.count.base = 0) + (fun _ _ => 0) := by + realize + all_goals + obtain ⟨hL, hI, h0⟩ := h + have h1 : Fits w 1 := hL.capacity_fits + have hne : d.2.items.base ≠ d.2.count.base := fun he => + hL.count_not_owns (he ▸ Block.owns_add (i := 0) (by rw [hL.items_length]; omega)) + have hlt : Fits w d.2.items.base := hmem.fits_of_lt_frontier (by + simpa using hI.item_lt (i := 0) (by rw [hL.items_length]; omega)) + simp only [StateTransitionSpec.clientState, StateTransitionSpec.clientResult, + Stack.Memory.runClient_push, h0, Nat.add_zero, Nat.zero_add, Function.update_self, + Function.update_of_ne hne, ne_eq, Nat.one_ne_zero, + not_false_eq_true, Stack.Memory.runClient_pop_of_pos, Nat.sub_self, optionWords_some, + Matrix.cons_val_zero, Matrix.cons_val_one] at * + all_goals first | exact hlt | exact h1 | exact le_rfl + +/-- **The native certificate.** One uniform emitter, chosen before every width, input and +memory, runs `program` from any memory whose frontier leaves room for the stack. -/ +public theorem realizes_program : Realizes .exact program + (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + (fun w _ s => Fits w (s.frontier + 2)) (fun _ _ => 1) := by + refine (Realizes.bind realizes_alloc realizes_run).consequence ?_ (fun _ _ => le_rfl) + intro w n s _ _ hf + refine ⟨hf, Stack.Memory.valid_allocLayout hf, (Stack.Memory.inv_allocLayout s).1, ?_⟩ + exact clear_of_owns ⟨le_rfl, Nat.lt_add_one _⟩ + +/-- The abstract client is admissible when its two values fit. -/ +private theorem validHistory_client {w n : ℕ} (h1 : Fits w 1) (hn : Fits w n) : + (Stack.spec w 1).ValidHistory [] ((Stack.spec w 1).clientHistory [] (client w n)) := by + by_cases hn0 : n = 0 <;> + simp [StateTransitionSpec.clientHistory, client, -PFunctor.FreeM.pure_eq_pure, hn0, hn, h1] + +/-- The abstract client computes `positive`. -/ +private theorem clientResult_client (w n : ℕ) : + (Stack.spec w 1).clientResult [] (client w n) = positive n := by + by_cases hn0 : n = 0 + · simp [StateTransitionSpec.clientResult, client, positive, -PFunctor.FreeM.pure_eq_pure, hn0] + · simp [StateTransitionSpec.clientResult, client, positive, -PFunctor.FreeM.pure_eq_pure, hn0, + max_eq_left (show 1 ≤ n by omega)] + +/-- The abstract client issues at most four stack calls. -/ +private theorem length_clientHistory_client (w n : ℕ) : + ((Stack.spec w 1).clientHistory [] (client w n)).length ≤ 4 := by + by_cases hn0 : n = 0 <;> + simp [StateTransitionSpec.clientHistory, client, -PFunctor.FreeM.pure_eq_pure, hn0] + +/-- Memory charges along a history do not depend on the word width. -/ +private theorem totalCost_width (w : ℕ) (s : Memory.State) (os : List Memory.Op) : + (Memory.spec 0).totalCost Memory.charge s os = + (Memory.spec w).totalCost Memory.charge s os := by + induction os generalizing s with + | nil => rfl + | cons o os ih => exact congrArg (Memory.charge s o + ·) (ih _) + +/-- **Correctness through the refinement.** From a memory whose frontier leaves room, `program` +returns `positive n`, its memory history is admissible, and it costs at most fifteen memory +units: two for the allocation and three for each of at most four stack calls. -/ +public theorem program_spec {w n : ℕ} {s : Memory.State} (hf : Fits w (s.frontier + 2)) + (hn : Fits w n) : + (Memory.spec w).clientResult s (program n) = positive n ∧ + (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s (program n)) ∧ + memoryCost (program n) s ≤ 15 := by + let L : Stack.Memory.Layout := ⟨⟨s.frontier, 1⟩, ⟨s.frontier + 1, 1⟩⟩ + have hL : L.Valid w 1 := Stack.Memory.valid_allocLayout hf + obtain ⟨hinv, habs⟩ := Stack.Memory.inv_allocLayout (C := 1) s + have hv := validHistory_client hL.capacity_fits hn + rw [← habs] at hv + have hlink : (Stack.Memory.refinement hL).methods = Stack.Memory.methods L w 1 := rfl + have habs' : (Stack.Memory.refinement hL).abs = Stack.Memory.abs L := rfl + obtain ⟨hvE, hres, -, -⟩ := (Stack.Memory.refinement hL).simulates_client (client w n) hinv hv + have hcost := (Stack.Memory.amortized hL).client (client w n) hinv hv + rw [hlink, ← run_eq_link] at hvE hres hcost + rw [habs', habs] at hres hcost + have hrun := Stack.Memory.runClient_allocLayout_bind (w := w) 1 (fun L => run L n) s + have hrun0 := Stack.Memory.runClient_allocLayout_bind (w := 0) 1 (fun L => run L n) s + refine ⟨?_, ?_, ?_⟩ + · simp only [StateTransitionSpec.clientResult, program] at hres ⊢ + rw [hrun, hres] + exact clientResult_client w n + · simp only [StateTransitionSpec.clientHistory, program] at hvE ⊢ + rw [hrun] + exact ⟨hf.of_le (by omega), hf, hvE⟩ + · rw [StateTransitionSpec.totalCost_const] at hcost + have hlen := length_clientHistory_client w n + unfold memoryCost + simp only [StateTransitionSpec.clientHistory, program] + rw [hrun0, ← runClient_width w, totalCost_width w] + change 2 + (1 + (Memory.spec w).totalCost Memory.charge ⟨clear s.mem s.frontier 1, + s.frontier + 1 + 1⟩ ((Memory.spec w).clientHistory ⟨clear s.mem s.frontier 1, + s.frontier + 1 + 1⟩ (run L n))) ≤ 15 + omega + +/-- The client, compiled to one flat word-RAM program, computes `positive` in constant time at +every width of at least three bits per input bit. -/ +@[fun_prop, program_certificate] +public theorem positive_polytime : + UniformComputableInPolyTime .standard positive (fun _ n => [n]) id := by + obtain ⟨m, C, emit, he⟩ := realizes_program + let fr : Fin (m + 4) := ⟨0, by omega⟩ + let src : Fin 1 → Fin (m + 4) := fun _ => ⟨1, by omega⟩ + let tmp : Fin (1 + m + 1) → Fin (m + 4) := fun i => ⟨i.val + 2, by omega⟩ + have htmp : Function.Injective tmp := fun i j h => + Fin.ext (by have := congrArg Fin.val h; simp only [tmp] at this; omega) + have hd : ∀ i j, src i ≠ tmp j := fun i j h => by + have := congrArg Fin.val h; simp only [src, tmp] at this; omega + have hf : ∀ j, tmp j ≠ fr := fun j h => by + have := congrArg Fin.val h; simp only [tmp, fr] at this; omega + have hs : ∀ i, src i ≠ fr := fun i h => by + have := congrArg Fin.val h; simp only [src, fr] at this; omega + apply UniformComputableInPolyTime.of_code + ((Code.seq (inputPrefix fr (tmp (Fin.last (1 + m))) src) (emit fr src tmp)).mapSimulates + .exactStandard) (tmp (Fin.castAdd (m + 1) 0)) (.const (7 + C * 17)) 3 (by decide) + intro n w hw hin + have h3 : 3 ≤ w := by unfold logWidth at hw; simp only [id] at hw; omega + have h4 : Fits w 4 := (show Fits 3 4 by decide).mono h3 + have hn : Fits w n := hin.2 n (by simp) + obtain ⟨x, ex, hx, hm, hwords, -⟩ := inputPrefix_exec fr (tmp (Fin.last (1 + m))) src [n] + (fun _ _ _ => Subsingleton.elim _ _) (hf _) (fun i => hd i _) hs (by simp) + (h4.of_le (by simp)) hin + have harg : x.regs ∘ src = (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding n := by + funext i + simpa using hwords i + have hroom : Fits w ((inputState [n]).frontier + 2) := h4.of_le (by simp [inputState]) + obtain ⟨hres, hvalid, hcost⟩ := program_spec hroom hn + obtain ⟨y, ey, -, hout, -⟩ := (he fr src tmp htmp hd hf hs).2 w n x hx harg + (by rw [hm]; exact hroom) (by rw [hm]; exact hvalid) + refine ⟨y, ((ex.seq ey).mapSimulates _).mono ?_, ?_⟩ + · rw [hm] + beta_reduce + have := Nat.mul_le_mul_left C (show memoryCost (program n) (inputState [n]) + 1 + 1 ≤ 17 by + omega) + omega + · rw [hout 0, hm, hres] + rfl + +end Cslib.DataStructure.Stack.Controller diff --git a/Complexity/DataStructure/Stack/Memory.lean b/Complexity/DataStructure/Stack/Memory.lean index cf79d42258..95884d1483 100644 --- a/Complexity/DataStructure/Stack/Memory.lean +++ b/Complexity/DataStructure/Stack/Memory.lean @@ -9,12 +9,12 @@ module public import Complexity.DataStructure.Memory.Field public import Complexity.DataStructure.StateTransition.Refinement -public import Complexity.DataStructure.Stack.WordRAM.Basic +public import Complexity.DataStructure.Stack.Spec /-! # An array stack over the memory oracle -The bounded stack `Stack.WordRAM.spec w C` is implemented by three short programs over +The bounded stack `Stack.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 at most three memory operations and @@ -26,9 +26,9 @@ write only the layout's cells and never allocate (`modifies_methods`), and the a 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 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. +No register or instruction count appears here. Word fit enters only through the admissibility +of `Memory.spec w` and one static fact of the layout, that the capacity fits. `Stack.Native` runs +these programs on the word RAM. ## Main definitions @@ -121,7 +121,7 @@ def pop : Prog (Option ℕ) := do 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) +def methods (w C : ℕ) : Methods (Stack.spec w C) (Memory.spec w) | .push v => push L v | .pop => pop L | .size => size L @@ -216,7 +216,7 @@ theorem Inv.item_lt {s : Memory.State} (hs : Inv L s) {i : ℕ} (hi : i < L.item 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) := +def refinement (hL : L.Valid w C) : Refinement (Stack.spec w C) (Memory.spec w) := Refinement.ofFn (methods L w C) (Inv L) (abs L) (by intro s o hs hv @@ -296,7 +296,7 @@ theorem amortized (hL : L.Valid w C) : /-- 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) : + (hv : (Stack.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 @@ -322,7 +322,7 @@ theorem modifies_methods (hL : L.Valid w C) {s : Memory.State} (o : StackOp ℕ) /-- 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) : + (o : StackOp ℕ) (hv : (Stack.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 @@ -335,14 +335,14 @@ theorem frame {L' : Layout} (hL : L.Valid w C) (hL' : L'.Valid w C) /-- 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) + Methods (product (Stack.spec w C) (Stack.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) : - Refinement (product (Stack.WordRAM.spec w C) (Stack.WordRAM.spec w C)) (Memory.spec w) := + Refinement (product (Stack.spec w C) (Stack.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 diff --git a/Complexity/DataStructure/Stack/Native.lean b/Complexity/DataStructure/Stack/Native.lean new file mode 100644 index 0000000000..589301d872 --- /dev/null +++ b/Complexity/DataStructure/Stack/Native.lean @@ -0,0 +1,392 @@ +/- +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.Computability.WordRAM.Realization.Client.MemoryStack +public import Complexity.Computability.WordRAM.Realization.Witness +public import Complexity.DataStructure.StateTransition.Resource + +/-! +# The array stack on the word RAM + +`Stack.Memory` implements the bounded stack `Stack.spec w C` by three memory programs. Here each +program runs as native code. The uniform realizations `MemoryStack.realizes_push`, +`realizes_pop` and `realizes_size` supply one static body per operation, chosen before every +width, capacity, layout and value; no instruction is stepped by hand. The operation's +constructor selects the body, and a push value is delivered to a register beforehand, uncharged, +as in a calling convention. The client between operations stays outside the machine; a whole +client compiles to one flat program when `realize` certifies it (`Stack.Controller`). + +The native data represents a stack when its registers hold the layout words and the frontier, +and its memory satisfies the invariant of `Stack.Memory` and abstracts to the stack. Correctness +is that of `Stack.Memory.refinement`, transported through the realizations' whole-memory +postcondition. Every operation runs within one constant number of instructions +(`instruction_bound`), so an adaptive client with `m` operations runs within `m` times that +constant (`run_client`). Each operation writes only the layout's cells (`modifies`, `frame`). + +## Main definitions + +* `registers`, `frontier`, `layoutReg`, `arg`: the register file. +* `body`, `prepare`, `result`, `exec`: the native body of each operation, argument delivery, + the decoded answer, and the execution relation. +* `Rep`: native data representing a stack. +* `implementation`: the native stack. +* `initialLayout`, `initial`: an empty stack at a fixed address, as a calling-convention state. + +## Main results + +* `instruction_bound`: a constant bound on the instructions of every operation. +* `run_client`: adaptive clients, with a bound linear in the number of operations. +* `modifies`, `frame`: operations write only the layout's cells. +* `initial_rep`: the initial state represents the empty stack. + +## References + +* [T. H. Cormen, C. E. Leiserson, R. L. Rivest and C. Stein, *Introduction to Algorithms*, + Section 10.1][CLRS2009] +-/ + +namespace Cslib.DataStructure.Stack.Native + +open Cslib.WordRAM Cslib.WordRAM.Realization Cslib.WordRAM.Realization.MemoryStack + Cslib.DataStructure.Queue + +private noncomputable def slots : ℕ := + (1 + realizes_size.scratchSize + 1) + (0 + realizes_push.scratchSize + 1) + + (2 + realizes_pop.scratchSize + 1) + +/-- One register count, selected before every width, layout, operation and value. -/ +public noncomputable def registers : ℕ := slots + 6 + +/-- Register zero holds the allocation frontier. -/ +public noncomputable def frontier : Fin registers := ⟨0, by unfold registers; omega⟩ + +/-- Registers one to four hold the layout words. -/ +public noncomputable def layoutReg (i : Fin 4) : Fin registers := + ⟨i.val + 1, by unfold registers; omega⟩ + +/-- Register five receives a push value. -/ +public noncomputable def arg : Fin registers := ⟨5, by unfold registers; omega⟩ + +private noncomputable def pushSrc (i : Fin 5) : Fin registers := + ⟨i.val + 1, by unfold registers; omega⟩ + +private noncomputable def tmp {n : ℕ} (hn : n ≤ slots) (i : Fin n) : Fin registers := + ⟨i.val + 6, by have := i.isLt; unfold registers; omega⟩ + +private theorem sizeSlots : 1 + realizes_size.scratchSize + 1 ≤ slots := by unfold slots; omega +private theorem pushSlots : 0 + realizes_push.scratchSize + 1 ≤ slots := by unfold slots; omega +private theorem popSlots : 2 + realizes_pop.scratchSize + 1 ≤ slots := by unfold slots; omega + +private theorem layout {k n : ℕ} (src : Fin k → Fin registers) + (hsrc : ∀ i, 1 ≤ (src i).val ∧ (src i).val < 6) (hn : n ≤ slots) : + Function.Injective (tmp hn) ∧ (∀ i j, src i ≠ tmp hn j) ∧ + (∀ j, tmp hn j ≠ frontier) ∧ (∀ i, src i ≠ frontier) := by + refine ⟨fun i j h => Fin.ext (by have := congrArg Fin.val h; simp only [tmp] at this; omega), + fun i j h => ?_, fun j h => ?_, fun i h => ?_⟩ + · have := congrArg Fin.val h + have := hsrc i + simp only [tmp] at * + omega + · have := congrArg Fin.val h + simp only [tmp, frontier] at this + omega + · have := congrArg Fin.val h + have := hsrc i + simp only [frontier] at * + omega + +/-- The static body of each operation; arguments and runtime layouts never select code. -/ +public noncomputable def body : StackOp ℕ → Code .exact registers + | .push _ => realizes_push.code frontier pushSrc (tmp pushSlots) + | .pop => realizes_pop.code frontier layoutReg (tmp popSlots) + | .size => realizes_size.code frontier layoutReg (tmp sizeSlots) + +/-- Deliver a push value to `arg`; the other operations take no argument. -/ +public noncomputable def prepare : StackOp ℕ → Data registers → Data registers + | .push v, x => x.set arg v + | .pop, x => x + | .size, x => x + +/-- The answer, read from the result registers: a presence tag and a payload for a pop, a word +for a size. Decoding executes no instruction. -/ +public noncomputable def result : (o : StackOp ℕ) → Data registers → (stack ℕ).Result o + | .push _, _ => () + | .pop, y => + if y.regs (tmp popSlots (Fin.castAdd (realizes_pop.scratchSize + 1) (0 : Fin 2))) = 0 then none + else some (y.regs (tmp popSlots (Fin.castAdd (realizes_pop.scratchSize + 1) (1 : Fin 2)))) + | .size, y => y.regs (tmp sizeSlots (Fin.castAdd (realizes_size.scratchSize + 1) (0 : Fin 1))) + +/-- An operation executes its body from the prepared data, in the counted number of +instructions, and returns the decoded answer. -/ +public noncomputable def exec (w C : ℕ) : Execution (Stack.spec w C) (Data registers) ℕ := + fun x o a y n => Exec w (body o) (prepare o x) y n ∧ a = result o y + +/-- Native data represents the stack `vs` on layout `L`: the layout is well formed, its words +sit in `layoutReg`, all words are bounded, and the memory with the frontier register satisfies +the invariant of `Stack.Memory` and abstracts to `vs`. -/ +public structure Rep (w C : ℕ) (L : Stack.Memory.Layout) (vs : List ℕ) (x : Data registers) : + Prop where + /-- The layout is well formed for capacity `C`. -/ + valid : L.Valid w C + /-- The layout words sit in their registers. -/ + layout : ∀ i, x.regs (layoutReg i) = layoutWords L i + /-- Every register and cell holds a word. -/ + bounded : x.Bounded w + /-- The memory satisfies the invariant. -/ + inv : Stack.Memory.Inv L (memoryState frontier x) + /-- The memory holds `vs`. -/ + abs_eq : Stack.Memory.abs L (memoryState frontier x) = vs + +private theorem step_methods {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C) + {s : Memory.State} (hs : Stack.Memory.Inv L s) {o : StackOp ℕ} + (hv : (Stack.spec w C).valid (Stack.Memory.abs L s) o) : + (Memory.spec w).ValidHistory s + ((Memory.spec w).clientHistory s (Stack.Memory.methods L w C o)) ∧ + (Stack.spec w C).stepFn (Stack.Memory.abs L s) o = + (Stack.Memory.abs L ((Memory.spec w).clientState s (Stack.Memory.methods L w C o)), + (Memory.spec w).clientResult s (Stack.Memory.methods L w C o)) ∧ + Stack.Memory.Inv L ((Memory.spec w).clientState s (Stack.Memory.methods L w C o)) := by + have hvalid := ((Memory.spec w).demonicValid_iff_validHistory_runClient _ _).mp + ((Stack.Memory.refinement hL).valid o hs hv) + obtain ⟨hstep, hinv⟩ := (Stack.Memory.refinement hL).correct o hs hv + ((Memory.spec w).clientRun_canonical s _ hvalid) + exact ⟨hvalid, (((Stack.spec w C).step_iff_stepFn _ _ _ _).mp hstep).symm, hinv⟩ + +private theorem memoryCost_methods_le (L : Stack.Memory.Layout) (w C : ℕ) (o : StackOp ℕ) + (s : Memory.State) : memoryCost (Stack.Memory.methods L w C o) s ≤ 3 := by + cases o with + | push v => exact le_of_eq rfl + | pop => + by_cases h : s.mem L.count.base = 0 + · simp [memoryCost, StateTransitionSpec.clientHistory, Stack.Memory.methods, + Stack.Memory.runClient_pop_of_eq_zero, h, StateTransitionSpec.totalCost, Memory.charge] + · simp [memoryCost, StateTransitionSpec.clientHistory, Stack.Memory.methods, + Stack.Memory.runClient_pop_of_pos, h, StateTransitionSpec.totalCost, Memory.charge] + | size => exact Nat.le_succ_of_le (Nat.le_succ_of_le le_rfl) + +private noncomputable def factor : ℕ := + max (max realizes_size.factor realizes_push.factor) realizes_pop.factor + +private theorem optionWords_decode (a : Option ℕ) : + (if optionWords a 0 = 0 then none else some (optionWords a 1)) = a := by + cases a <;> simp + +private theorem layoutReg_ne_tmp {n : ℕ} (hn : n ≤ slots) (i : Fin 4) (j : Fin n) : + tmp hn j ≠ layoutReg i := by + intro h + have := congrArg Fin.val h + simp only [tmp, layoutReg] at this + omega + +private theorem layoutReg_ne_frontier (i : Fin 4) : layoutReg i ≠ frontier := by + intro h + have := congrArg Fin.val h + simp only [layoutReg, frontier] at this + omega + +private theorem layoutReg_ne_arg (i : Fin 4) : layoutReg i ≠ arg := by + intro h + have := congrArg Fin.val h + simp only [layoutReg, arg] at this + have := i.isLt + omega + +private theorem prepare_memoryState (o : StackOp ℕ) (x : Data registers) : + memoryState frontier (prepare o x) = memoryState frontier x := by + cases o with + | push v => + have h : arg ≠ frontier := by + intro h + have := congrArg Fin.val h + simp only [arg, frontier] at this + omega + exact Memory.State.ext rfl (Function.update_of_ne h.symm v x.regs) + | pop | size => rfl + +private theorem prepare_layout (o : StackOp ℕ) (x : Data registers) (i : Fin 4) : + (prepare o x).regs (layoutReg i) = x.regs (layoutReg i) := by + cases o with + | push v => exact Function.update_of_ne (layoutReg_ne_arg i) _ _ + | pop | size => rfl + +/-- One operation's body, from represented data: its instruction count, memory, answer and +preserved layout registers. -/ +private theorem exists_body {w C : ℕ} {L : Stack.Memory.Layout} {vs : List ℕ} + {x : Data registers} (hr : Rep w C L vs x) (o : StackOp ℕ) + (hv : (Stack.spec w C).valid vs o) : + ∃ y n, Exec w (body o) (prepare o x) y n ∧ n ≤ factor * 23 ∧ + memoryState frontier y = (Memory.spec w).clientState (memoryState frontier x) + (Stack.Memory.methods L w C o) ∧ + result o y = (Memory.spec w).clientResult (memoryState frontier x) + (Stack.Memory.methods L w C o) ∧ + y.Bounded w ∧ ∀ i, y.regs (layoutReg i) = x.regs (layoutReg i) := by + have hL := hr.valid + have hC := hL.items_length + rw [← hr.abs_eq] at hv + have hvalid := (step_methods hL hr.inv hv).1 + have hcost := memoryCost_methods_le L w C o (memoryState frontier x) + have hregs : x.regs ∘ layoutReg = layoutWords L := funext hr.layout + cases o with + | push v => + obtain ⟨hlen, hfit⟩ := hv + rw [Stack.Memory.length_abs] at hlen + have hb : (prepare (.push v) x).Bounded w := hr.bounded.set hfit + have hp := prepare_memoryState (.push v) x + obtain ⟨hi, hd, hf, hs⟩ := layout pushSrc (fun i => by simp only [pushSrc]; omega) pushSlots + have hin : (prepare (.push v) x).regs ∘ pushSrc = pushWords (L, v) := by + funext i + fin_cases i + · exact (prepare_layout _ x 0).trans (hr.layout 0) + · exact (prepare_layout _ x 1).trans (hr.layout 1) + · exact (prepare_layout _ x 2).trans (hr.layout 2) + · exact (prepare_layout _ x 3).trans (hr.layout 3) + · exact Function.update_self arg v x.regs + obtain ⟨y, ⟨n, hn, he⟩, hm, _, hframe⟩ := + (realizes_push.code_spec frontier _ _ hi hd hf hs).2 w (L, v) _ hb hin + ⟨hC ▸ hL, hp ▸ hr.inv, by + change (memoryState frontier (prepare (.push v) x)).mem L.count.base < L.items.length + rw [hp]; omega⟩ (by rw [hp]; exact hvalid) + rw [hp] at hn hm + have hc : memoryCost (Stack.Memory.push (L, v).1 (L, v).2) (memoryState frontier x) ≤ 3 := + hcost + refine ⟨y, n, he, hn.trans ?_, hm, rfl, he.bounded hb, fun i => ?_⟩ + · exact Nat.mul_le_mul (by unfold factor; omega) (by omega) + · rw [hframe _ (layoutReg_ne_frontier i) (fun j => layoutReg_ne_tmp _ i j)] + exact prepare_layout _ x i + | pop => + obtain ⟨hi, hd, hf, hs⟩ := layout layoutReg (fun i => by simp only [layoutReg]; omega) + popSlots + obtain ⟨y, ⟨n, hn, he⟩, hm, hout, hframe⟩ := + (realizes_pop.code_spec frontier _ _ hi hd hf hs).2 w L x hr.bounded hregs + ⟨hC ▸ hL, hr.inv⟩ hvalid + have hc : memoryCost (Stack.Memory.pop L) (memoryState frontier x) ≤ 3 := hcost + refine ⟨y, n, he, hn.trans ?_, hm, ?_, he.bounded hr.bounded, fun i => ?_⟩ + · exact Nat.mul_le_mul (by unfold factor; omega) (by omega) + · simp only [result, hout] + exact optionWords_decode _ + · exact hframe _ (layoutReg_ne_frontier i) (fun j => layoutReg_ne_tmp _ i j) + | size => + obtain ⟨hi, hd, hf, hs⟩ := layout layoutReg (fun i => by simp only [layoutReg]; omega) + sizeSlots + obtain ⟨y, ⟨n, hn, he⟩, hm, hout, hframe⟩ := + (realizes_size.code_spec frontier _ _ hi hd hf hs).2 w L x hr.bounded hregs trivial + hvalid + have hc : memoryCost (Stack.Memory.size L) (memoryState frontier x) ≤ 3 := hcost + refine ⟨y, n, he, hn.trans ?_, hm, hout 0, he.bounded hr.bounded, fun i => ?_⟩ + · exact Nat.mul_le_mul (by unfold factor; omega) (by omega) + · exact hframe _ (layoutReg_ne_frontier i) (fun j => layoutReg_ne_tmp _ i j) + +/-- The native stack on layout `L`. -/ +public noncomputable def implementation (w C : ℕ) (L : Stack.Memory.Layout) : + StateTransitionImpl (Stack.spec w C) (exec w C) := + StateTransitionImpl.ofFn (Rep w C L) + (fun o hr hv => by + obtain ⟨y, n, he, _⟩ := exists_body hr o hv + exact ⟨result o y, y, n, he, rfl⟩) + (by + intro vs x o a y n hr hv he + obtain ⟨z, m, hz, _, hm, hres, hb, hl⟩ := exists_body hr o hv + obtain ⟨rfl, -⟩ := he.1.unique hz + rw [← hr.abs_eq] at hv + obtain ⟨-, hstep, hinv⟩ := step_methods hr.valid hr.inv hv + rw [hr.abs_eq] at hstep + rw [hstep, he.2, hres, ← hm] + exact ⟨rfl, hr.valid, fun i => (hl i).trans (hr.layout i), hb, hm ▸ hinv, rfl⟩) + +/-- The representation of the native stack. -/ +public theorem rep_iff (w C : ℕ) (L : Stack.Memory.Layout) (vs : List ℕ) (x : Data registers) : + (implementation w C L).rep vs x ↔ Rep w C L vs x := Iff.rfl + +/-- **Constant time per operation.** One constant `K`, chosen before every width, capacity, +layout, state and operation, bounds the instructions of every admissible operation. -/ +public theorem instruction_bound : ∃ K, ∀ (w C : ℕ) (L : Stack.Memory.Layout), + Bound (implementation w C L) id fun _ _ => K := by + refine ⟨factor * 23, fun w C L => ?_⟩ + intro vs x o a y n hr hv he + obtain ⟨z, m, hz, hm, _⟩ := exists_body hr o hv + obtain ⟨-, rfl⟩ := he.1.unique hz + exact hm + +/-- **Adaptive clients.** Every client whose history is admissible has a native execution, one +operation at a time, that returns the specified answer and represents the specified final stack. +Its instruction total is at most `K` per operation, for one constant `K`. The client's own +computation between operations is outside the machine. -/ +public theorem run_client : ∃ K, ∀ {w C : ℕ} {L : Stack.Memory.Layout} {α : Type} + (p : (Stack.spec w C).Client α) {vs : List ℕ} {x : Data registers}, Rep w C L vs x → + (Stack.spec w C).ValidHistory vs ((Stack.spec w C).clientHistory vs p) → + ∃ y traces, Executes (exec w C) p x ((Stack.spec w C).clientResult vs p) y traces ∧ + Rep w C L ((Stack.spec w C).clientState vs p) y ∧ + total id traces ≤ ((Stack.spec w C).clientHistory vs p).length * K := by + obtain ⟨K, hK⟩ := instruction_bound + exact ⟨K, fun p _ _ hr hv => Bound.exists_executes_const (hK _ _ _) p hr hv⟩ + +/-- Every operation writes only the layout's cells and keeps the frontier. -/ +public theorem modifies {w C : ℕ} {L : Stack.Memory.Layout} {vs : List ℕ} + {x y : Data registers} {o : StackOp ℕ} {a : (Stack.spec w C).Result o} {n : ℕ} + (hr : Rep w C L vs x) (hv : (Stack.spec w C).valid vs o) (he : exec w C x o a y n) : + Modifies L.footprint x.mem y.mem ∧ y.regs frontier = x.regs frontier := by + obtain ⟨z, m, hz, _, hm, _⟩ := exists_body hr o hv + obtain ⟨rfl, -⟩ := he.1.unique hz + rw [← hr.abs_eq] at hv + obtain ⟨hmem, hfr⟩ := Stack.Memory.modifies_methods hr.valid o hv + rw [← hm] at hmem hfr + exact ⟨hmem, hfr⟩ + +/-- Every operation preserves each heap assertion disjoint from the layout. -/ +public theorem frame {w C : ℕ} {L : Stack.Memory.Layout} {vs : List ℕ} + {x y : Data registers} {o : StackOp ℕ} {a : (Stack.spec w C).Result o} {n : ℕ} + (hr : Rep w C L vs x) (hv : (Stack.spec w C).valid vs o) (he : exec w C x o a y n) + {Q : HeapAssertion} (hQ : Q.holds x.mem) (hd : Disjoint Q.footprint L.footprint) : + Q.holds y.mem := + HeapAssertion.frame hQ hd (modifies hr hv he).1 + +/-- The layout of a stack of capacity `C` at `base`: the count cell `base`, then the items. -/ +public def initialLayout (base C : ℕ) : Stack.Memory.Layout := ⟨⟨base, 1⟩, ⟨base + 1, C⟩⟩ + +/-- An empty stack of capacity `C` at `base`, with zero memory: the layout words in their +registers, the frontier just past the items, and every other register zero. This is a +calling-convention state, not an uncharged allocation; `Stack.Memory.allocLayout` allocates. -/ +public noncomputable def initial (base C : ℕ) : Data registers where + regs i := if i = frontier then base + 1 + C + else if h : 1 ≤ i.val ∧ i.val ≤ 4 then layoutWords (initialLayout base C) ⟨i.val - 1, by omega⟩ + else 0 + mem _ := 0 + +/-- The initial state represents the empty stack when the end of the items fits a word. -/ +public theorem initial_rep {w base C : ℕ} (hf : Fits w (base + 1 + C)) : + Rep w C (initialLayout base C) [] (initial base C) := by + have hfr : (initial base C).regs frontier = base + 1 + C := by simp [initial] + have hl (i : Fin 4) : + (initial base C).regs (layoutReg i) = layoutWords (initialLayout base C) i := by + have h1 : 1 ≤ (layoutReg i).val ∧ (layoutReg i).val ≤ 4 := by + simp only [layoutReg]; omega + simp only [initial, layoutReg_ne_frontier i, h1, ↓reduceIte] + congr 1 + refine ⟨Stack.Memory.valid_allocLayout hf, hl, ⟨fun i => ?_, fun _ => fits_zero w⟩, + ⟨?_, ?_, ?_⟩, ?_⟩ + · change Fits w (ite _ _ _) + split + · exact hf + · split + · rename_i h + refine hf.of_le ?_ + have hi : ∀ j : Fin 4, layoutWords (initialLayout base C) j ≤ base + 1 + C := by + intro j + fin_cases j <;> simp [layoutWords, initialLayout] <;> omega + exact hi _ + · exact fits_zero w + · change base + 1 ≤ (initial base C).regs frontier + omega + · change base + 1 + C ≤ (initial base C).regs frontier + omega + · exact Nat.zero_le _ + · simp [Stack.Memory.abs, memoryState, initial] + +end Cslib.DataStructure.Stack.Native diff --git a/Complexity/DataStructure/Stack/Spec.lean b/Complexity/DataStructure/Stack/Spec.lean new file mode 100644 index 0000000000..2ba427c65c --- /dev/null +++ b/Complexity/DataStructure/Stack/Spec.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: candidate + +public import Complexity.Computability.WordRAM.Defs +public import Complexity.DataStructure.Queue.Spec + +/-! +# The bounded word stack + +`Stack.spec w C` restricts the mathematical stack `Queue.stack ℕ` to at most `C` items, each +fitting a `w`-bit word. A push needs spare capacity and a fitting value; pop and size are always +admissible, and a pop from an empty stack returns `none`. `Stack.Memory` implements it over the +memory oracle, and `Stack.Native` runs that implementation on the word RAM. + +## Main definitions + +* `spec`: the bounded stack. + +## 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 + +open Cslib.WordRAM Cslib.DataStructure.Queue + +/-- The stack restricted to `C` items that fit a `w`-bit word. -/ +abbrev spec (w C : ℕ) : StateTransitionSpec := + StateTransitionSpec.ofFn (stack ℕ).stepFn fun vs o => match o with + | .push v => vs.length < C ∧ Fits w v + | .pop | .size => True + +end Cslib.DataStructure.Stack diff --git a/Complexity/DataStructure/Stack/WordRAM/Basic.lean b/Complexity/DataStructure/Stack/WordRAM/Basic.lean deleted file mode 100644 index 8e3894bf10..0000000000 --- a/Complexity/DataStructure/Stack/WordRAM/Basic.lean +++ /dev/null @@ -1,361 +0,0 @@ -/- -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.Dynamic -public import Complexity.DataStructure.Queue.Spec - -/-! -# A fixed-program bounded stack on the word RAM - -A nine-register program dispatches push, pop, and size operations using an encoded operation -tag. The stack occupies a fixed consecutive memory block, with its top at the highest live -address. A pop from an empty stack returns `none`; other pops return a word and decrease the -length without clearing the old cell. Push requires spare capacity and a fitting value. - -The semantic certificate is independent of the separate instruction certificate. Every call -includes its final halt; argument delivery and the initial reserved block are calling-convention -assumptions. This realizes a bounded restriction of the mathematical stack, not its unrestricted -interface. A native instruction theorem for an arbitrary free-monad client still excludes -execution of the client's pure controller. - -## Main definitions - -* `spec`: the mathematical stack with explicit bounded-operation admissibility. -* `tag`, `arg`, `len`, `ptr`, `addr`, `out`, `zero`, `one`, `present`: named registers of the - nine-register dispatcher. -* `program`, `implementation`, `bodyCost`: fixed dispatch program, dynamic calling convention, and - per-operation instruction count. -* `Rep`, `footprint`, `initial`: top-first list representation, reserved memory block, and initial - calling-convention state. -* `certificate`: semantic realization over native word-RAM traces. - -## Main results - -* `execute`: actual per-operation execution, representation, and frame guarantee. -* `instruction_bound`: at most twelve instructions per admissible operation. -* `frame`: every operation preserves any disjoint heap representation. -* `initial_rep`: a concrete empty represented stack. - -## References - -* [T. H. Cormen, C. E. Leiserson, R. L. Rivest and C. Stein, *Introduction to Algorithms*, - Section 10.1][CLRS2009] - -The abstract stack interface is shared with `Cslib.DataStructure.Queue`. Array indexing and -machine execution reuse the checked `WordRAM.Allocation` and `WordRAM.Structured` substrate. --/ - -@[expose] public section - -namespace Cslib.DataStructure.Stack.WordRAM - -open Cslib.WordRAM Cslib.DataStructure.Queue - -/-- A fixed-capacity restriction of the same mathematical stack interface. -/ -abbrev spec (w capacity : ℕ) : DataStructure.StateTransitionSpec := - DataStructure.StateTransitionSpec.ofFn (stack ℕ).stepFn fun vs o => match o with - | .push v => vs.length < capacity ∧ Fits w v - | .pop | .size => True - -/-- Encoded operation tag: push = 0, pop = 1, size = 2. -/ -abbrev tag : Fin 9 := 0 -/-- Caller-supplied push value. -/ -abbrev arg : Fin 9 := 1 -/-- Persistent number of live entries. -/ -abbrev len : Fin 9 := 2 -/-- Persistent base address of the reserved region. -/ -abbrev ptr : Fin 9 := 3 -/-- Temporary cell address. -/ -abbrev addr : Fin 9 := 4 -/-- Returned value. -/ -abbrev out : Fin 9 := 5 -/-- Constant zero. -/ -abbrev zero : Fin 9 := 6 -/-- Constant one. -/ -abbrev one : Fin 9 := 7 -/-- Presence bit distinguishing `none` from a returned zero word. -/ -abbrev present : Fin 9 := 8 - -/-- Fixed operation dispatch. No code is generated from an operation or its payload. -/ -def program : Code .standard 9 := ram[ - zero ← #0; one ← #1; - if tag = zero then [ - ⟪writeAt addr ptr len arg⟫; len ← len + one; out ← zero - ] else [ - if tag = one then [ - if len = zero then [ out ← zero; present ← zero ] else [ - len ← len - one; ⟪readAt addr out ptr len⟫; present ← one - ] - ] else [ out ← len ] - ] ] - -/-- Calling convention for the fixed dispatch program. Answers inspect only output registers. -/ -def implementation (w capacity : ℕ) : DynamicProgram (spec w capacity) 9 where - code := program - output := out - input i := i == tag || i == arg - encode o i := if i = tag then - match o with | .push _ => 0 | .pop => 1 | .size => 2 - else match o with | .push v => v | .pop | .size => 0 - decode o regs := match o with - | .push _ => some () - | .pop => some (if regs present = 0 then none else some (regs out)) - | .size => some (regs out) - -/-- Persistent register, bounded-word, and live array invariants. Cells above the live prefix -may contain old popped values; they are part of the reserved footprint. -/ -structure Rep (w base capacity : ℕ) (vs : List ℕ) (x : Data 9) : Prop where - bounded : x.Bounded w - pointer : x.regs ptr = base - length_eq : x.regs len = vs.length - capacity_le : vs.length ≤ capacity - contents : ∀ i : Fin vs.length, x.mem (base + i.val) = vs.reverse[i.val] - -/-- Reserved memory footprint; it stays fixed through pushes and pops. -/ -def footprint (base capacity : ℕ) : Finset ℕ := - (Block.mk base capacity).footprint - -/-- Encoded input registers fit in a `w`-bit word for every admissible stack operation. -/ -theorem arguments_fit {w capacity : ℕ} (h2 : Fits w 2) {vs : List ℕ} (o : StackOp ℕ) - (hv : (spec w capacity).valid vs o) (i : Fin 9) : - Fits w ((implementation w capacity).encode o i) := by - cases o with - | push v => - change Fits w (if i = tag then 0 else v) - split <;> first | exact fits_zero w | exact hv.2 - | pop => - change Fits w (if i = tag then 1 else 0) - split - · exact h2.of_le (by decide) - · exact fits_zero w - | size => - change Fits w (if i = tag then 2 else 0) - split <;> first | exact h2 | exact fits_zero w - -private theorem prepare_bounded {w capacity : ℕ} (h2 : Fits w 2) - {vs : List ℕ} {x : Data 9} {o : StackOp ℕ} - (hx : x.Bounded w) (hv : (spec w capacity).valid vs o) : - ((implementation w capacity).prepare o x).Bounded w := - (implementation w capacity).prepare_bounded hx (fun i _ => arguments_fit h2 o hv i) - -/-- Actual instructions excluding the final halt. -/ -def bodyCost (vs : List ℕ) : StackOp ℕ → ℕ - | .push _ => 7 - | .pop => if vs = [] then 8 else 11 - | .size => 7 - -private theorem execute_push {w base capacity : ℕ} (h2 : Fits w 2) - (hcap : Fits w (base + capacity)) {vs : List ℕ} {x : Data 9} - (hr : Rep w base capacity vs x) (v : ℕ) (hv : vs.length < capacity) (hw : Fits w v) : - ∃ y, Exec w program ((implementation w capacity).prepare (.push v) x) y 7 ∧ - Rep w base capacity (v :: vs) y ∧ Modifies (footprint base capacity) x.mem y.mem := by - have h1 : Fits w 1 := h2.of_le (by decide) - have haddr : Fits w (base + vs.length) := hcap.of_le (by omega) - have hinc : Fits w (vs.length + 1) := hcap.of_le (by omega) - let y := ((((((implementation w capacity).prepare (.push v) x).set zero 0).set one 1).set - addr (base + vs.length)).write (base + vs.length) v |>.set len (vs.length + 1)).set out 0 - have he : Exec w program ((implementation w capacity).prepare (.push v) x) y 7 := by - ram_eval [program, implementation, DynamicProgram.prepare, writeAt, y, - hr.pointer, hr.length_eq, h1, haddr, hinc] - refine ⟨y, he, ?_, ?_⟩ - · refine ⟨?_, ?_, ?_, ?_, ?_⟩ - · exact he.bounded (prepare_bounded (capacity := capacity) (vs := vs) - (o := .push v) h2 hr.bounded ⟨hv, hw⟩) - · simp +decide [y, Data.set, Data.write, implementation, DynamicProgram.prepare, hr.pointer] - · simp +decide [y, Data.set, Data.write, implementation, DynamicProgram.prepare] - · simpa using hv - · intro i - by_cases hi : i.val < vs.length - · have hne : base + i.val ≠ base + vs.length := by omega - simpa [y, Data.set, Data.write, implementation, DynamicProgram.prepare, - List.reverse_cons, List.getElem_append, hi, Function.update_of_ne hne] using - hr.contents ⟨i.val, hi⟩ - · have hi' : i.val = vs.length := by - have := i.isLt - simp only [List.length_cons] at this - omega - simp [y, Data.set, Data.write, implementation, DynamicProgram.prepare, - List.reverse_cons, hi'] - · exact Modifies.write_of_owns ⟨Nat.le_add_right _ _, Nat.add_lt_add_left hv _⟩ v - -private theorem execute_pop_cons {w base capacity : ℕ} (h2 : Fits w 2) - (hcap : Fits w (base + capacity)) {vs : List ℕ} {x : Data 9} {v : ℕ} - (hr : Rep w base capacity (v :: vs) x) : - ∃ y, Exec w program ((implementation w capacity).prepare .pop x) y 11 ∧ - (implementation w capacity).decode .pop y.regs = some (some v) ∧ - Rep w base capacity vs y ∧ Modifies (footprint base capacity) x.mem y.mem := by - have h1 : Fits w 1 := h2.of_le (by decide) - have hl : x.regs len = vs.length + 1 := hr.length_eq - have hsub : Fits w vs.length := hcap.of_le (by have := hr.capacity_le; simp_all; omega) - have ha : Fits w (base + vs.length) := hcap.of_le (by have := hr.capacity_le; simp_all; omega) - have hread : x.mem (base + vs.length) = v := by - have h := hr.contents ⟨vs.length, by simp⟩ - simpa [List.reverse_cons] using h - let y := ((((((implementation w capacity).prepare .pop x).set zero 0).set one 1).set - len vs.length).set addr (base + vs.length)).set out v |>.set present 1 - have he : Exec w program ((implementation w capacity).prepare .pop x) y 11 := by - ram_eval [program, implementation, DynamicProgram.prepare, readAt, y, - hr.pointer, hl, h1, ha, hsub, Nat.add_sub_cancel, Nat.add_eq_zero_iff, and_false, hread] - refine ⟨y, he, ?_, ?_, Modifies.refl _ _⟩ - · simp +decide [implementation, y, Data.set] - · refine ⟨he.bounded (prepare_bounded (vs := v :: vs) h2 hr.bounded trivial), ?_, ?_, ?_, ?_⟩ - · simp +decide [y, Data.set, implementation, DynamicProgram.prepare, hr.pointer] - · simp +decide [y, Data.set] - · have := hr.capacity_le - simp only [List.length_cons] at this - omega - · intro i - have hi : i.val < (v :: vs).length := by - simp only [List.length_cons] - omega - have h := hr.contents ⟨i.val, hi⟩ - simpa [y, Data.set, DynamicProgram.prepare, List.reverse_cons, - List.getElem_append, i.isLt] using h - -private theorem execute_pop_nil {w base capacity : ℕ} (h2 : Fits w 2) - {x : Data 9} (hr : Rep w base capacity [] x) : - ∃ y, Exec w program ((implementation w capacity).prepare .pop x) y 8 ∧ - (implementation w capacity).decode .pop y.regs = some none ∧ - Rep w base capacity [] y ∧ Modifies (footprint base capacity) x.mem y.mem := by - have h1 : Fits w 1 := h2.of_le (by decide) - have hl : x.regs len = 0 := hr.length_eq - let y := ((((implementation w capacity).prepare .pop x).set zero 0).set one 1).set - out 0 |>.set present 0 - have he : Exec w program ((implementation w capacity).prepare .pop x) y 8 := by - ram_eval [program, implementation, DynamicProgram.prepare, y, hl, h1] - refine ⟨y, he, ?_, ?_, Modifies.refl _ _⟩ - · simp +decide [implementation, y, Data.set] - · refine ⟨he.bounded (prepare_bounded (vs := []) h2 hr.bounded trivial), ?_, ?_, - Nat.zero_le _, fun i => Fin.elim0 i⟩ - · simp +decide [y, Data.set, implementation, DynamicProgram.prepare, hr.pointer] - · simp +decide [y, Data.set, implementation, DynamicProgram.prepare, hl] - -private theorem execute_size {w base capacity : ℕ} (h2 : Fits w 2) - {vs : List ℕ} {x : Data 9} (hr : Rep w base capacity vs x) : - ∃ y, Exec w program ((implementation w capacity).prepare .size x) y 7 ∧ - (implementation w capacity).decode .size y.regs = some vs.length ∧ - Rep w base capacity vs y ∧ Modifies (footprint base capacity) x.mem y.mem := by - have h1 : Fits w 1 := h2.of_le (by decide) - let y := (((implementation w capacity).prepare .size x).set zero 0).set one 1 |>.set - out vs.length - have he : Exec w program ((implementation w capacity).prepare .size x) y 7 := by - ram_eval [program, implementation, DynamicProgram.prepare, y, hr.length_eq, h1] - refine ⟨y, he, ?_, ?_, Modifies.refl _ _⟩ - · simp [implementation, y, Data.set] - · refine ⟨he.bounded (prepare_bounded (vs := vs) h2 hr.bounded trivial), ?_, ?_, - hr.capacity_le, hr.contents⟩ - · simp +decide [y, Data.set, implementation, DynamicProgram.prepare, hr.pointer] - · simp +decide [y, Data.set, implementation, DynamicProgram.prepare, hr.length_eq] - -/-- Every admissible operation executes the fixed dispatcher with an exact instruction count, -returns the specified answer, and preserves memory outside the reserved region. -/ -theorem execute {w base capacity : ℕ} (h2 : Fits w 2) (hcap : Fits w (base + capacity)) - {vs : List ℕ} {x : Data 9} (hr : Rep w base capacity vs x) (o : StackOp ℕ) - (hv : (spec w capacity).valid vs o) : - ∃ y, Exec w program ((implementation w capacity).prepare o x) y (bodyCost vs o) ∧ - (implementation w capacity).decode o y.regs = some ((spec w capacity).stepFn vs o).2 ∧ - Rep w base capacity ((spec w capacity).stepFn vs o).1 y ∧ - Modifies (footprint base capacity) x.mem y.mem := by - cases o with - | push v => - obtain ⟨y, he, hr, hf⟩ := execute_push h2 hcap hr v hv.1 hv.2 - exact ⟨y, he, rfl, hr, hf⟩ - | pop => - cases vs with - | nil => exact execute_pop_nil h2 hr - | cons v vs => - simpa only [bodyCost, List.cons_ne_nil, ↓reduceIte, List.head?_cons, - List.tail_cons] using execute_pop_cons h2 hcap hr - | size => exact execute_size h2 hr - -/-- Semantic realization of the bounded stack, independently of its performance theorem. -/ -def certificate {w base capacity : ℕ} (h2 : Fits w 2) (hcap : Fits w (base + capacity)) : - DynamicCertificate (implementation w capacity) w := - DynamicCertificate.ofFn - (rep := Rep w base capacity) - (bounded := fun h => h.bounded) - (arguments_fit := fun o hv i _ => arguments_fit h2 o hv i) - (execute := fun o hr hv => by - obtain ⟨y, he, ha, hr, _⟩ := Stack.WordRAM.execute h2 hcap hr o hv - exact ⟨y, _, he, ha, hr⟩) - -/-- Actual instruction count, including dispatch and the final halt, for every native call. -/ -theorem instructions_eq {w base capacity : ℕ} (h2 : Fits w 2) - (hcap : Fits w (base + capacity)) {vs : List ℕ} {x y : Data 9} {o : StackOp ℕ} - {a : (spec w capacity).Result o} {trace : DynamicProgram.OperationTrace} - (hr : Rep w base capacity vs x) (hv : (spec w capacity).valid vs o) - (he : (implementation w capacity).ExecOp w x o a y trace) : - trace.instructions = bodyCost vs o + 1 := by - obtain ⟨z, hz, _, _, _⟩ := execute h2 hcap hr o hv - exact (he.eq_of_exec hz).2 - -/-- Every admissible call has a separate worst-case bound of twelve native instructions. -/ -theorem instruction_bound {w base capacity : ℕ} (h2 : Fits w 2) - (hcap : Fits w (base + capacity)) : - DataStructure.Bound (certificate h2 hcap).impl - DynamicProgram.OperationTrace.instructions (fun _ _ => 12) := - (certificate h2 hcap).instruction_bound_const 12 fun o hr hv => by - obtain ⟨y, he, _⟩ := execute h2 hcap hr o hv - refine ⟨y, bodyCost _ o, he, ?_⟩ - cases o with - | push | size => simp +decide [bodyCost] - | pop => dsimp only [bodyCost]; split <;> decide - -/-- Every concrete call preserves all memory outside the same reserved stack block. -/ -theorem modifies {w base capacity : ℕ} (h2 : Fits w 2) (hcap : Fits w (base + capacity)) - {vs : List ℕ} {x y : Data 9} {o : StackOp ℕ} - {a : (spec w capacity).Result o} {trace : DynamicProgram.OperationTrace} - (hr : Rep w base capacity vs x) (hv : (spec w capacity).valid vs o) - (he : (implementation w capacity).ExecOp w x o a y trace) : - Modifies (footprint base capacity) x.mem y.mem := - (certificate h2 hcap).modifies_of_execute - (fun o hr' hv' => let ⟨z, hz, _, _, hf⟩ := execute h2 hcap hr' o hv'; ⟨z, _, hz, hf⟩) - hr hv he - -/-- The machine stack preserves any disjoint heap representation. -/ -theorem frame {w base capacity : ℕ} (h2 : Fits w 2) (hcap : Fits w (base + capacity)) - {vs : List ℕ} {x y : Data 9} {o : StackOp ℕ} - {a : (spec w capacity).Result o} {trace : DynamicProgram.OperationTrace} - (hr : Rep w base capacity vs x) (hv : (spec w capacity).valid vs o) - (he : (implementation w capacity).ExecOp w x o a y trace) - {Q : HeapAssertion} (hQ : Q.holds x.mem) - (hd : Disjoint Q.footprint (footprint base capacity)) : Q.holds y.mem := - HeapAssertion.frame hQ hd (modifies h2 hcap hr hv he) - -/-- An empty reserved block, with all memory zero and the persistent pointer installed. -This is a calling-convention initial state, not an uncharged allocation instruction. -/ -def initial (base : ℕ) : Data 9 where - regs i := if i = ptr then base else 0 - mem _ := 0 - -/-- The initial representation is inhabited for every fitting reserved block. -/ -theorem initial_rep {w base capacity : ℕ} (hcap : Fits w (base + capacity)) : - Rep w base capacity [] (initial base) := by - refine ⟨⟨fun i => ?_, fun _ => fits_zero w⟩, ?_, ?_, Nat.zero_le _, fun i => Fin.elim0 i⟩ - · change Fits w (if i = ptr then base else 0) - split - · exact hcap.of_le (Nat.le_add_right _ _) - · exact fits_zero w - · simp [initial] - · simp +decide [initial] - -/-- One-operation realization and an independent resource certificate compose across arbitrary -admissible adaptive clients. The client controller itself is external to the machine code. -/ -theorem run_client {w base capacity : ℕ} (h2 : Fits w 2) (hcap : Fits w (base + capacity)) - {α : Type} (p : (spec w capacity).Client α) {vs : List ℕ} {x : Data 9} - (hr : Rep w base capacity vs x) - (hv : (spec w capacity).ValidHistory vs ((spec w capacity).runClient vs p).2.2) : - ∃ y traces, DataStructure.Executes ((implementation w capacity).ExecOp w) p x - ((spec w capacity).runClient vs p).1 y traces ∧ - Rep w base capacity ((spec w capacity).runClient vs p).2.1 y ∧ - DataStructure.total DynamicProgram.OperationTrace.instructions traces ≤ - ((spec w capacity).runClient vs p).2.2.length * 12 := - (certificate h2 hcap).run_client_const (instruction_bound h2 hcap) p hr hv - -end Cslib.DataStructure.Stack.WordRAM diff --git a/Complexity/DataStructure/Stack/WordRAM/Controller.lean b/Complexity/DataStructure/Stack/WordRAM/Controller.lean deleted file mode 100644 index 5f285a32f5..0000000000 --- a/Complexity/DataStructure/Stack/WordRAM/Controller.lean +++ /dev/null @@ -1,234 +0,0 @@ -/- -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.Stack.WordRAM.Basic -public import Complexity.Tactic.WordRAM - -/-! -# An adaptive stack client executed entirely on the word RAM - -The client pushes its input, pops it, and replaces a returned zero by another push and pop of -one. Its fixed controller explicitly executes initialization, argument preparation and branch -instructions. Input placement follows `WordRAM.Data.init`; no additional heap initialization is -needed because the reserved stack starts empty. - -## Main definitions - -* `roundTrip`, `setup`: push/pop round trip and four-instruction stack initialization. -* `code`: initialize one reserved cell and execute the adaptive controller. -* `client`: the same adaptive computation over the abstract stack interface. -* `positive`: the word-valued function `max n 1` computed by the adaptive stack controller. - -## Main results - -* `execute`: the whole controller returns `max n 1`, leaving its stack empty. -* `refines`: the fixed machine controller implements the abstract adaptive client. -* `executes`: a halting flat program, with all controller costs and its final halt charged. -* `positive_polytime`: a reusable polynomial RAM-time certificate for the complete client. - -## References - -The stack implementation and specification are those of `DataStructure.Stack.WordRAM` and -`DataStructure.Queue`. This example is a certificate-composition test, not a new stack algorithm. --/ - -@[expose] public section - -namespace Cslib.DataStructure.Stack.WordRAM.Controller - -open Cslib.WordRAM Cslib.DataStructure.Queue - -/-- Push the argument register, then pop it; argument tags and clearing are real instructions. -/ -def roundTrip : Code .standard 9 := ram[ - tag ← #0; ⟪program⟫; tag ← #1; arg ← #0; ⟪program⟫ ] - -/-- One reserved stack cell at address two, outside the singleton input at address one. -/ -def setup : Code .standard 9 := ram[ - ptr ← #2; len ← #0; addr ← #1; arg ← *addr ] - -/-- Fixed adaptive code: replace a popped zero using another pair of stack operations. -/ -def code : Code .standard 9 := ram[ - ⟪setup⟫; ⟪roundTrip⟫; - if out = len then [ arg ← #1; ⟪roundTrip⟫ ] else [ out ← out ] ] - -/-- The abstract controller observes its first pop before deciding whether to call again. -/ -def client (w n : ℕ) : (spec w 1).Client ℕ := - .liftBind (.push n) fun _ => .liftBind .pop fun (a : Option ℕ) => - if a = some 0 then - .liftBind (.push 1) fun _ => .liftBind .pop fun (b : Option ℕ) => .pure (b.getD 0) - else .pure (a.getD 0) - -private theorem prepare_push {w n : ℕ} {x : Data 9} (hn : x.regs arg = n) : - (implementation w 1).prepare (.push n) x = x.set tag 0 := by - ext i - · fin_cases i <;> simp_all +decide [implementation, DynamicProgram.prepare, Data.set, - tag, arg] - · rfl - -private theorem prepare_pop (w : ℕ) (x : Data 9) : - (implementation w 1).prepare .pop x = (x.set tag 1).set arg 0 := by - ext i - · fin_cases i <;> simp +decide [implementation, DynamicProgram.prepare, Data.set, tag, arg] - · rfl - -/-- A full push/pop round trip costs twenty-one instructions, including argument delivery. -/ -private theorem roundTrip_exec {w n : ℕ} {x : Data 9} (h3 : Fits w 3) - (hr : Rep w 2 1 [] x) (hn : x.regs arg = n) : - ∃ y, Exec w roundTrip x y 21 ∧ Rep w 2 1 [] y ∧ y.regs out = n := by - have h2 : Fits w 2 := h3.of_le (by decide) - have h1 : Fits w 1 := h3.of_le (by decide) - have hfit : Fits w n := hn ▸ hr.bounded.1 arg - obtain ⟨y, hy, _, hry, _⟩ := Stack.WordRAM.execute h2 h3 hr (.push n) ⟨by decide, hfit⟩ - obtain ⟨z, hz, ha, hrz, _⟩ := Stack.WordRAM.execute h2 h3 hry .pop trivial - have hp : Exec (B := .standard) w (.op (.const tag 0)) x - ((implementation w 1).prepare (.push n) x) 1 := by - rw [prepare_push hn] - exact Exec.const (fits_zero w) - refine ⟨z, ?_, hrz, ?_⟩ - · exact Exec.seq hp (Exec.seq hy (Exec.seq (Exec.const h1) - (Exec.seq (Exec.const (fits_zero w)) (by simpa only [prepare_pop] using hz)))) - · change some (if z.regs present = 0 then none else some (z.regs out)) = some (some n) at ha - split at ha - · cases ha - · exact Option.some.inj (Option.some.inj ha) - -/-- Initialize the empty representation and load the input in four actual instructions. -/ -private theorem setup_exec {w n : ℕ} (h3 : Fits w 3) (hn : InputFits w [n]) : - ∃ x, Exec w setup (Data.init [n]) x 4 ∧ Rep w 2 1 [] x ∧ x.regs arg = n := by - let x := (((Data.init [n]).set ptr 2).set len 0).set addr 1 |>.set arg n - have h2 : Fits w 2 := h3.of_le (by decide) - have h1 : Fits w 1 := h3.of_le (by decide) - have he : Exec w setup (Data.init [n]) x 4 := by - ram_eval [setup, x, Data.init, h2, h1] - refine ⟨x, he, ⟨he.bounded (Data.Bounded.init hn), ?_, ?_, by decide, ?_⟩, ?_⟩ - · simp +decide [x, Data.set, ptr, len, addr, arg] - · simp +decide [x, Data.set, ptr, len, addr, arg] - · intro i - exact Fin.elim0 i - · simp [x, Data.set] - -private theorem rep_set_arg {w : ℕ} {x : Data 9} (hr : Rep w 2 1 [] x) - (h1 : Fits w 1) : Rep w 2 1 [] (x.set arg 1) := by - refine ⟨(Exec.const (B := Basis.standard) (d := arg) (x := x) h1).bounded hr.bounded, - ?_, ?_, by decide, fun i => Fin.elim0 i⟩ - · simpa +decide [Data.set, arg, ptr] using hr.pointer - · simpa +decide [Data.set, arg, len] using hr.length_eq - -/-- The exact controller time includes setup, adaptive control and argument preparation. -The final halt is added separately by `Exec.executes`. -/ -theorem execute {w n : ℕ} (h3 : Fits w 3) (hn : InputFits w [n]) : - ∃ y, Exec w code (Data.init [n]) y (if n = 0 then 48 else 28) ∧ - Rep w 2 1 [] y ∧ y.regs out = max n 1 := by - obtain ⟨x, hx, hrx, hnx⟩ := setup_exec h3 hn - obtain ⟨y, hy, hry, hny⟩ := roundTrip_exec h3 hrx hnx - have hl : y.regs len = 0 := hry.length_eq - have h1 : Fits w 1 := h3.of_le (by decide) - by_cases hn0 : n = 0 - · obtain ⟨z, hz, hrz, hnz⟩ := roundTrip_exec h3 (rep_set_arg hry h1) - (n := 1) (by simp [Data.set]) - refine ⟨z, ?_, hrz, ?_⟩ - · rw [ite_eq_left hn0] - exact Exec.seq hx (Exec.seq hy (Exec.branch_pos (c := .eq out len) (by - change y.regs out = y.regs len - omega) (Exec.seq (Exec.const h1) hz))) - · simpa [hn0] using hnz - · have hc : Exec (B := .standard) w (.op (.copy out out)) y y 1 := by - convert (Exec.copy (B := Basis.standard) (x := y) (d := out) (s := out)) using 1 - ext i <;> simp [Data.set] - refine ⟨y, ?_, hry, ?_⟩ - · simp only [hn0, ↓reduceIte] - exact Exec.seq hx (Exec.seq hy (Exec.branch_neg (by - change ¬y.regs out = y.regs len - omega) hc)) - · rw [hny, max_eq_left (by omega)] - -/-- The abstract client returns the same positive value and restores the empty stack. -/ -private theorem runClient_client (w n : ℕ) : - ((spec w 1).runClient [] (client w n)).1 = max n 1 ∧ - ((spec w 1).runClient [] (client w n)).2.1 = [] := by - by_cases hn : n = 0 - · simp [client, -PFunctor.FreeM.pure_eq_pure, hn] - · simp [client, -PFunctor.FreeM.pure_eq_pure, hn, - max_eq_left (show 1 ≤ n by omega)] - -/-- Both adaptive histories respect the one-cell capacity and word bounds. -/ -private theorem client_valid {w n : ℕ} (h3 : Fits w 3) (hn : InputFits w [n]) : - (spec w 1).ValidHistory [] ((spec w 1).runClient [] (client w n)).2.2 := by - have h1 : Fits w 1 := h3.of_le (by decide) - have hfit : Fits w n := hn.2 n (by simp) - by_cases hn0 : n = 0 <;> - simp [client, -PFunctor.FreeM.pure_eq_pure, hn0, hfit, h1] - -/-- The full fixed controller refines the adaptive client, including setup and argument work. -/ -@[program_certificate] -theorem refines {w n : ℕ} (h3 : Fits w 3) (hn : InputFits w [n]) : - Refines w (fun s x => s = [] ∧ x = Data.init [n]) (client w n) - (fun a s y => Rep w 2 1 s y ∧ y.regs out = a) - (fun _ _ => if n = 0 then 48 else 28) code := by - apply Refines.ofFn - rintro s x ⟨rfl, rfl⟩ - obtain ⟨y, he, hr, ha⟩ := execute h3 hn - obtain ⟨hv, hs⟩ := runClient_client w n - refine ⟨client_valid h3 hn, y, he.within, ?_, ?_⟩ - · simpa only [hs] using hr - · simpa only [hv] using ha - -/-- A single flat program computes the positive replacement, including its final halt. -/ -theorem executes {w n : ℕ} (h3 : Fits w 3) (hn : InputFits w [n]) : - Cslib.WordRAM.Executes w (code.program out) (Cslib.WordRAM.State.init [n]) (max n 1) - ((if n = 0 then 48 else 28) + 1) := by - obtain ⟨y, he, _, ha⟩ := execute h3 hn - simpa only [ha, Cslib.WordRAM.State.init] using he.executes out - -/-- The shared refinement API produces a uniform forty-nine-instruction certificate. -/ -theorem executes_le {w n : ℕ} (h3 : Fits w 3) (hn : InputFits w [n]) : - ∃ t ≤ 49, Cslib.WordRAM.Executes w (code.program out) (Cslib.WordRAM.State.init [n]) - (max n 1) t := by - obtain ⟨t, ht, hb⟩ := (refines h3 hn).executes out id - (fun _ _ _ h => h.2) ⟨rfl, rfl⟩ - refine ⟨t, ?_, ?_⟩ - · split at hb <;> omega - · simpa only [id_eq, (runClient_client w n).1, Cslib.WordRAM.State.init] using ht - -/-- The word-valued function computed by the adaptive stack controller. -/ -def positive (n : ℕ) : ℕ := max n 1 - -/-- Uniform computation at every fitting word size of at least two bits. -/ -theorem computesInTime : - (code.program out).ComputesInTime (fun _ n => [n]) (fun _ _ => 49) - (fun _ w => 2 ≤ w) positive := by - intro n w hw hn - have h3 : Fits w 3 := (show Fits 2 3 by decide).mono hw - obtain ⟨t, ht, he⟩ := executes_le h3 hn - exact ⟨t, he, ht⟩ - -/-- The same fixed controller has a constant, hence polynomial, RAM instruction bound. -The size parameter is the numerical magnitude `n`; the admissible width is logarithmic in it. -/ -@[fun_prop, program_certificate] -theorem positive_polytime : - UniformComputableInPolyTime .standard positive (fun _ n => [n]) id := by - apply UniformComputableInPolyTime.of_program (code.program out) (.const 49) 2 (by decide) - apply computesInTime.mono (fun _ _ => le_rfl) - intro n w hw - change 2 * (Nat.log 2 n + 1) ≤ w at hw - omega - -/-- The logarithmic threshold used by the function certificate fits every input, so the -uniform computation theorem has a successful execution for every natural input. -/ -theorem input_fits (n : ℕ) : InputFits (logWidth 2 n) [n] := by - have hw : 2 ≤ logWidth 2 n := by unfold logWidth; omega - refine ⟨?_, ?_⟩ - · exact (show Fits 2 1 by decide).mono hw - · intro v hv - have hvn : v = n := by simpa using hv - subst v - exact (fits_of_lt_pow (k := 1) (n := n) (by simp)).mono - (logWidth_mono (by decide) le_rfl) - -end Cslib.DataStructure.Stack.WordRAM.Controller diff --git a/ComplexityTests/DataStructureStack.lean b/ComplexityTests/DataStructureStack.lean new file mode 100644 index 0000000000..2813398870 --- /dev/null +++ b/ComplexityTests/DataStructureStack.lean @@ -0,0 +1,46 @@ +/- +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.DataStructure.Stack.Controller +import Complexity.DataStructure.Stack.Native + +/-! The bounded stack: admissibility at the boundary, an adaptive client through the native +stack from the initial state, and the compiled controller on concrete inputs. -/ + +namespace ComplexityTests.DataStructureStack + +open Cslib Cslib.WordRAM Cslib.DataStructure Cslib.DataStructure.Queue + +/-- A full stack rejects another push, while the largest fitting word remains a valid value. -/ +example : ¬(Stack.spec 4 2).valid [4, 7] (.push 2) ∧ (Stack.spec 4 2).valid [] (.push 15) := by + decide + +/-- An adaptive client chooses whether to pop from the observed size. -/ +def stackClient : (Stack.spec 4 2).Client (Option ℕ × ℕ) := + .liftBind (.push 7) fun _ => + .liftBind .size fun (n : ℕ) => + if n = 1 then + .liftBind .pop fun v => .liftBind .size fun (m : ℕ) => .pure (v, m) + else .pure (none, n) + +/-- The native stack runs the client from the initial state, one operation at a time: it pops +the pushed value, ends empty, and spends at most the constant `K` on each of its four calls. -/ +example : ∃ K, ∃ y traces, + Executes (Stack.Native.exec 4 2) stackClient (Stack.Native.initial 3 2) (some 7, 0) y traces ∧ + Stack.Native.Rep 4 2 (Stack.Native.initialLayout 3 2) [] y ∧ total id traces ≤ 4 * K := by + obtain ⟨K, hK⟩ := Stack.Native.run_client + exact ⟨K, hK stackClient (Stack.Native.initial_rep (by decide)) (by + change (0 < 2 ∧ Fits 4 7) ∧ True ∧ True ∧ True ∧ True + decide)⟩ + +/-- The compiled controller's memory program returns `max n 1`, here at both branches. -/ +example : (Memory.spec 4).clientResult (Realization.inputState [0]) (Stack.Controller.program 0) = + 1 ∧ + (Memory.spec 4).clientResult (Realization.inputState [5]) (Stack.Controller.program 5) = 5 := + ⟨(Stack.Controller.program_spec (by decide) (by decide)).1, + (Stack.Controller.program_spec (by decide) (by decide)).1⟩ + +end ComplexityTests.DataStructureStack diff --git a/ComplexityTests/NativeChanHeTraversal.lean b/ComplexityTests/NativeChanHeTraversal.lean index b1ebba1f66..173b94637f 100644 --- a/ComplexityTests/NativeChanHeTraversal.lean +++ b/ComplexityTests/NativeChanHeTraversal.lean @@ -9,11 +9,11 @@ import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.TraversalExec import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.TraversalRoot open Cslib.WordRAM -open Cslib.DataStructure Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Traversal +open Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Traversal private def fixture : Data 128 where - regs i := if i = registers Stack.WordRAM.ptr then 20 - else if i = registers Stack.WordRAM.len then 0 + regs i := if i = registers WordStack.ptr then 20 + else if i = registers WordStack.len then 0 else if 64 ≤ i.val ∧ i.val < 71 then i.val - 60 else 79 mem _ := 79 @@ -25,7 +25,7 @@ private def roundTrip : Code .standard 128 := .seq pushFrame popFrame #guard_msgs in #eval decide ((Code.eval 8 16 roundTrip fixture).map (fun (y, _) => (List.ofFn (fun i : Fin 7 => y.regs (slot i)), - y.regs 0, y.regs (registers Stack.WordRAM.len), y.mem 19, y.mem 27)) = + y.regs 0, y.regs (registers WordStack.len), y.mem 19, y.mem 27)) = some ([4, 5, 6, 7, 8, 9, 10], 79, 0, 79, 79)) -- Address arithmetic really runs in the admitted physical width. @@ -33,7 +33,7 @@ private def roundTrip : Code .standard 128 := .seq pushFrame popFrame #guard_msgs in #eval (Code.eval 1 16 pushFrame fixture).isNone -private theorem fixture_rep : Stack.WordRAM.Rep 8 20 7 [] (fixture.comap registers) := by +private theorem fixture_rep : WordStack.Rep 8 20 7 [] (fixture.comap registers) := by refine ⟨?_, ?_, ?_, by decide, fun i => Fin.elim0 i⟩ · constructor · intro i @@ -56,7 +56,7 @@ private theorem fixture_rep : Stack.WordRAM.Rep 8 20 7 [] (fixture.comap registe -- The native transfer contracts have a fully admitted client, including old nonzero memory. example : ∃ y, Exec 8 roundTrip fixture y 163 ∧ - Stack.WordRAM.Rep 8 20 7 [] (y.comap registers) ∧ + WordStack.Rep 8 20 7 [] (y.comap registers) ∧ (∀ i, y.regs (slot i) = fixture.regs (slot i)) := by have hwords (i : Fin 7) : Fits 8 (fixture.regs (slot i)) := by dsimp only [fixture] @@ -111,7 +111,7 @@ private def treeInput (f n : ℕ) (z : ℤ) : Data 128 where private def observeTree (f n : ℕ) (z : ℤ) := (Code.eval 16 32 oneRoot (treeInput f n z)).map fun (y, _) => - (y.regs outputCount, y.regs heap, y.mem 60, y.regs (registers Stack.WordRAM.len), + (y.regs outputCount, y.regs heap, y.mem 60, y.regs (registers WordStack.len), (List.range 32).map (fun i => y.mem (106 + i))) private def packedSingleton (z : ℤ) : List ℕ := @@ -168,7 +168,7 @@ private def expectedBranching : List (List ℕ) := /-- info: true -/ #guard_msgs in #eval decide ((Code.eval 16 64 oneRoot branchingInput).map (fun (y, _) => - (y.regs outputCount, y.regs heap, y.regs (registers Stack.WordRAM.len), + (y.regs outputCount, y.regs heap, y.regs (registers WordStack.len), [y.mem 60, y.mem 61], [108, 156].map fun p => (List.range 32).map fun i => y.mem (p + i))) = some (2, 196, 0, [108, 156], expectedBranching)) diff --git a/ComplexityTests/WordRAMController.lean b/ComplexityTests/WordRAMController.lean index e97525ebb4..b29e07a6b7 100644 --- a/ComplexityTests/WordRAMController.lean +++ b/ComplexityTests/WordRAMController.lean @@ -7,201 +7,191 @@ Authors: Samuel Schlesinger module -- upstream: local-only (test driver) -public import Complexity.DataStructure.Stack.WordRAM.Controller +public import ComplexityTests.WordRAMDynamic +public import Complexity.DataStructure.Stack.Controller +public import Complexity.Tactic.WordRAM /-! -# Controller certificates on concrete stack code +# Controller certificates on concrete code -The adaptive size/push client passes an observed size into a subsequent operation using one +An adaptive client of the exchange cell passes the first answer into a second exchange with one fixed continuation fragment. Additional tests exercise charged branching and initialization, -both exact execution paths, the smallest admissible word width, and rejection of a false budget. +both exact execution paths, the smallest admissible word width, rejection of a false budget, and +the polynomial-time automation on the compiled stack controller. -/ @[expose] public section namespace ComplexityTests.WordRAMController -open Cslib Cslib.WordRAM Cslib.DataStructure.Stack.WordRAM +open Cslib Cslib.WordRAM WordRAMDynamicTests -private def sizePrep : Code .standard 9 := ram[tag ← #2; arg ← #0] -private def pushPrep : Code .standard 9 := ram[arg ← out; tag ← #0] +private def firstPrep (v : ℕ) : Code .standard 3 := .op (.const 1 v) +private def secondPrep : Code .standard 3 := .op (.copy 1 2) -private def entry (w : ℕ) (s : List ℕ) (x : Data 9) : Prop := - Rep w 2 2 s x ∧ s.length < 2 +private def entry (w : ℕ) (s : ℕ) (x : Data 3) : Prop := (exchangeCertificate w).rep s x -private def mid (w a : ℕ) (s : List ℕ) (x : Data 9) : Prop := - entry w s x ∧ x.regs out = a ∧ a = s.length +private def mid (w a : ℕ) (s : ℕ) (x : Data 3) : Prop := entry w s x ∧ x.regs 2 = a -private theorem size_refines {w : ℕ} (h4 : Fits w 4) : - Refines w (entry w) (D := spec w 2) (.liftBind .size fun a => .pure a) - (mid w) (fun _ _ => 9) (.seq sizePrep program) := by - have h2 : Fits w 2 := h4.of_le (by decide) - have hcall := Refines.call (certificate h2 h4) .size - (pre := entry w) (prep := sizePrep) (setup := fun _ _ => 2) (body := fun _ _ => 7) - (fun _ _ h => ⟨h.1, trivial⟩) +private theorem exchange_exec (w : ℕ) (x : Data 3) : ∃ y, Exec w (exchangeProgram w).code x y 3 := + ⟨_, (Exec.const (fits_zero w)).seq (Exec.load.seq Exec.store)⟩ + +private theorem first_refines {w v : ℕ} (hv : Fits w v) : + Refines w (entry w) (D := exchangeSpec w) (.liftBind v fun a => .pure a) + (mid w) (fun _ _ => 4) (.seq (firstPrep v) (exchangeProgram w).code) := by + have hcall := Refines.call (exchangeCertificate w) v + (pre := entry w) (prep := firstPrep v) (setup := fun _ _ => 1) (body := fun _ _ => 3) + (fun _ _ h => ⟨h, hv⟩) (by intro s x _ - have he : (implementation w 2).prepare .size x = (x.set tag 2).set arg 0 := by + have he : (exchangeProgram w).prepare v x = x.set 1 v := by ext i - · fin_cases i <;> simp +decide [implementation, DynamicProgram.prepare, Data.set, tag, arg] + · fin_cases i <;> simp [exchangeProgram, DynamicProgram.prepare, Data.set] · rfl rw [he] - exact (Exec.seq (Exec.const h2) (Exec.const (fits_zero w))).within) + exact (Exec.const hv).within) (by - intro s x hx y n he - obtain ⟨z, hz, _, _, _⟩ := DataStructure.Stack.WordRAM.execute h2 h4 hx.1 .size trivial + intro s x _ y n he + obtain ⟨z, hz⟩ := exchange_exec w ((exchangeProgram w).prepare v x) obtain ⟨_, rfl⟩ := he.unique hz exact le_rfl) - have hs : (spec w 2).Hoare (fun s => s.length < 2) - (.liftBind .size fun a => .pure a) (fun a s => a = s.length ∧ s.length < 2) := - DataStructure.StateTransitionSpec.Hoare.call_ofFn _ _ _ (fun _ h => ⟨trivial, rfl, h⟩) - exact (hcall.hoare hs (fun _ _ h => h.2)).consequence - (fun _ _ h => h) - (fun a s x h => ⟨⟨h.1.1, h.2.2⟩, Option.some.inj h.1.2, h.2.1⟩) + exact hcall.consequence (fun _ _ h => h) + (fun a s x h => ⟨h.1, Option.some.inj (h.2 : some (x.regs 2) = some a)⟩) (fun _ _ _ => le_rfl) -private theorem push_refines {w a : ℕ} (h4 : Fits w 4) : - Refines w (mid w a) (D := spec w 2) (.liftBind (.push a) fun b => .pure b) - (fun _ s y => Rep w 2 2 s y) (fun _ _ => 9) (.seq pushPrep program) := by - have h2 : Fits w 2 := h4.of_le (by decide) - have hv : ∀ s x, mid w a s x → (spec w 2).valid s (.push a) := by - intro s x hx - exact ⟨hx.1.2, hx.2.1 ▸ hx.1.1.bounded.1 out⟩ - have hcall := Refines.call (certificate h2 h4) (.push a) - (pre := mid w a) (prep := pushPrep) (setup := fun _ _ => 2) (body := fun _ _ => 7) - (fun s x h => ⟨h.1.1, hv s x h⟩) +private theorem second_refines {w a : ℕ} : + Refines w (mid w a) (D := exchangeSpec w) (.liftBind a fun b => .pure b) + (mid w) (fun _ _ => 4) (.seq secondPrep (exchangeProgram w).code) := by + have hv : ∀ s x, mid w a s x → (exchangeSpec w).valid s a := fun s x hx => + hx.2 ▸ hx.1.1.1 2 + have hcall := Refines.call (exchangeCertificate w) a + (pre := mid w a) (prep := secondPrep) (setup := fun _ _ => 1) (body := fun _ _ => 3) + (fun s x h => ⟨h.1, hv s x h⟩) (by intro s x hx - have he : (implementation w 2).prepare (.push a) x = - (x.set arg (x.regs out)).set tag 0 := by + have he : (exchangeProgram w).prepare a x = x.set 1 (x.regs 2) := by ext i - · fin_cases i <;> simp_all +decide [implementation, DynamicProgram.prepare, - Data.set, tag, arg, mid] + · fin_cases i <;> simp_all [exchangeProgram, DynamicProgram.prepare, Data.set, mid] · rfl rw [he] - exact (Exec.seq Exec.copy (Exec.const (fits_zero w))).within) + exact Exec.copy.within) (by - intro s x hx y n he - obtain ⟨z, hz, _, _, _⟩ := - DataStructure.Stack.WordRAM.execute h2 h4 hx.1.1 (.push a) (hv s x hx) + intro s x _ y n he + obtain ⟨z, hz⟩ := exchange_exec w ((exchangeProgram w).prepare a x) obtain ⟨_, rfl⟩ := he.unique hz exact le_rfl) - exact hcall.consequence (fun _ _ h => h) (fun _ _ _ h => h.1) + exact hcall.consequence (fun _ _ h => h) + (fun b s x h => ⟨h.1, Option.some.inj (h.2 : some (x.regs 2) = some b)⟩) (fun _ _ _ => le_rfl) -/-- The second operation's argument is the first observation, with fixed code for every answer. -/ -private theorem adaptive_refines {w : ℕ} (h4 : Fits w 4) : - Refines w (entry w) (D := spec w 2) - (((.liftBind .size fun a => .pure a) : (spec w 2).Client ℕ).bind fun a => - .liftBind (.push a) fun b => .pure b) - (fun _ s y => Rep w 2 2 s y) (fun _ _ => 18) - (.seq (.seq sizePrep program) (.seq pushPrep program)) := - Refines.bind_ofFn (size_refines h4) (fun _ => push_refines h4) (fun _ _ _ _ _ _ _ => le_rfl) +/-- Exchange `v` in, then put the old contents back: the second argument is the first answer, with +fixed code for every answer. -/ +private def swapBack (w v : ℕ) : (exchangeSpec w).Client ℕ := + ((.liftBind v fun a => .pure a) : (exchangeSpec w).Client ℕ).bind fun a => + .liftBind a fun b => .pure b + +private def swapCode (v : ℕ) : Code .standard 3 := + .seq (.seq (firstPrep v) (exchangeProgram 0).code) (.seq secondPrep (exchangeProgram 0).code) + +/-- The second operation's argument is the first observation. -/ +private theorem adaptive_refines {w v : ℕ} (hv : Fits w v) : + Refines w (entry w) (swapBack w v) (mid w) (fun _ _ => 8) (swapCode v) := + Refines.bind_ofFn (first_refines hv) (fun _ => second_refines) (fun _ _ _ _ _ _ _ => le_rfl) /-- Initialization is an actual register write, followed by the composed adaptive certificate. -/ -private theorem initialized_adaptive : - ∃ y, ExecWithin 3 (.seq (.op (.const ptr 2)) - (.seq (.seq sizePrep program) (.seq pushPrep program))) (Data.init []) y 19 ∧ - Rep 3 2 2 [0] y := by - have h4 : Fits 3 4 := by decide - have hinit : ∀ s x, s = ([] : List ℕ) ∧ x = (Data.init [] : Data 9) → - ∃ y, ExecWithin (B := .standard) 3 (.op (.const ptr 2)) x y 1 ∧ entry 3 s y := by - rintro s x ⟨rfl, rfl⟩ - refine ⟨initial 2, ?_, initial_rep h4, by decide⟩ - have hi : (Data.init ([] : List ℕ) : Data 9).set ptr 2 = initial 2 := by - ext i <;> simp [Data.init, Data.set, initial, Function.update_apply] - simpa only [hi] using (Exec.const (B := Basis.standard) (x := Data.init []) - (d := ptr) (show Fits 3 2 by decide)).within - have h := Refines.prepend (budget' := fun _ _ => 19) hinit (adaptive_refines h4) +@[program_certificate] +private theorem initialized_refines {w v : ℕ} (hv : Fits w v) : + Refines w (D := exchangeSpec w) (fun s x => s = 0 ∧ x = Data.init []) (swapBack w v) (mid w) + (fun _ _ => 9) + (.seq (.op (.const 2 0)) (swapCode v)) := by + refine Refines.prepend (setup := fun _ _ => 1) ?_ (adaptive_refines hv) (fun _ _ _ _ _ => le_rfl) - obtain ⟨_, y, he, hr⟩ := h.toOfFn ⟨rfl, rfl⟩ - exact ⟨y, he, hr⟩ - -/-- A different observation drives the same continuation code to push one rather than zero. -/ -private theorem populated_adaptive : - ∃ x y, Rep 3 2 2 [5] x ∧ - ExecWithin 3 (.seq (.seq sizePrep program) (.seq pushPrep program)) x y 18 ∧ - Rep 3 2 2 [1, 5] y := by - let x := ((initial 2).set len 1).write 2 5 - have h4 : Fits 3 4 := by decide - have hr : Rep 3 2 2 [5] x := by - refine ⟨((initial_rep (capacity := 2) h4).bounded.set (show Fits 3 1 by decide)).write - (show Fits 3 5 by decide), ?_, ?_, by decide, ?_⟩ - · simp +decide [x, Data.set, Data.write, initial, len, ptr] - · simp [x, Data.set, Data.write] - · intro i - fin_cases i - simp [x, Data.set, Data.write] - obtain ⟨_, y, he, hy⟩ := (adaptive_refines h4).toOfFn ⟨hr, by decide⟩ - exact ⟨x, y, hr, he, hy⟩ + rintro s x ⟨rfl, rfl⟩ + refine ⟨(Data.init [] : Data 3).set 2 0, (Exec.const (fits_zero w)).within, ?_⟩ + exact ⟨((Data.Bounded.init (by simp [InputFits, fits_zero])).set (fits_zero w)), rfl⟩ + +/-- The same continuation code restores a different observed cell. -/ +example : ∃ y, ExecWithin 3 (swapCode 6) ⟨fun _ => 0, fun _ => 5⟩ y 8 ∧ + y.mem 0 = 5 ∧ y.regs 2 = 6 := by + have hr : entry 3 5 ⟨fun _ => 0, fun _ => 5⟩ := + ⟨⟨fun _ => fits_zero 3, fun _ => show Fits 3 5 by decide⟩, rfl⟩ + obtain ⟨_, y, he, ⟨hent, hy⟩⟩ := + (adaptive_refines (w := 3) (show Fits 3 6 by decide)).toOfFn hr + exact ⟨y, he, hent.2, hy⟩ private theorem branch_refines {w n : ℕ} (h2 : Fits w 2) : - Refines (D := spec w 2) (B := .standard) w - (fun s x => s = [] ∧ x.regs out = n ∧ x.regs len = 0) + Refines (D := exchangeSpec w) (B := .standard) w + (fun s (x : Data 3) => s = 0 ∧ x.regs 2 = n ∧ x.regs 0 = 0) (if n = 0 then .pure 1 else .pure 2) - (fun (a : ℕ) _ y => y.regs out = a) (fun _ _ => 3) - (.branch (.eq out len) (.op (.const out 1)) (.op (.const out 2))) := by + (fun (a : ℕ) _ y => y.regs 2 = a) (fun _ _ => 3) + (.branch (.eq 2 0) (.op (.const 2 1)) (.op (.const 2 2))) := by apply Refines.branch (n = 0) · intro s x hx - change x.regs out = x.regs len ↔ n = 0 + change x.regs 2 = x.regs 0 ↔ n = 0 rw [hx.2.1, hx.2.2] · apply Refines.pure intro s x _ - exact ⟨x.set out 1, (Exec.const (h2.of_le (by decide))).within, by simp [Data.set]⟩ + exact ⟨x.set 2 1, (Exec.const (h2.of_le (by decide))).within, by simp [Data.set]⟩ · apply Refines.pure intro s x _ - exact ⟨x.set out 2, (Exec.const h2).within, by simp [Data.set]⟩ + exact ⟨x.set 2 2, (Exec.const h2).within, by simp [Data.set]⟩ + +private theorem swap_executes : + Executes 2 ((exchangeProgram 2).code.program (2 : Fin 3)) (State.init [0]) 1 4 := by + obtain ⟨y, he⟩ := exchange_exec 2 (Data.init [0]) + have hy : y.regs 2 = 1 := by + obtain ⟨rfl, -⟩ := he.unique ((Exec.const (fits_zero 2)).seq (Exec.load.seq Exec.store)) + simp [Data.set, Data.write, Data.init] + simpa only [hy, State.init] using he.executes 2 example : (∃ y, ExecWithin (B := .standard) 2 - (.branch (.eq out len) (.op (.const out 1)) (.op (.const out 2))) - (Data.init []) y 3 ∧ y.regs out = 1) ∧ + (.branch (.eq 2 0) (.op (.const 2 1)) (.op (.const 2 2))) + (Data.init [] : Data 3) y 3 ∧ y.regs 2 = 1) ∧ (∃ y, ExecWithin (B := .standard) 2 - (.branch (.eq out len) (.op (.const out 1)) (.op (.const out 2))) - ((Data.init []).set out 1) y 3 ∧ y.regs out = 2) ∧ - Executes 2 (Controller.code.program out) (State.init [0]) 1 49 ∧ - Executes 2 (Controller.code.program out) (State.init [3]) 3 29 := by + (.branch (.eq 2 0) (.op (.const 2 1)) (.op (.const 2 2))) + ((Data.init [] : Data 3).set 2 1) y 3 ∧ y.regs 2 = 2) ∧ + Executes 2 ((exchangeProgram 2).code.program (2 : Fin 3)) (State.init [0]) 1 4 := by obtain ⟨_, y₀, he₀, hr₀⟩ := (branch_refines (n := 0) (show Fits 2 2 by decide)).toOfFn (x := Data.init []) ⟨rfl, rfl, rfl⟩ obtain ⟨_, y₁, he₁, hr₁⟩ := (branch_refines (n := 1) (show Fits 2 2 by decide)).toOfFn - (x := (Data.init []).set out 1) ⟨rfl, by simp [Data.set], by decide⟩ - exact ⟨⟨y₀, he₀, hr₀⟩, ⟨y₁, he₁, hr₁⟩, - Controller.executes (by decide) (by decide), - Controller.executes (by decide) (by decide)⟩ + (x := (Data.init []).set 2 1) ⟨rfl, by simp [Data.set], by decide⟩ + exact ⟨⟨y₀, he₀, hr₀⟩, ⟨y₁, he₁, hr₁⟩, swap_executes⟩ -/-- The actual zero-input execution refutes a forty-eight-step whole-program budget. -/ +/-- The actual execution refutes a three-step whole-program budget. -/ theorem rejects_short_budget : - ¬∃ t ≤ 48, Executes 2 (Controller.code.program out) (State.init [0]) 1 t := by + ¬∃ t ≤ 3, Executes 2 ((exchangeProgram 2).code.program (2 : Fin 3)) (State.init [0]) 1 t := by rintro ⟨t, ht, he⟩ - have hx := Controller.executes (w := 2) (n := 0) (by decide) (by decide) - have hn : t = 49 := (he.unique hx).2 + have hn : t = 4 := (he.unique swap_executes).2 omega -- A second matching certificate must not hide the usable one or force its false premise. @[program_certificate] -private theorem unusable_controller {w n : ℕ} (impossible : False) : - Refines w (fun s x => s = [] ∧ x = Data.init [n]) (Controller.client w n) - (fun a s y => Rep w 2 1 s y ∧ y.regs out = a) - (fun _ _ => if n = 0 then 48 else 28) Controller.code := impossible.elim - -example {w n : ℕ} (h3 : Fits w 3) (hn : InputFits w [n]) : - Refines w (fun s x => s = [] ∧ x = Data.init [n]) (Controller.client w n) - (fun a s y => Rep w 2 1 s y ∧ y.regs out = a) - (fun _ _ => if n = 0 then 48 else 28) Controller.code := by +private theorem unusable_refines {w v : ℕ} (impossible : False) : + Refines w (D := exchangeSpec w) (fun s x => s = 0 ∧ x = Data.init []) (swapBack w v) (mid w) + (fun _ _ => 9) + (.seq (.op (.const 2 0)) (swapCode v)) := impossible.elim + +example {w v : ℕ} (hv : Fits w v) : + Refines w (D := exchangeSpec w) (fun s x => s = 0 ∧ x = Data.init []) (swapBack w v) (mid w) + (fun _ _ => 9) + (.seq (.op (.const 2 0)) (swapCode v)) := by stateful @[program_certificate] private theorem nonuniform_positive_polytime : - ComputableInPolyTime .standard Controller.positive (fun _ n => [n]) id := - Controller.positive_polytime.computableInPolyTime + ComputableInPolyTime .standard DataStructure.Stack.Controller.positive (fun _ n => [n]) id := + DataStructure.Stack.Controller.positive_polytime.computableInPolyTime attribute [local fun_prop] nonuniform_positive_polytime example : - UniformComputableInPolyTime .standard Controller.positive (fun _ n => [n]) id ∧ - UniformComputableInPolyTime .standard Controller.positive (fun _ n => [n]) id ∧ - ComputableInPolyTime .standard Controller.positive (fun _ n => [n]) id ∧ - ComputableInPolyTime .standard Controller.positive (fun _ n => [n]) id := + UniformComputableInPolyTime .standard DataStructure.Stack.Controller.positive + (fun _ n => [n]) id ∧ + UniformComputableInPolyTime .standard DataStructure.Stack.Controller.positive + (fun _ n => [n]) id ∧ + ComputableInPolyTime .standard DataStructure.Stack.Controller.positive (fun _ n => [n]) id ∧ + ComputableInPolyTime .standard DataStructure.Stack.Controller.positive (fun _ n => [n]) id := ⟨by polytime, by fun_prop, by polytime, by fun_prop⟩ /-- The family puts its width in the instruction's literal, rather than reading it at runtime. -/ diff --git a/ComplexityTests/WordRAMDynamic.lean b/ComplexityTests/WordRAMDynamic.lean index 80735658d3..b58cba7631 100644 --- a/ComplexityTests/WordRAMDynamic.lean +++ b/ComplexityTests/WordRAMDynamic.lean @@ -10,7 +10,6 @@ module public import Complexity.Computability.WordRAM.Dynamic public import Complexity.Computability.WordRAM.Subroutine public import Complexity.DataStructure.WordRAM.StateTransition -public import Complexity.DataStructure.Stack.WordRAM.Basic /-! Nonvacuous fixed-program clients: a persistent exchange cell and a read-only array. Both obtain arbitrary-history theorems from the same certificate interface. -/ @@ -110,41 +109,11 @@ example (w : ℕ) (x : Data 2) (h0 : x.regs 0 = 0) (h1 : x.mem 1 = 7) : (TraceExec.op (o := .store 0 1) (w := w) (x := x) rfl) exact he.frame h1 (by simp [HeapAssertion.pointsto, Probe.writes]) -/-- An adaptive client chooses whether to pop from the observed size. -/ -def stackClient : (Stack.WordRAM.spec 4 2).Client (Option ℕ × ℕ) := - .liftBind (.push 7) fun _ => - .liftBind .size fun (n : ℕ) => - if n = 1 then - .liftBind .pop fun v => .liftBind .size fun (m : ℕ) => .pure (v, m) - else .pure (none, n) - -/-- The test client is admissible from the concrete empty representation. -/ -example : (Stack.WordRAM.spec 4 2).ValidHistory [] - ((Stack.WordRAM.spec 4 2).runClient [] stackClient).2.2 := - by - change (0 < 2 ∧ Fits 4 7) ∧ True ∧ True ∧ True ∧ True - decide - -/-- The concrete stack realizes an adaptive observation with a cumulative native cost bound. -/ -example : ∃ y ts, - DataStructure.Executes ((Stack.WordRAM.implementation 4 2).ExecOp 4) stackClient - (Stack.WordRAM.initial 3) (some 7, 0) y ts ∧ Stack.WordRAM.Rep 4 3 2 [] y ∧ - DataStructure.total DynamicProgram.OperationTrace.instructions ts ≤ 48 := by - exact Stack.WordRAM.run_client (base := 3) (by decide : Fits 4 2) (by decide : Fits 4 (3 + 2)) - stackClient (Stack.WordRAM.initial_rep (by decide : Fits 4 (3 + 2))) (by - change (0 < 2 ∧ Fits 4 7) ∧ True ∧ True ∧ True ∧ True - decide) - -/-- Typed push, empty pop, and size queries on `Stack.WordRAM.implementation 4 2`, plus decoder -rejection and loop-fuel exhaustion on `exchangeProgram 4`. -/ +/-- A typed exchange that writes and returns the old cell, plus decoder rejection and loop-fuel +exhaustion on `exchangeProgram 4`. -/ example : - ((Stack.WordRAM.implementation 4 2).eval 4 0 (.push 7) (Stack.WordRAM.initial 3)).map - (fun (y, answer, cost) => (y.mem 3, y.regs Stack.WordRAM.len, answer, cost)) = - some (7, 1, (), 8) ∧ - ((Stack.WordRAM.implementation 4 2).eval 4 0 .pop (Stack.WordRAM.initial 3)).map - (fun (_, answer, _) => answer) = some none ∧ - ((Stack.WordRAM.implementation 4 2).eval 4 0 .size (Stack.WordRAM.initial 3)).map - (fun (_, answer, _) => answer) = some 0 ∧ + ((exchangeProgram 4).eval 4 0 7 ⟨fun _ => 0, fun _ => 5⟩).map + (fun (y, answer, cost) => (y.mem 0, answer, cost)) = some (7, 5, 4) ∧ ({ exchangeProgram 4 with decode := fun _ _ => none } : DynamicProgram (exchangeSpec 4) 3 .standard).eval 4 0 7 ⟨fun _ => 0, fun _ => 0⟩ = none ∧ @@ -154,18 +123,10 @@ example : decide /-- Successful evaluation of a represented, admissible call supplies native execution evidence. -/ -example {y : Data 9} {cost : ℕ} - (he : (Stack.WordRAM.implementation 4 2).eval 4 0 (.push 7) (Stack.WordRAM.initial 3) = - some (y, (), cost)) : - ∃ probes, (Stack.WordRAM.implementation 4 2).ExecOp 4 (Stack.WordRAM.initial 3) (.push 7) () y - ⟨cost, probes⟩ := - (Stack.WordRAM.certificate (w := 4) (capacity := 2) (base := 3) (by decide) - (by decide)).execOp_of_eval - (Stack.WordRAM.initial_rep (by decide)) (by decide) he - -/-- A full stack rejects another push, while the largest fitting word remains a valid value. -/ -example : ¬(Stack.WordRAM.spec 4 2).valid [4, 7] (.push 2) ∧ - (Stack.WordRAM.spec 4 2).valid [] (.push 15) := by decide +example {y : Data 3} {cost : ℕ} + (he : (exchangeProgram 4).eval 4 0 7 ⟨fun _ => 0, fun _ => 0⟩ = some (y, 0, cost)) : + ∃ probes, (exchangeProgram 4).ExecOp 4 ⟨fun _ => 0, fun _ => 0⟩ 7 0 y ⟨cost, probes⟩ := + (exchangeCertificate 4).execOp_of_eval (exchange_initial 4) (by decide) he /-- info: 'Cslib.WordRAM.DynamicCertificate.impl' depends on axioms: diff --git a/ROADMAP.md b/ROADMAP.md index 0db31f5c09..38716b62d9 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -642,7 +642,9 @@ reusing graph and matroid foundations and the shared data-structure interfaces. 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. 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: + have uniform native certificates over runtime layouts, with local work 0/10/19. The stack is + migrated: `Stack.Native` and `Stack.Controller` replace its hand-stepped dispatcher, which + remains only as the stepped component of Chan–He traversal (`Traversal.WordStack`). 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 @@ -654,7 +656,7 @@ reusing graph and matroid foundations and the shared data-structure interfaces. predecessor for van Emde Boas sets; and deallocation, shrinking and compiled client controllers for the doubling array. - **A typed-call evaluator** for fixed-program Word-RAM clients, returning the typed observation, - final state and halt-inclusive instruction cost, so the prefix-sum and stack clients no longer + final state and halt-inclusive instruction cost, so the prefix-sum clients no longer assemble preparation, evaluation and halt accounting by hand. - **Priority-queue and shortest-path consolidation** ([#949]): lift operation-count helpers (`numQueries`, `numInserts`, `numDecreaseKeys`, `numExtractMins`) to `IndexedMinPQ`, parameterize diff --git a/UPSTREAM.md b/UPSTREAM.md index c7761ca654..f628392e39 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -506,8 +506,7 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he arbitrary pure continuations do not have machine-time certificates. `CostHoare` adds client operation-cost contracts and separate method-budget composition. The `stateful` Aesop rules support these contracts. `WordRAM/Controller` adds fixed-code client refinements with charged - preparation, control flow and final halt; the adaptive stack example constructs its empty - representation from the standard input. `WordRAM/Function` gives separate nonuniform and + preparation, control flow and final halt. `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 @@ -527,6 +526,10 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he 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. + `Stack.Native` runs those three certificates as a native stack with one constant + per-operation bound, and `Stack.Controller` compiles an adaptive stack client to one flat + program through `realize`; the hand-stepped stack dispatcher survives only inside Chan–He + traversal (`Traversal.WordStack`), which steps it with exact counts. 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 0a8975c8c4826b62be577c296ec5d440782f3fdd Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 22:31:56 -0500 Subject: [PATCH 24/40] =?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 25/40] 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 26/40] 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 27/40] 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 28/40] 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) : From c9f63024b02e656436e072c5b744d36ae895868c Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 22:58:44 -0500 Subject: [PATCH 29/40] refactor(Stack): run the native stack on Realization.Backend Adopts worker-claude-015dlg8k-adapter's drop-in for #2277: Stack.Native instantiates Realization.Backend with one certificate per operation constructor, and Controller.positive_polytime comes from Realizes.uniformComputableInPolyTime. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../DataStructure/Stack/Controller.lean | 58 +-- Complexity/DataStructure/Stack/Native.lean | 443 +++++------------- ComplexityTests/DataStructureStack.lean | 9 +- .../WordRAMRealizationBackend.lean | 20 +- 4 files changed, 142 insertions(+), 388 deletions(-) diff --git a/Complexity/DataStructure/Stack/Controller.lean b/Complexity/DataStructure/Stack/Controller.lean index c1c7fbc70e..ad3c4a5656 100644 --- a/Complexity/DataStructure/Stack/Controller.lean +++ b/Complexity/DataStructure/Stack/Controller.lean @@ -10,7 +10,8 @@ module public import Complexity.Computability.WordRAM.Function public import Complexity.Computability.WordRAM.MapOp public import Complexity.Computability.WordRAM.Realization.Client.MemoryStack -public import Complexity.Computability.WordRAM.Realization.Program +public import Complexity.Computability.WordRAM.Realization.Backend +public import Complexity.Computability.WordRAM.Realization.Function import Complexity.Tactic.Realize import Complexity.Tactic.WordRAM @@ -153,14 +154,6 @@ private theorem length_clientHistory_client (w n : ℕ) : by_cases hn0 : n = 0 <;> simp [StateTransitionSpec.clientHistory, client, -PFunctor.FreeM.pure_eq_pure, hn0] -/-- Memory charges along a history do not depend on the word width. -/ -private theorem totalCost_width (w : ℕ) (s : Memory.State) (os : List Memory.Op) : - (Memory.spec 0).totalCost Memory.charge s os = - (Memory.spec w).totalCost Memory.charge s os := by - induction os generalizing s with - | nil => rfl - | cons o os ih => exact congrArg (Memory.charge s o + ·) (ih _) - /-- **Correctness through the refinement.** From a memory whose frontier leaves room, `program` returns `positive n`, its memory history is admissible, and it costs at most fifteen memory units: two for the allocation and three for each of at most four stack calls. -/ @@ -193,7 +186,7 @@ public theorem program_spec {w n : ℕ} {s : Memory.State} (hf : Fits w (s.front have hlen := length_clientHistory_client w n unfold memoryCost simp only [StateTransitionSpec.clientHistory, program] - rw [hrun0, ← runClient_width w, totalCost_width w] + rw [hrun0, ← runClient_width w, ← totalCost_width w] change 2 + (1 + (Memory.spec w).totalCost Memory.charge ⟨clear s.mem s.frontier 1, s.frontier + 1 + 1⟩ ((Memory.spec w).clientHistory ⟨clear s.mem s.frontier 1, s.frontier + 1 + 1⟩ (run L n))) ≤ 15 @@ -203,43 +196,12 @@ public theorem program_spec {w n : ℕ} {s : Memory.State} (hf : Fits w (s.front every width of at least three bits per input bit. -/ @[fun_prop, program_certificate] public theorem positive_polytime : - UniformComputableInPolyTime .standard positive (fun _ n => [n]) id := by - obtain ⟨m, C, emit, he⟩ := realizes_program - let fr : Fin (m + 4) := ⟨0, by omega⟩ - let src : Fin 1 → Fin (m + 4) := fun _ => ⟨1, by omega⟩ - let tmp : Fin (1 + m + 1) → Fin (m + 4) := fun i => ⟨i.val + 2, by omega⟩ - have htmp : Function.Injective tmp := fun i j h => - Fin.ext (by have := congrArg Fin.val h; simp only [tmp] at this; omega) - have hd : ∀ i j, src i ≠ tmp j := fun i j h => by - have := congrArg Fin.val h; simp only [src, tmp] at this; omega - have hf : ∀ j, tmp j ≠ fr := fun j h => by - have := congrArg Fin.val h; simp only [tmp, fr] at this; omega - have hs : ∀ i, src i ≠ fr := fun i h => by - have := congrArg Fin.val h; simp only [src, fr] at this; omega - apply UniformComputableInPolyTime.of_code - ((Code.seq (inputPrefix fr (tmp (Fin.last (1 + m))) src) (emit fr src tmp)).mapSimulates - .exactStandard) (tmp (Fin.castAdd (m + 1) 0)) (.const (7 + C * 17)) 3 (by decide) - intro n w hw hin - have h3 : 3 ≤ w := by unfold logWidth at hw; simp only [id] at hw; omega - have h4 : Fits w 4 := (show Fits 3 4 by decide).mono h3 - have hn : Fits w n := hin.2 n (by simp) - obtain ⟨x, ex, hx, hm, hwords, -⟩ := inputPrefix_exec fr (tmp (Fin.last (1 + m))) src [n] - (fun _ _ _ => Subsingleton.elim _ _) (hf _) (fun i => hd i _) hs (by simp) - (h4.of_le (by simp)) hin - have harg : x.regs ∘ src = (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding n := by - funext i - simpa using hwords i - have hroom : Fits w ((inputState [n]).frontier + 2) := h4.of_le (by simp [inputState]) - obtain ⟨hres, hvalid, hcost⟩ := program_spec hroom hn - obtain ⟨y, ey, -, hout, -⟩ := (he fr src tmp htmp hd hf hs).2 w n x hx harg - (by rw [hm]; exact hroom) (by rw [hm]; exact hvalid) - refine ⟨y, ((ex.seq ey).mapSimulates _).mono ?_, ?_⟩ - · rw [hm] - beta_reduce - have := Nat.mul_le_mul_left C (show memoryCost (program n) (inputState [n]) + 1 + 1 ≤ 17 by - omega) - omega - · rw [hout 0, hm, hres] - rfl + UniformComputableInPolyTime .standard positive (fun _ n => [n]) id := + realizes_program.uniformComputableInPolyTime .exactStandard 0 (.const 16) 3 (by decide) + (fun _ _ j => by fin_cases j; rfl) fun n w hw hin => by + have h4 : Fits w 4 := (show Fits 3 4 by decide).mono (le_trans (by simp [logWidth]) hw) + have hroom : Fits w ((inputState [n]).frontier + 2) := h4.of_le (by simp [inputState]) + obtain ⟨hres, hvalid, hcost⟩ := program_spec hroom (hin.2 n (by simp)) + exact ⟨by simp, h4.of_le (by simp), hroom, hvalid, hres, by omega⟩ end Cslib.DataStructure.Stack.Controller diff --git a/Complexity/DataStructure/Stack/Native.lean b/Complexity/DataStructure/Stack/Native.lean index 589301d872..67e7e79255 100644 --- a/Complexity/DataStructure/Stack/Native.lean +++ b/Complexity/DataStructure/Stack/Native.lean @@ -8,41 +8,36 @@ module -- upstream: candidate public import Complexity.Computability.WordRAM.Heap +public import Complexity.Computability.WordRAM.Realization.Backend public import Complexity.Computability.WordRAM.Realization.Client.MemoryStack -public import Complexity.Computability.WordRAM.Realization.Witness -public import Complexity.DataStructure.StateTransition.Resource /-! # The array stack on the word RAM `Stack.Memory` implements the bounded stack `Stack.spec w C` by three memory programs. Here each -program runs as native code. The uniform realizations `MemoryStack.realizes_push`, -`realizes_pop` and `realizes_size` supply one static body per operation, chosen before every -width, capacity, layout and value; no instruction is stepped by hand. The operation's -constructor selects the body, and a push value is delivered to a register beforehand, uncharged, -as in a calling convention. The client between operations stays outside the machine; a whole -client compiles to one flat program when `realize` certifies it (`Stack.Controller`). - -The native data represents a stack when its registers hold the layout words and the frontier, -and its memory satisfies the invariant of `Stack.Memory` and abstracts to the stack. Correctness -is that of `Stack.Memory.refinement`, transported through the realizations' whole-memory -postcondition. Every operation runs within one constant number of instructions -(`instruction_bound`), so an adaptive client with `m` operations runs within `m` times that -constant (`run_client`). Each operation writes only the layout's cells (`modifies`, `frame`). +program runs as the static body of its uniform realization, `MemoryStack.realizes_push`, +`realizes_pop` or `realizes_size`, selected by the operation's constructor through +`Realization.Backend`; no instruction is stepped by hand. The layout and a push value are +installed in registers before the body, uncharged, as a calling convention. A push answers `()` +with no result words. + +Correctness is that of `Stack.Memory.refinement`. Its memory charges are constant +(`Stack.Memory.amortized`), so one constant, chosen before every width, capacity and layout, +bounds the instructions of every operation (`instruction_bound`), and an adaptive client with +`m` operations runs within `m` times that constant (`run_client`). Each operation writes only +the layout's cells (`modifies`, `frame`). ## Main definitions -* `registers`, `frontier`, `layoutReg`, `arg`: the register file. -* `body`, `prepare`, `result`, `exec`: the native body of each operation, argument delivery, - the decoded answer, and the execution relation. -* `Rep`: native data representing a stack. +* `Tag`, `certificates`, `backend`: the operation constructors, their realizations, and the + stack's operations on a layout. * `implementation`: the native stack. * `initialLayout`, `initial`: an empty stack at a fixed address, as a calling-convention state. ## Main results -* `instruction_bound`: a constant bound on the instructions of every operation. -* `run_client`: adaptive clients, with a bound linear in the number of operations. +* `implements`: the backend runs the methods of `Stack.Memory`. +* `instruction_bound`, `run_client`: a constant bound per operation. * `modifies`, `frame`: operations write only the layout's cells. * `initial_rep`: the initial state represents the empty stack. @@ -57,336 +52,130 @@ namespace Cslib.DataStructure.Stack.Native open Cslib.WordRAM Cslib.WordRAM.Realization Cslib.WordRAM.Realization.MemoryStack Cslib.DataStructure.Queue -private noncomputable def slots : ℕ := - (1 + realizes_size.scratchSize + 1) + (0 + realizes_push.scratchSize + 1) + - (2 + realizes_pop.scratchSize + 1) - -/-- One register count, selected before every width, layout, operation and value. -/ -public noncomputable def registers : ℕ := slots + 6 - -/-- Register zero holds the allocation frontier. -/ -public noncomputable def frontier : Fin registers := ⟨0, by unfold registers; omega⟩ - -/-- Registers one to four hold the layout words. -/ -public noncomputable def layoutReg (i : Fin 4) : Fin registers := - ⟨i.val + 1, by unfold registers; omega⟩ - -/-- Register five receives a push value. -/ -public noncomputable def arg : Fin registers := ⟨5, by unfold registers; omega⟩ - -private noncomputable def pushSrc (i : Fin 5) : Fin registers := - ⟨i.val + 1, by unfold registers; omega⟩ - -private noncomputable def tmp {n : ℕ} (hn : n ≤ slots) (i : Fin n) : Fin registers := - ⟨i.val + 6, by have := i.isLt; unfold registers; omega⟩ - -private theorem sizeSlots : 1 + realizes_size.scratchSize + 1 ≤ slots := by unfold slots; omega -private theorem pushSlots : 0 + realizes_push.scratchSize + 1 ≤ slots := by unfold slots; omega -private theorem popSlots : 2 + realizes_pop.scratchSize + 1 ≤ slots := by unfold slots; omega - -private theorem layout {k n : ℕ} (src : Fin k → Fin registers) - (hsrc : ∀ i, 1 ≤ (src i).val ∧ (src i).val < 6) (hn : n ≤ slots) : - Function.Injective (tmp hn) ∧ (∀ i j, src i ≠ tmp hn j) ∧ - (∀ j, tmp hn j ≠ frontier) ∧ (∀ i, src i ≠ frontier) := by - refine ⟨fun i j h => Fin.ext (by have := congrArg Fin.val h; simp only [tmp] at this; omega), - fun i j h => ?_, fun j h => ?_, fun i h => ?_⟩ - · have := congrArg Fin.val h - have := hsrc i - simp only [tmp] at * - omega - · have := congrArg Fin.val h - simp only [tmp, frontier] at this - omega - · have := congrArg Fin.val h - have := hsrc i - simp only [frontier] at * - omega - -/-- The static body of each operation; arguments and runtime layouts never select code. -/ -public noncomputable def body : StackOp ℕ → Code .exact registers - | .push _ => realizes_push.code frontier pushSrc (tmp pushSlots) - | .pop => realizes_pop.code frontier layoutReg (tmp popSlots) - | .size => realizes_size.code frontier layoutReg (tmp sizeSlots) - -/-- Deliver a push value to `arg`; the other operations take no argument. -/ -public noncomputable def prepare : StackOp ℕ → Data registers → Data registers - | .push v, x => x.set arg v - | .pop, x => x - | .size, x => x - -/-- The answer, read from the result registers: a presence tag and a payload for a pop, a word -for a size. Decoding executes no instruction. -/ -public noncomputable def result : (o : StackOp ℕ) → Data registers → (stack ℕ).Result o - | .push _, _ => () - | .pop, y => - if y.regs (tmp popSlots (Fin.castAdd (realizes_pop.scratchSize + 1) (0 : Fin 2))) = 0 then none - else some (y.regs (tmp popSlots (Fin.castAdd (realizes_pop.scratchSize + 1) (1 : Fin 2)))) - | .size, y => y.regs (tmp sizeSlots (Fin.castAdd (realizes_size.scratchSize + 1) (0 : Fin 1))) - -/-- An operation executes its body from the prepared data, in the counted number of -instructions, and returns the decoded answer. -/ -public noncomputable def exec (w C : ℕ) : Execution (Stack.spec w C) (Data registers) ℕ := - fun x o a y n => Exec w (body o) (prepare o x) y n ∧ a = result o y - -/-- Native data represents the stack `vs` on layout `L`: the layout is well formed, its words -sit in `layoutReg`, all words are bounded, and the memory with the frontier register satisfies -the invariant of `Stack.Memory` and abstracts to `vs`. -/ -public structure Rep (w C : ℕ) (L : Stack.Memory.Layout) (vs : List ℕ) (x : Data registers) : - Prop where - /-- The layout is well formed for capacity `C`. -/ - valid : L.Valid w C - /-- The layout words sit in their registers. -/ - layout : ∀ i, x.regs (layoutReg i) = layoutWords L i - /-- Every register and cell holds a word. -/ - bounded : x.Bounded w - /-- The memory satisfies the invariant. -/ - inv : Stack.Memory.Inv L (memoryState frontier x) - /-- The memory holds `vs`. -/ - abs_eq : Stack.Memory.abs L (memoryState frontier x) = vs - -private theorem step_methods {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C) - {s : Memory.State} (hs : Stack.Memory.Inv L s) {o : StackOp ℕ} - (hv : (Stack.spec w C).valid (Stack.Memory.abs L s) o) : - (Memory.spec w).ValidHistory s - ((Memory.spec w).clientHistory s (Stack.Memory.methods L w C o)) ∧ - (Stack.spec w C).stepFn (Stack.Memory.abs L s) o = - (Stack.Memory.abs L ((Memory.spec w).clientState s (Stack.Memory.methods L w C o)), - (Memory.spec w).clientResult s (Stack.Memory.methods L w C o)) ∧ - Stack.Memory.Inv L ((Memory.spec w).clientState s (Stack.Memory.methods L w C o)) := by - have hvalid := ((Memory.spec w).demonicValid_iff_validHistory_runClient _ _).mp - ((Stack.Memory.refinement hL).valid o hs hv) - obtain ⟨hstep, hinv⟩ := (Stack.Memory.refinement hL).correct o hs hv - ((Memory.spec w).clientRun_canonical s _ hvalid) - exact ⟨hvalid, (((Stack.spec w C).step_iff_stepFn _ _ _ _).mp hstep).symm, hinv⟩ - -private theorem memoryCost_methods_le (L : Stack.Memory.Layout) (w C : ℕ) (o : StackOp ℕ) - (s : Memory.State) : memoryCost (Stack.Memory.methods L w C o) s ≤ 3 := by - cases o with - | push v => exact le_of_eq rfl - | pop => - by_cases h : s.mem L.count.base = 0 - · simp [memoryCost, StateTransitionSpec.clientHistory, Stack.Memory.methods, - Stack.Memory.runClient_pop_of_eq_zero, h, StateTransitionSpec.totalCost, Memory.charge] - · simp [memoryCost, StateTransitionSpec.clientHistory, Stack.Memory.methods, - Stack.Memory.runClient_pop_of_pos, h, StateTransitionSpec.totalCost, Memory.charge] - | size => exact Nat.le_succ_of_le (Nat.le_succ_of_le le_rfl) - -private noncomputable def factor : ℕ := - max (max realizes_size.factor realizes_push.factor) realizes_pop.factor - -private theorem optionWords_decode (a : Option ℕ) : - (if optionWords a 0 = 0 then none else some (optionWords a 1)) = a := by - cases a <;> simp - -private theorem layoutReg_ne_tmp {n : ℕ} (hn : n ≤ slots) (i : Fin 4) (j : Fin n) : - tmp hn j ≠ layoutReg i := by - intro h - have := congrArg Fin.val h - simp only [tmp, layoutReg] at this - omega - -private theorem layoutReg_ne_frontier (i : Fin 4) : layoutReg i ≠ frontier := by - intro h - have := congrArg Fin.val h - simp only [layoutReg, frontier] at this - omega - -private theorem layoutReg_ne_arg (i : Fin 4) : layoutReg i ≠ arg := by - intro h - have := congrArg Fin.val h - simp only [layoutReg, arg] at this - have := i.isLt - omega - -private theorem prepare_memoryState (o : StackOp ℕ) (x : Data registers) : - memoryState frontier (prepare o x) = memoryState frontier x := by - cases o with - | push v => - have h : arg ≠ frontier := by - intro h - have := congrArg Fin.val h - simp only [arg, frontier] at this - omega - exact Memory.State.ext rfl (Function.update_of_ne h.symm v x.regs) - | pop | size => rfl - -private theorem prepare_layout (o : StackOp ℕ) (x : Data registers) (i : Fin 4) : - (prepare o x).regs (layoutReg i) = x.regs (layoutReg i) := by - cases o with - | push v => exact Function.update_of_ne (layoutReg_ne_arg i) _ _ - | pop | size => rfl - -/-- One operation's body, from represented data: its instruction count, memory, answer and -preserved layout registers. -/ -private theorem exists_body {w C : ℕ} {L : Stack.Memory.Layout} {vs : List ℕ} - {x : Data registers} (hr : Rep w C L vs x) (o : StackOp ℕ) - (hv : (Stack.spec w C).valid vs o) : - ∃ y n, Exec w (body o) (prepare o x) y n ∧ n ≤ factor * 23 ∧ - memoryState frontier y = (Memory.spec w).clientState (memoryState frontier x) - (Stack.Memory.methods L w C o) ∧ - result o y = (Memory.spec w).clientResult (memoryState frontier x) - (Stack.Memory.methods L w C o) ∧ - y.Bounded w ∧ ∀ i, y.regs (layoutReg i) = x.regs (layoutReg i) := by - have hL := hr.valid - have hC := hL.items_length - rw [← hr.abs_eq] at hv - have hvalid := (step_methods hL hr.inv hv).1 - have hcost := memoryCost_methods_le L w C o (memoryState frontier x) - have hregs : x.regs ∘ layoutReg = layoutWords L := funext hr.layout - cases o with - | push v => - obtain ⟨hlen, hfit⟩ := hv - rw [Stack.Memory.length_abs] at hlen - have hb : (prepare (.push v) x).Bounded w := hr.bounded.set hfit - have hp := prepare_memoryState (.push v) x - obtain ⟨hi, hd, hf, hs⟩ := layout pushSrc (fun i => by simp only [pushSrc]; omega) pushSlots - have hin : (prepare (.push v) x).regs ∘ pushSrc = pushWords (L, v) := by - funext i - fin_cases i - · exact (prepare_layout _ x 0).trans (hr.layout 0) - · exact (prepare_layout _ x 1).trans (hr.layout 1) - · exact (prepare_layout _ x 2).trans (hr.layout 2) - · exact (prepare_layout _ x 3).trans (hr.layout 3) - · exact Function.update_self arg v x.regs - obtain ⟨y, ⟨n, hn, he⟩, hm, _, hframe⟩ := - (realizes_push.code_spec frontier _ _ hi hd hf hs).2 w (L, v) _ hb hin - ⟨hC ▸ hL, hp ▸ hr.inv, by - change (memoryState frontier (prepare (.push v) x)).mem L.count.base < L.items.length - rw [hp]; omega⟩ (by rw [hp]; exact hvalid) - rw [hp] at hn hm - have hc : memoryCost (Stack.Memory.push (L, v).1 (L, v).2) (memoryState frontier x) ≤ 3 := - hcost - refine ⟨y, n, he, hn.trans ?_, hm, rfl, he.bounded hb, fun i => ?_⟩ - · exact Nat.mul_le_mul (by unfold factor; omega) (by omega) - · rw [hframe _ (layoutReg_ne_frontier i) (fun j => layoutReg_ne_tmp _ i j)] - exact prepare_layout _ x i - | pop => - obtain ⟨hi, hd, hf, hs⟩ := layout layoutReg (fun i => by simp only [layoutReg]; omega) - popSlots - obtain ⟨y, ⟨n, hn, he⟩, hm, hout, hframe⟩ := - (realizes_pop.code_spec frontier _ _ hi hd hf hs).2 w L x hr.bounded hregs - ⟨hC ▸ hL, hr.inv⟩ hvalid - have hc : memoryCost (Stack.Memory.pop L) (memoryState frontier x) ≤ 3 := hcost - refine ⟨y, n, he, hn.trans ?_, hm, ?_, he.bounded hr.bounded, fun i => ?_⟩ - · exact Nat.mul_le_mul (by unfold factor; omega) (by omega) - · simp only [result, hout] - exact optionWords_decode _ - · exact hframe _ (layoutReg_ne_frontier i) (fun j => layoutReg_ne_tmp _ i j) - | size => - obtain ⟨hi, hd, hf, hs⟩ := layout layoutReg (fun i => by simp only [layoutReg]; omega) - sizeSlots - obtain ⟨y, ⟨n, hn, he⟩, hm, hout, hframe⟩ := - (realizes_size.code_spec frontier _ _ hi hd hf hs).2 w L x hr.bounded hregs trivial - hvalid - have hc : memoryCost (Stack.Memory.size L) (memoryState frontier x) ≤ 3 := hcost - refine ⟨y, n, he, hn.trans ?_, hm, hout 0, he.bounded hr.bounded, fun i => ?_⟩ - · exact Nat.mul_le_mul (by unfold factor; omega) (by omega) - · exact hframe _ (layoutReg_ne_frontier i) (fun j => layoutReg_ne_tmp _ i j) +/-- The constructors of the stack's operations. -/ +public inductive Tag + | push + | pop + | size + deriving DecidableEq + +public instance : Fintype Tag := Fintype.ofList [.push, .pop, .size] fun t => by cases t <;> simp + +/-- One realization per constructor. -/ +public noncomputable def certificates : Tag → Certificate .exact + | .push => ⟨realizes_push⟩ + | .pop => ⟨realizes_pop⟩ + | .size => ⟨realizes_size⟩ + +/-- The stack's operations on layout `L`: every operation passes the layout, a push its value. -/ +public noncomputable def backend (w C : ℕ) (L : Stack.Memory.Layout) : + Backend (Stack.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 + +private 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 + +/-- The backend runs the methods of `Stack.Memory`. -/ +public 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 + 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⟩ /-- The native stack on layout `L`. -/ -public noncomputable def implementation (w C : ℕ) (L : Stack.Memory.Layout) : - StateTransitionImpl (Stack.spec w C) (exec w C) := - StateTransitionImpl.ofFn (Rep w C L) - (fun o hr hv => by - obtain ⟨y, n, he, _⟩ := exists_body hr o hv - exact ⟨result o y, y, n, he, rfl⟩) - (by - intro vs x o a y n hr hv he - obtain ⟨z, m, hz, _, hm, hres, hb, hl⟩ := exists_body hr o hv - obtain ⟨rfl, -⟩ := he.1.unique hz - rw [← hr.abs_eq] at hv - obtain ⟨-, hstep, hinv⟩ := step_methods hr.valid hr.inv hv - rw [hr.abs_eq] at hstep - rw [hstep, he.2, hres, ← hm] - exact ⟨rfl, hr.valid, fun i => (hl i).trans (hr.layout i), hb, hm ▸ hinv, rfl⟩) - -/-- The representation of the native stack. -/ -public theorem rep_iff (w C : ℕ) (L : Stack.Memory.Layout) (vs : List ℕ) (x : Data registers) : - (implementation w C L).rep vs x ↔ Rep w C L vs x := Iff.rfl +public noncomputable def implementation {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C) : + StateTransitionImpl (Stack.spec w C) ((backend w C L).exec w) := + Backend.implementation (implements hL) /-- **Constant time per operation.** One constant `K`, chosen before every width, capacity, layout, state and operation, bounds the instructions of every admissible operation. -/ -public theorem instruction_bound : ∃ K, ∀ (w C : ℕ) (L : Stack.Memory.Layout), - Bound (implementation w C L) id fun _ _ => K := by - refine ⟨factor * 23, fun w C L => ?_⟩ - intro vs x o a y n hr hv he - obtain ⟨z, m, hz, hm, _⟩ := exists_body hr o hv - obtain ⟨-, rfl⟩ := he.1.unique hz - exact hm +public 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.** Every client whose history is admissible has a native execution, one -operation at a time, that returns the specified answer and represents the specified final stack. -Its instruction total is at most `K` per operation, for one constant `K`. The client's own -computation between operations is outside the machine. -/ -public theorem run_client : ∃ K, ∀ {w C : ℕ} {L : Stack.Memory.Layout} {α : Type} - (p : (Stack.spec w C).Client α) {vs : List ℕ} {x : Data registers}, Rep w C L vs x → +operation at a time, that returns the specified answer and represents the specified final stack, +within `K` instructions per operation. The client's own computation is outside the machine. -/ +public theorem run_client : ∃ K, ∀ {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C) + {α : Type} (p : (Stack.spec w C).Client α) {vs : List ℕ} + {x : Data (Backend.registers certificates)}, (implementation hL).rep vs x → (Stack.spec w C).ValidHistory vs ((Stack.spec w C).clientHistory vs p) → - ∃ y traces, Executes (exec w C) p x ((Stack.spec w C).clientResult vs p) y traces ∧ - Rep w C L ((Stack.spec w C).clientState vs p) y ∧ + ∃ y traces, Executes ((backend w C L).exec w) p x ((Stack.spec w C).clientResult vs p) y + traces ∧ (implementation hL).rep ((Stack.spec w C).clientState vs p) y ∧ total id traces ≤ ((Stack.spec w C).clientHistory vs p).length * K := by obtain ⟨K, hK⟩ := instruction_bound - exact ⟨K, fun p _ _ hr hv => Bound.exists_executes_const (hK _ _ _) p hr hv⟩ + exact ⟨K, fun hL _ p _ _ hr hv => Bound.exists_executes_const (hK hL) p hr hv⟩ /-- Every operation writes only the layout's cells and keeps the frontier. -/ -public theorem modifies {w C : ℕ} {L : Stack.Memory.Layout} {vs : List ℕ} - {x y : Data registers} {o : StackOp ℕ} {a : (Stack.spec w C).Result o} {n : ℕ} - (hr : Rep w C L vs x) (hv : (Stack.spec w C).valid vs o) (he : exec w C x o a y n) : - Modifies L.footprint x.mem y.mem ∧ y.regs frontier = x.regs frontier := by - obtain ⟨z, m, hz, _, hm, _⟩ := exists_body hr o hv - obtain ⟨rfl, -⟩ := he.1.unique hz - rw [← hr.abs_eq] at hv - obtain ⟨hmem, hfr⟩ := Stack.Memory.modifies_methods hr.valid o hv +public theorem modifies {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C) {vs : List ℕ} + {x y : Data (Backend.registers certificates)} {o : StackOp ℕ} + {a : (Stack.spec w C).Result o} {n : ℕ} (hr : (implementation hL).rep vs x) + (hv : (Stack.spec w C).valid vs o) (he : (backend w C L).exec w x o a y n) : + Modifies L.footprint x.mem y.mem ∧ + y.regs (Backend.frontier certificates) = x.regs (Backend.frontier certificates) := by + have hm : memoryState (Backend.frontier certificates) y = (Memory.spec w).clientState + (memoryState (Backend.frontier certificates) x) (Stack.Memory.methods L w C o) := + Backend.exec_memoryState (implements hL) hr hv he + rw [← ((Backend.rep_iff _ _ _).mp hr).2.1] at hv + obtain ⟨hmem, hfr⟩ := Stack.Memory.modifies_methods hL o hv rw [← hm] at hmem hfr exact ⟨hmem, hfr⟩ /-- Every operation preserves each heap assertion disjoint from the layout. -/ -public theorem frame {w C : ℕ} {L : Stack.Memory.Layout} {vs : List ℕ} - {x y : Data registers} {o : StackOp ℕ} {a : (Stack.spec w C).Result o} {n : ℕ} - (hr : Rep w C L vs x) (hv : (Stack.spec w C).valid vs o) (he : exec w C x o a y n) +public theorem frame {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C) {vs : List ℕ} + {x y : Data (Backend.registers certificates)} {o : StackOp ℕ} + {a : (Stack.spec w C).Result o} {n : ℕ} (hr : (implementation hL).rep vs x) + (hv : (Stack.spec w C).valid vs o) (he : (backend w C L).exec w x o a y n) {Q : HeapAssertion} (hQ : Q.holds x.mem) (hd : Disjoint Q.footprint L.footprint) : Q.holds y.mem := - HeapAssertion.frame hQ hd (modifies hr hv he).1 + HeapAssertion.frame hQ hd (modifies hL hr hv he).1 /-- The layout of a stack of capacity `C` at `base`: the count cell `base`, then the items. -/ public def initialLayout (base C : ℕ) : Stack.Memory.Layout := ⟨⟨base, 1⟩, ⟨base + 1, C⟩⟩ -/-- An empty stack of capacity `C` at `base`, with zero memory: the layout words in their -registers, the frontier just past the items, and every other register zero. This is a -calling-convention state, not an uncharged allocation; `Stack.Memory.allocLayout` allocates. -/ -public noncomputable def initial (base C : ℕ) : Data registers where - regs i := if i = frontier then base + 1 + C - else if h : 1 ≤ i.val ∧ i.val ≤ 4 then layoutWords (initialLayout base C) ⟨i.val - 1, by omega⟩ - else 0 - mem _ := 0 +/-- The initial layout is well formed when the end of the items fits a word. -/ +public theorem valid_initialLayout {w base C : ℕ} (hf : Fits w (base + 1 + C)) : + (initialLayout base C).Valid w C := Stack.Memory.valid_allocLayout hf + +/-- An empty stack of capacity `C` at `base`: zero memory, and the frontier just past the items. +This is a calling-convention state, not an uncharged allocation; `Stack.Memory.allocLayout` +allocates. -/ +public noncomputable def initial (base C : ℕ) : Data (Backend.registers certificates) := + Backend.data certificates ⟨fun _ => 0, base + 1 + C⟩ /-- The initial state represents the empty stack when the end of the items fits a word. -/ public theorem initial_rep {w base C : ℕ} (hf : Fits w (base + 1 + C)) : - Rep w C (initialLayout base C) [] (initial base C) := by - have hfr : (initial base C).regs frontier = base + 1 + C := by simp [initial] - have hl (i : Fin 4) : - (initial base C).regs (layoutReg i) = layoutWords (initialLayout base C) i := by - have h1 : 1 ≤ (layoutReg i).val ∧ (layoutReg i).val ≤ 4 := by - simp only [layoutReg]; omega - simp only [initial, layoutReg_ne_frontier i, h1, ↓reduceIte] - congr 1 - refine ⟨Stack.Memory.valid_allocLayout hf, hl, ⟨fun i => ?_, fun _ => fits_zero w⟩, - ⟨?_, ?_, ?_⟩, ?_⟩ - · change Fits w (ite _ _ _) - split - · exact hf - · split - · rename_i h - refine hf.of_le ?_ - have hi : ∀ j : Fin 4, layoutWords (initialLayout base C) j ≤ base + 1 + C := by - intro j - fin_cases j <;> simp [layoutWords, initialLayout] <;> omega - exact hi _ - · exact fits_zero w - · change base + 1 ≤ (initial base C).regs frontier - omega - · change base + 1 + C ≤ (initial base C).regs frontier - omega - · exact Nat.zero_le _ - · simp [Stack.Memory.abs, memoryState, initial] + (implementation (valid_initialLayout hf)).rep [] (initial base C) := + Backend.rep_data _ ⟨hf, fun _ => fits_zero w⟩ + ⟨show base + 1 ≤ base + 1 + C by omega, le_rfl, Nat.zero_le _⟩ + (by simp [Stack.Memory.refinement, Stack.Memory.abs]) end Cslib.DataStructure.Stack.Native diff --git a/ComplexityTests/DataStructureStack.lean b/ComplexityTests/DataStructureStack.lean index 2813398870..0e8f913835 100644 --- a/ComplexityTests/DataStructureStack.lean +++ b/ComplexityTests/DataStructureStack.lean @@ -29,10 +29,13 @@ def stackClient : (Stack.spec 4 2).Client (Option ℕ × ℕ) := /-- The native stack runs the client from the initial state, one operation at a time: it pops the pushed value, ends empty, and spends at most the constant `K` on each of its four calls. -/ example : ∃ K, ∃ y traces, - Executes (Stack.Native.exec 4 2) stackClient (Stack.Native.initial 3 2) (some 7, 0) y traces ∧ - Stack.Native.Rep 4 2 (Stack.Native.initialLayout 3 2) [] y ∧ total id traces ≤ 4 * K := by + Executes ((Stack.Native.backend 4 2 (Stack.Native.initialLayout 3 2)).exec 4) stackClient + (Stack.Native.initial 3 2) (some 7, 0) y traces ∧ + (Stack.Native.implementation + (Stack.Native.valid_initialLayout (w := 4) (base := 3) (C := 2) (by decide))).rep [] y ∧ + total id traces ≤ 4 * K := by obtain ⟨K, hK⟩ := Stack.Native.run_client - exact ⟨K, hK stackClient (Stack.Native.initial_rep (by decide)) (by + exact ⟨K, hK _ stackClient (Stack.Native.initial_rep (by decide)) (by change (0 < 2 ∧ Fits 4 7) ∧ True ∧ True ∧ True ∧ True decide)⟩ diff --git a/ComplexityTests/WordRAMRealizationBackend.lean b/ComplexityTests/WordRAMRealizationBackend.lean index 9012d398d6..11d8a3202f 100644 --- a/ComplexityTests/WordRAMRealizationBackend.lean +++ b/ComplexityTests/WordRAMRealizationBackend.lean @@ -38,7 +38,7 @@ noncomputable def certificates : Tag → Certificate .exact /-- 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 + Backend (Stack.spec w C) certificates where tag | .push _ => .push | .pop => .pop @@ -78,7 +78,7 @@ theorem implements {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C) : /-- 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) := + StateTransitionImpl (Stack.spec w C) ((backend w C L).exec w) := Backend.implementation (implements hL) /-- One constant bounds every operation, before every width, capacity and layout. -/ @@ -90,14 +90,14 @@ 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 ℕ} + (p : (Stack.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) → + (Stack.spec w C).ValidHistory vs ((Stack.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 + ((Stack.spec w C).clientResult vs p) y traces ∧ + (implementation hL).rep ((Stack.spec w C).clientState vs p) y ∧ + total id traces ≤ ((Stack.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⟩ @@ -113,9 +113,9 @@ example : (certificates .push).l = 0 := rfl -- 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 ∧ + {a : (Stack.spec w C).Result o} {n : ℕ} (hr : (implementation hL).rep vs x) + (hv : (Stack.spec w C).valid vs o) (he : (backend w C L).exec w x o a y n) : + a = ((Stack.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, From 212e582460938d065cf7f98688a2767d1128dc89 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 23:02:00 -0500 Subject: [PATCH 30/40] perf(WordRAM): state realize certificates per form in the client's terms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `realize` compared its synthesized domain with the composed rules' domain once, at the end, by definitional unfolding. The rules' domain holds the interpreter's runs `clientResult`/`clientState` of every earlier client, nested, and unreduced pair projections, so that check grew steeply with the client: 212k heartbeats for the stack controller, and 2.5M when a bind's first client ends in a `match`. Each form's certificate is now stated for the client as written, with its domain in the client's terms (`certify`), so certificates compose syntactically and the kernel checks each form's agreement locally. A bind's continuation domain is stated at the first client's reply and next memory in closed form (`Realizes.bind₀_of_run`). Result encodings are proved by `Fin.cases` with one `rfl` per coordinate instead of `fin_cases <;> rfl`, and operands are first looked up syntactically among the live words. Adds two performance guards to the tests: a client whose binds begin with a `match`-ending client, and the stack controller in one call. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../WordRAM/Realization/Absorb.lean | 15 +++ Complexity/Tactic/Realize.lean | 100 +++++++++++++----- ComplexityTests/WordRAMRealize.lean | 78 ++++++++++++++ 3 files changed, 165 insertions(+), 28 deletions(-) diff --git a/Complexity/Computability/WordRAM/Realization/Absorb.lean b/Complexity/Computability/WordRAM/Realization/Absorb.lean index d69fdbdcf6..26274344f5 100644 --- a/Complexity/Computability/WordRAM/Realization/Absorb.lean +++ b/Complexity/Computability/WordRAM/Realization/Absorb.lean @@ -39,6 +39,8 @@ absorption applied. They are the target shapes of the `realize` tactic. Glue tak plus another grading. * `Realizes.congr`: transport a certificate along pointwise equal programs. * `Realizes.bind₀`, `Realizes.branch₀`: sequencing and branching with absorbed control work. + `Realizes.bind₀_of_run` states the continuation's domain at a closed form of the first + client's reply and next memory. * `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. @@ -132,6 +134,19 @@ theorem bind₀ (hP : Realizes B p input answer safeP (fun _ _ => 0)) (fun _ _ => 0) := (hP.bind hQ).mono_mul 1 (fun _ _ => by omega) +/-- Sequencing with the first client's reply and next memory given in closed form, so that the +continuation's domain is stated at them rather than at the interpreter's run. -/ +theorem bind₀_of_run (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)) + (reply : δ → Memory.State → α) (next : δ → Memory.State → Memory.State) + (hreply : ∀ d s, (Memory.spec 0).clientResult s (p d) = reply d s) + (hnext : ∀ d s, (Memory.spec 0).clientState s (p d) = next d s) : + 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, reply d s) (next d s)) (fun _ _ => 0) := + (bind₀ hP hQ).consequence (fun w d s _ _ h => by rw [hreply, hnext]; exact h) + (fun _ _ => le_rfl) + /-- 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} diff --git a/Complexity/Tactic/Realize.lean b/Complexity/Tactic/Realize.lean index 42ddc42df3..0ec652571c 100644 --- a/Complexity/Tactic/Realize.lean +++ b/Complexity/Tactic/Realize.lean @@ -8,7 +8,6 @@ module -- upstream: candidate public import Complexity.Computability.WordRAM.Realization.Absorb -public import Mathlib.Tactic.FinCases /-! # The `realize` tactic @@ -49,6 +48,11 @@ environment, the memory, the fit of the input words, memory well-formedness, and domain. It splits the conjunctions and `if`s of the synthesized domain and leaves each remaining condition as a goal. There is no search: an unsupported form is reported with its subterm. +Each certificate is stated for the client form as written, with its domain in the client's terms +(`certify`), so certificates compose syntactically. A bind's continuation domain is stated at the +first client's reply and next memory in closed form (`Realizes.bind₀_of_run`), not at the +interpreter's run. The kernel checks, locally at each form, that the rule's statement agrees. + ## Main definitions * `realize`: the tactic. @@ -100,9 +104,11 @@ 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. -/ +/-- Locate an expression among the live words: syntactically after `whnf`, as the live words are +themselves in weak-head normal form, and otherwise by definitional equality. -/ private def findAtom (atoms : Array Expr) (e : Expr) : MetaM (Option Nat) := do let e' ← whnf e + if let some i := atoms.findIdx? (· == e') then return some i for h : i in [0:atoms.size] do if ← withNewMCtxDepth (isDefEq e' atoms[i]) then return some i return none @@ -497,13 +503,28 @@ private def bindPure (a cont : Expr) : MetaM Expr := do 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 +/-- Prove `∀ d i, output (f d) i = g i (input d)` for `i : Fin l` by `Fin.cases`, with one `rfl` per +coordinate. Both sides are the same coordinate by construction of `g` (`coordinates`, `analyze`); +the kernel checks each `rfl`. -/ +private def proveResult (ty : Expr) (l : Nat) : MetaM Expr := + forallBoundedTelescope ty (some 2) fun xs eq => do + let #[d, i] := xs | throwError "realize: internal error in the result encoding" + let P ← mkLambdaFVars #[i] eq + -- a proof of `∀ j : Fin n, P (emb j)` + let rec cover (n : Nat) (emb : Expr → MetaM Expr) : MetaM Expr := do + let finN := mkApp (mkConst ``Fin) (mkNatLit n) + match n with + | 0 => withLocalDeclD `j finN fun j => do + mkLambdaFVars #[j] (← mkAppOptM ``Fin.elim0 #[P.beta #[← emb j], j]) + | m + 1 => do + let motive ← withLocalDeclD `j finN fun j => do mkLambdaFVars #[j] (P.beta #[← emb j]) + let goal := P.beta #[← emb (← mkNumeral finN 0)] + let some (_, lhs, _) := goal.eq? + | throwError "realize: internal error in the result encoding" + let h0 ← mkExpectedTypeHint (← mkEqRefl lhs) goal + let hs ← cover m fun j => do emb (← mkAppM ``Fin.succ #[j]) + mkAppOptM ``Fin.cases #[mkNatLit m, motive, h0, hs] + mkLambdaFVars #[d] (← cover l pure) /-- 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) : @@ -562,7 +583,7 @@ private def realizePure (ctx : Ctx) (env : Env) (d : Expr) (atoms : Array Expr) 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 hresult ← proveResult resTy l let proof ← mkAppM ``Realizes.pure₀ #[env.input, out, f, gfun, need, hresult, hcoord, hneed] let safe ← withLocalDeclD `s (mkConst ``Memory.State) fun s => @@ -652,6 +673,24 @@ private def proveQuery (body ty : Expr) : MetaM Expr := let goal ← mkAppM ``LE.le #[mkNatLit 1, ← mkAppM ``memoryCost #[mkApp body x, s]] mkLambdaFVars #[x, s] (← mkExpectedTypeHint pf goal) +/-- State `proof` as the certificate `Realizes B prog input output safe 0`, for the client `prog` +as written and the domain `safe` in the client's terms. + +The rule's statement agrees with this one by construction: up to unfolding `prog`, reducing pair +projections, and evaluating the live words, which were located as the weak-head normal forms of +`input d i`. The kernel checks the agreement locally at each form. Elaboration does not re-check +it, because unfolding composed domains with nested interpreter runs grows with the client. -/ +private def certify (ctx : Ctx) (env : Env) (proof prog output safe : Expr) : MetaM Expr := do + mkExpectedTypeHint proof + (← mkAppM ``Realizes #[ctx.basis, prog, env.input, output, safe, ← zeroWork env.ty]) + +/-- `Realizes.bind₀_of_run hP hQ reply next`, with the reply and next memory `fun d s => …` proved +by `rfl`. -/ +private def bindRun (hP hQ reply next : Expr) : MetaM Expr := do + let refl (f : Expr) : MetaM Expr := + lambdaBoundedTelescope f 2 fun xs b => do mkLambdaFVars xs (← mkEqRefl b) + mkAppM ``Realizes.bind₀_of_run #[hP, hQ, reply, next, ← refl reply, ← refl next] + mutual /-- Certify a client `prog : δ → Memory.Prog α` in the environment `env`. -/ @@ -660,7 +699,8 @@ private partial def realizeProg (ctx : Ctx) (env : Env) (prog : Expr) (output? : 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? + let out ← realizeBody ctx env d atoms body output? + return { out with proof := ← certify ctx env out.proof prog out.output out.safe } /-- Certify the body of a client for the environment variable `d`. -/ private partial def realizeBody (ctx : Ctx) (env : Env) (d : Expr) (atoms : Array Expr) @@ -777,9 +817,8 @@ private partial def realizeOptionSplit (ctx : Ctx) (env : Env) (d : Expr) (atoms 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] + let h ← certify ctx env out.proof (← mkLambdaFVars #[d] rhs) out.output out.safe + let proof ← mkAppM ``Realizes.congr #[h, eq] return { out with proof } /-- Certify `iterate step D n a`: copy the initial state and the fuel, then run the loop over the @@ -811,8 +850,15 @@ private partial def realizeIterate (ctx : Ctx) (env : Env) (d : Expr) (atoms : A 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] + -- both binds return a computed value and leave the memory unchanged + let (nReply, nNext) ← withLocalDeclD `x envB.ty fun x => + withLocalDeclD `s (mkConst ``Memory.State) fun s => do + let nx := (← mkLambdaFVars #[d] n).beta #[← mkAppM ``Prod.fst #[x]] + return (← mkLambdaFVars #[x, s] nx, ← mkLambdaFVars #[x, s] s) + let h2 ← bindRun hN.proof hLoop nReply nNext + let (aReply, aNext) ← withLocalDeclD `s (mkConst ``Memory.State) fun s => do + return (← mkLambdaFVars #[d, s] a, ← mkLambdaFVars #[d, s] s) + let proof ← bindRun hA.proof h2 aReply aNext let safe ← withLocalDeclD `w (mkConst ``Nat) fun w => withLocalDeclD `s (mkConst ``Memory.State) fun s => do let pair ← mkAppM ``Prod.mk #[d, a] @@ -820,8 +866,7 @@ private partial def realizeIterate (ctx : Ctx) (env : Env) (d : Expr) (atoms : A 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 + let proof ← certify ctx env proof progR out safe 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])) @@ -840,12 +885,15 @@ private partial def realizeBind (ctx : Ctx) (env : Env) (d m f : Expr) (output? 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] + -- the reply and next memory in the client's terms, so that the domain is stated in them + let (reply, next) ← withLocalDeclD `s (mkConst ``Memory.State) fun s => do + let (r, s') ← replyState m s + return (← mkLambdaFVars #[d, s] r, ← mkLambdaFVars #[d, s] s') + let proof ← bindRun hP.proof hQ.proof reply next 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']) + let pair ← mkAppM ``Prod.mk #[d, reply.beta #[d, s]] + let sq ← reducePairs (hQ.safe.beta #[w, pair, next.beta #[d, s]]) mkLambdaFVars #[w, d, s] (mkAnd (hP.safe.beta #[w, d, s]) sq) return { proof, output := hQ.output, safe } @@ -896,14 +944,10 @@ elab "realize" : tactic => withMainContext do let atoms ← withLocalDeclD `d δ fun d => do (← encodingAtoms input d k).mapM (mkLambdaFVars #[d] ·) let env : Env := { ty := δ, k, input, atoms } + -- `realizeProg` states the certificate for `p` itself, with the domain `out.safe` 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] + output, out.safe, ← zeroWork δ, args[8]!, args[9]!, out.proof] let [hsafe, hwork] ← goal.apply cons | throwError "realize: internal error in `consequence`" setGoals [hwork] evalTactic (← `(tactic| exact fun _ _ => Nat.zero_le _)) diff --git a/ComplexityTests/WordRAMRealize.lean b/ComplexityTests/WordRAMRealize.lean index 5030864328..397b524987 100644 --- a/ComplexityTests/WordRAMRealize.lean +++ b/ComplexityTests/WordRAMRealize.lean @@ -205,6 +205,84 @@ theorem roundTrip_program : ∃ n C : ℕ, (hc.of_le (by simp [inputState]; omega)) hvalid exact ⟨t, ht, hle.trans (by rfl)⟩ +/-! ## Performance guards + +Each certificate is stated for the client form as written, with its domain in the client's +terms, so the cost of `realize` follows the client's size. Before that, the domains of nested +binds were compared by unfolding the interpreter's runs. A bind whose first client ends in a +`match` then took about 800k heartbeats, and the adaptive stack controller about 212k. The +budgets below include the side conditions. -/ + +/-- Pop, reading an empty stack as zero. -/ +def popWord (L : Stack.Memory.Layout) : Memory.Prog ℕ := do + match ← Stack.Memory.pop L with + | none => pure 0 + | some v => pure v + +/-- Push `v` and pop it through `popWord`; on zero, push one and pop again. Both pops are binds +whose first client ends in a `match`. -/ +def bounce (d : Stack.Memory.Layout × ℕ) : Memory.Prog ℕ := do + Stack.Memory.push d.1 d.2 + let v ← popWord d.1 + if v = 0 then do + Stack.Memory.push d.1 1 + popWord d.1 + else pure v + +set_option maxHeartbeats 40000 in +-- Performance guard: about 8k heartbeats are needed; `realize` alone once took about 800k. +example : Realizes .exact bounce pushWords (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + (fun w d s => d.1.Valid w 1 ∧ Stack.Memory.Inv d.1 s ∧ s.mem d.1.count.base = 0) + (fun _ _ => 0) := by + realize + all_goals + obtain ⟨hL, hI, h0⟩ := h + have hne : d.1.items.base ≠ d.1.count.base := fun he => + hL.count_not_owns (he ▸ Block.owns_add (i := 0) (by rw [hL.items_length]; omega)) + have hlt : Fits w d.1.items.base := hmem.fits_of_lt_frontier (by + simpa using hI.item_lt (i := 0) (by rw [hL.items_length]; omega)) + try simp only [popWord, ← PFunctor.FreeM.bind_eq_bind, StateTransitionSpec.runClient_bind, + StateTransitionSpec.clientState, StateTransitionSpec.clientResult, + StateTransitionSpec.runClient_pure', Stack.Memory.runClient_push, + Stack.Memory.runClient_pop_of_pos, h0, Nat.add_zero, Nat.zero_add, Nat.sub_self, ne_eq, + Nat.one_ne_zero, not_false_eq_true, Function.update_self, Function.update_of_ne hne, + Function.update_idem] at * + first | exact hlt | exact hL.capacity_fits | exact le_rfl + +/-- The adaptive stack controller: allocate a stack of capacity one, push `n` and pop; on a zero +answer push one and pop again. -/ +def controller (n : ℕ) : Memory.Prog ℕ := do + let L ← Stack.Memory.allocLayout 1 + Stack.Memory.push L n + match ← Stack.Memory.pop L with + | none => pure 0 + | some v => + if v = 0 then + (do + Stack.Memory.push L 1 + match ← Stack.Memory.pop L with + | none => pure 0 + | some u => pure u) + else pure v + +set_option maxHeartbeats 40000 in +-- Performance guard: about 11k heartbeats are needed; `realize` alone once took about 212k. +example : Realizes .exact controller (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding (fun w _ s => Fits w (s.frontier + 2)) + (fun _ _ => 0) := by + realize + all_goals + try simp only [show (Memory.spec 0).clientResult s (Stack.Memory.allocLayout 1) = + ⟨⟨s.frontier, 1⟩, ⟨s.frontier + 1, 1⟩⟩ from rfl, + show (Memory.spec 0).clientState s (Stack.Memory.allocLayout 1) = + ⟨clear s.mem s.frontier 1, s.frontier + 1 + 1⟩ from rfl, + clear_of_owns (show (Block.mk s.frontier 1).Owns s.frontier from ⟨le_rfl, by simp⟩), + StateTransitionSpec.clientState, StateTransitionSpec.clientResult, + Stack.Memory.runClient_push, Stack.Memory.runClient_pop_of_pos, Nat.zero_add, + Nat.add_zero, Nat.sub_self, ne_eq, Nat.one_ne_zero, not_false_eq_true, + Function.update_self, optionWords_some, Matrix.cons_val_zero, Matrix.cons_val_one] at * + first | exact le_rfl | exact h.of_le (by omega) + /-! ## Failure messages -/ /-- A power has no glue rule. -/ From 65c1d5d6fa4696f6aafc27121c9c100bf5d1d2a0 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 23:05:08 -0500 Subject: [PATCH 31/40] refactor(Stack): keep only consumed native API; fold the traversal word stack into TraversalStack Stack.Native keeps the backend, implementation and instruction_bound. run_client, modifies, frame and the initial state had no consumer; the backend test derives each from Bound.exists_executes_const, Backend.exec_memoryState with Stack.Memory.modifies_methods, and Backend.rep_data. The backend test now uses Stack.Native instead of its own copy, and absorbs DataStructureStack. Traversal.WordStack moves into TraversalStack.lean, its only importer, unchanged. Stack.Controller uses memoryCost_eq_totalCost and drops unused imports. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 - .../ThreeSum/ChanHe/TraversalStack.lean | 138 ++++++++++++++- .../ThreeSum/ChanHe/TraversalWordStack.lean | 159 ------------------ .../DataStructure/Stack/Controller.lean | 7 +- Complexity/DataStructure/Stack/Native.lean | 70 +------- ComplexityTests/DataStructureStack.lean | 49 ------ .../WordRAMRealizationBackend.lean | 138 +++++---------- UPSTREAM.md | 8 +- 8 files changed, 187 insertions(+), 383 deletions(-) delete mode 100644 Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalWordStack.lean delete mode 100644 ComplexityTests/DataStructureStack.lean diff --git a/Complexity.lean b/Complexity.lean index fd1417611c..49df25b06f 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -497,7 +497,6 @@ public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.TraversalStack public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.TraversalStep public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.TraversalTime public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.TraversalVisit -public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.TraversalWordStack public import Complexity.Algorithms.WordRAM.ThreeSum.Decision public import Complexity.Algorithms.WordRAM.ThreeSum.Decision.Body public import Complexity.Algorithms.WordRAM.ThreeSum.Decision.Boundary diff --git a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalStack.lean b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalStack.lean index 4793a0bb45..e3b3a6d64d 100644 --- a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalStack.lean +++ b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalStack.lean @@ -1,13 +1,14 @@ /- Copyright (c) 2026 Erik Demaine. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Erik Demaine +Authors: Erik Demaine, Samuel Schlesinger -/ module -- upstream: candidate -public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.TraversalWordStack +public import Complexity.Computability.WordRAM.Allocation +public import Complexity.Computability.WordRAM.Heap import Mathlib.Tactic.FinCases /-! @@ -18,11 +19,20 @@ registers 96–104. Registers below 64 remain available to the complete modulus Each record contains fuel followed by three base/length pairs. The stack stores words, so a record is pushed in reverse order and popped in forward order. -The dispatcher, representation and memory frame are those of `Traversal.WordStack`. -Argument delivery and result copies here are actual native instructions and are charged. +The word stack is a fixed nine-register program, `WordStack.program`, that dispatches on an +operation tag: push = 0, pop = 1, size = 2. It occupies a fixed consecutive memory block, with +its top at the highest live address; a pop leaves the old cell in place. Traversal's time bound +adds up exact instruction counts, so the two calls it makes, a push with spare capacity and a pop +from a nonempty stack, are proved instruction by instruction. The library's bounded stack is +`Stack.Memory`, which `Stack.Native` runs on the word RAM; this dispatcher is part of the +hand-stepped traversal and goes with it. Argument delivery and result copies here are actual +native instructions and are charged. ## Main definitions +* `WordStack.program`, `WordStack.prepare`: the dispatcher and its argument delivery. +* `WordStack.Rep`, `WordStack.footprint`: the top-first list representation and the reserved + memory block. * `slot`: the seven persistent traversal-record registers. * `registers`: the nine-register stack dispatcher inside the 128-register controller. * `pushSlot`: native argument delivery followed by the complete bounded-stack dispatcher. @@ -30,11 +40,18 @@ Argument delivery and result copies here are actual native instructions and are ## Main results +* `WordStack.exec_push`, `WordStack.exec_pop`: a push in seven instructions and a nonempty pop in + eleven. * `pushSlot_exec`: a nine-instruction push, with the exact stack, memory and register frames. * `popSlot_exec`: a fourteen-instruction nonempty pop with the same exact frames. * `pushFrame_exec`, `popFrame_exec`: complete seven-word record transfers in 64 and 99 instructions. The native controller and its composition proofs are original. + +## References + +* [T. H. Cormen, C. E. Leiserson, R. L. Rivest and C. Stein, *Introduction to Algorithms*, + Section 10.1][CLRS2009] -/ @[expose] public section @@ -43,6 +60,119 @@ namespace Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Traversal open Cslib.WordRAM +namespace WordStack + +/-- Encoded operation tag: push = 0, pop = 1, size = 2. -/ +abbrev tag : Fin 9 := 0 +/-- Caller-supplied push value. -/ +abbrev arg : Fin 9 := 1 +/-- Persistent number of live entries. -/ +abbrev len : Fin 9 := 2 +/-- Persistent base address of the reserved region. -/ +abbrev ptr : Fin 9 := 3 +/-- Temporary cell address. -/ +abbrev addr : Fin 9 := 4 +/-- Returned value. -/ +abbrev out : Fin 9 := 5 +/-- Constant zero. -/ +abbrev zero : Fin 9 := 6 +/-- Constant one. -/ +abbrev one : Fin 9 := 7 +/-- Presence bit distinguishing `none` from a returned zero word. -/ +abbrev present : Fin 9 := 8 + +/-- Fixed operation dispatch. No code is generated from an operation or its payload. -/ +def program : Code .standard 9 := ram[ + zero ← #0; one ← #1; + if tag = zero then [ + ⟪writeAt addr ptr len arg⟫; len ← len + one; out ← zero + ] else [ + if tag = one then [ + if len = zero then [ out ← zero; present ← zero ] else [ + len ← len - one; ⟪readAt addr out ptr len⟫; present ← one + ] + ] else [ out ← len ] + ] ] + +/-- Deliver an operation tag and a push value. -/ +def prepare (t v : ℕ) (x : Data 9) : Data 9 := (x.set tag t).set arg v + +/-- Persistent register, bounded-word, and live array invariants. Cells above the live prefix +may contain old popped values; they are part of the reserved footprint. -/ +structure Rep (w base capacity : ℕ) (vs : List ℕ) (x : Data 9) : Prop where + bounded : x.Bounded w + pointer : x.regs ptr = base + length_eq : x.regs len = vs.length + capacity_le : vs.length ≤ capacity + contents : ∀ i : Fin vs.length, x.mem (base + i.val) = vs.reverse[i.val] + +/-- Reserved memory footprint; it stays fixed through pushes and pops. -/ +def footprint (base capacity : ℕ) : Finset ℕ := + (Block.mk base capacity).footprint + +/-- A push with spare capacity writes the value above the top in seven instructions. -/ +theorem exec_push {w base capacity : ℕ} (h2 : Fits w 2) (hcap : Fits w (base + capacity)) + {vs : List ℕ} {x : Data 9} {v : ℕ} (hr : Rep w base capacity vs x) + (hv : vs.length < capacity) (hw : Fits w v) : + ∃ y, Exec w program (prepare 0 v x) y 7 ∧ + Rep w base capacity (v :: vs) y ∧ Modifies (footprint base capacity) x.mem y.mem := by + have h1 : Fits w 1 := h2.of_le (by decide) + have haddr : Fits w (base + vs.length) := hcap.of_le (by omega) + have hinc : Fits w (vs.length + 1) := hcap.of_le (by omega) + let y := (((((prepare 0 v x).set zero 0).set one 1).set + addr (base + vs.length)).write (base + vs.length) v |>.set len (vs.length + 1)).set out 0 + have he : Exec w program (prepare 0 v x) y 7 := by + ram_eval [program, prepare, writeAt, y, hr.pointer, hr.length_eq, h1, haddr, hinc] + refine ⟨y, he, ⟨?_, ?_, ?_, ?_, ?_⟩, ?_⟩ + · exact he.bounded ((hr.bounded.set (fits_zero w)).set hw) + · simp +decide [y, Data.set, Data.write, prepare, hr.pointer] + · simp +decide [y, Data.set, Data.write, prepare] + · simpa using hv + · intro i + by_cases hi : i.val < vs.length + · have hne : base + i.val ≠ base + vs.length := by omega + simpa [y, Data.set, Data.write, prepare, List.reverse_cons, List.getElem_append, hi, + Function.update_of_ne hne] using hr.contents ⟨i.val, hi⟩ + · have hi' : i.val = vs.length := by + have := i.isLt + simp only [List.length_cons] at this + omega + simp [y, Data.set, Data.write, prepare, List.reverse_cons, hi'] + · exact Modifies.write_of_owns ⟨Nat.le_add_right _ _, Nat.add_lt_add_left hv _⟩ v + +/-- A pop from a nonempty stack returns its top in eleven instructions and writes no memory. -/ +theorem exec_pop {w base capacity : ℕ} (h2 : Fits w 2) (hcap : Fits w (base + capacity)) + {vs : List ℕ} {x : Data 9} {v : ℕ} (hr : Rep w base capacity (v :: vs) x) : + ∃ y, Exec w program (prepare 1 0 x) y 11 ∧ y.regs out = v ∧ + Rep w base capacity vs y ∧ Modifies (footprint base capacity) x.mem y.mem := by + have h1 : Fits w 1 := h2.of_le (by decide) + have hl : x.regs len = vs.length + 1 := hr.length_eq + have hsub : Fits w vs.length := hcap.of_le (by have := hr.capacity_le; simp_all; omega) + have ha : Fits w (base + vs.length) := hcap.of_le (by have := hr.capacity_le; simp_all; omega) + have hread : x.mem (base + vs.length) = v := by + have h := hr.contents ⟨vs.length, by simp⟩ + simpa [List.reverse_cons] using h + let y := (((((prepare 1 0 x).set zero 0).set one 1).set + len vs.length).set addr (base + vs.length)).set out v |>.set present 1 + have he : Exec w program (prepare 1 0 x) y 11 := by + ram_eval [program, prepare, readAt, y, hr.pointer, hl, h1, ha, hsub, Nat.add_sub_cancel, + Nat.add_eq_zero_iff, and_false, hread] + refine ⟨y, he, by simp +decide [y, Data.set], ⟨?_, ?_, ?_, ?_, ?_⟩, Modifies.refl _ _⟩ + · exact he.bounded ((hr.bounded.set (h2.of_le (by decide))).set (fits_zero w)) + · simp +decide [y, Data.set, prepare, hr.pointer] + · simp +decide [y, Data.set] + · have := hr.capacity_le + simp only [List.length_cons] at this + omega + · intro i + have hi : i.val < (v :: vs).length := by + simp only [List.length_cons] + omega + have h := hr.contents ⟨i.val, hi⟩ + simpa [y, Data.set, prepare, List.reverse_cons, List.getElem_append, i.isLt] using h + +end WordStack + /-- Fuel and the three base/length pairs, retained across both 64-register native kernels. -/ def slot (i : Fin 7) : Fin 128 := ⟨64 + i.val, by omega⟩ diff --git a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalWordStack.lean b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalWordStack.lean deleted file mode 100644 index 6b6736ffd0..0000000000 --- a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/TraversalWordStack.lean +++ /dev/null @@ -1,159 +0,0 @@ -/- -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.Allocation -public import Complexity.Computability.WordRAM.Heap - -/-! -# The word stack inside Chan–He traversal - -Traversal keeps its pending records on a bounded stack of words, run by a fixed nine-register -program that dispatches on an operation tag: push = 0, pop = 1, size = 2. The stack occupies a -fixed consecutive memory block, with its top at the highest live address; a pop leaves the old -cell in place. This file proves the two calls traversal makes, a push with spare capacity and a -pop from a nonempty stack, instruction by instruction: traversal's time bound adds up their exact -instruction counts. - -The library's bounded stack is `Stack.Memory`, which `Stack.Native` runs on the word RAM. This -dispatcher is part of the hand-stepped traversal and goes with it. - -## Main definitions - -* `tag`, `arg`, `len`, `ptr`, `addr`, `out`, `zero`, `one`, `present`: the nine registers. -* `program`: the fixed dispatcher. -* `prepare`: argument delivery of an operation tag and a value. -* `Rep`, `footprint`: the top-first list representation and the reserved memory block. - -## Main results - -* `exec_push`: a push in seven instructions. -* `exec_pop`: a nonempty pop in eleven instructions. - -## 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.Algorithms.WordRAM.ThreeSum.ChanHe.Traversal.WordStack - -open Cslib.WordRAM - -/-- Encoded operation tag: push = 0, pop = 1, size = 2. -/ -abbrev tag : Fin 9 := 0 -/-- Caller-supplied push value. -/ -abbrev arg : Fin 9 := 1 -/-- Persistent number of live entries. -/ -abbrev len : Fin 9 := 2 -/-- Persistent base address of the reserved region. -/ -abbrev ptr : Fin 9 := 3 -/-- Temporary cell address. -/ -abbrev addr : Fin 9 := 4 -/-- Returned value. -/ -abbrev out : Fin 9 := 5 -/-- Constant zero. -/ -abbrev zero : Fin 9 := 6 -/-- Constant one. -/ -abbrev one : Fin 9 := 7 -/-- Presence bit distinguishing `none` from a returned zero word. -/ -abbrev present : Fin 9 := 8 - -/-- Fixed operation dispatch. No code is generated from an operation or its payload. -/ -def program : Code .standard 9 := ram[ - zero ← #0; one ← #1; - if tag = zero then [ - ⟪writeAt addr ptr len arg⟫; len ← len + one; out ← zero - ] else [ - if tag = one then [ - if len = zero then [ out ← zero; present ← zero ] else [ - len ← len - one; ⟪readAt addr out ptr len⟫; present ← one - ] - ] else [ out ← len ] - ] ] - -/-- Deliver an operation tag and a push value. -/ -def prepare (t v : ℕ) (x : Data 9) : Data 9 := (x.set tag t).set arg v - -/-- Persistent register, bounded-word, and live array invariants. Cells above the live prefix -may contain old popped values; they are part of the reserved footprint. -/ -structure Rep (w base capacity : ℕ) (vs : List ℕ) (x : Data 9) : Prop where - bounded : x.Bounded w - pointer : x.regs ptr = base - length_eq : x.regs len = vs.length - capacity_le : vs.length ≤ capacity - contents : ∀ i : Fin vs.length, x.mem (base + i.val) = vs.reverse[i.val] - -/-- Reserved memory footprint; it stays fixed through pushes and pops. -/ -def footprint (base capacity : ℕ) : Finset ℕ := - (Block.mk base capacity).footprint - -/-- A push with spare capacity writes the value above the top in seven instructions. -/ -theorem exec_push {w base capacity : ℕ} (h2 : Fits w 2) (hcap : Fits w (base + capacity)) - {vs : List ℕ} {x : Data 9} {v : ℕ} (hr : Rep w base capacity vs x) - (hv : vs.length < capacity) (hw : Fits w v) : - ∃ y, Exec w program (prepare 0 v x) y 7 ∧ - Rep w base capacity (v :: vs) y ∧ Modifies (footprint base capacity) x.mem y.mem := by - have h1 : Fits w 1 := h2.of_le (by decide) - have haddr : Fits w (base + vs.length) := hcap.of_le (by omega) - have hinc : Fits w (vs.length + 1) := hcap.of_le (by omega) - let y := (((((prepare 0 v x).set zero 0).set one 1).set - addr (base + vs.length)).write (base + vs.length) v |>.set len (vs.length + 1)).set out 0 - have he : Exec w program (prepare 0 v x) y 7 := by - ram_eval [program, prepare, writeAt, y, hr.pointer, hr.length_eq, h1, haddr, hinc] - refine ⟨y, he, ⟨?_, ?_, ?_, ?_, ?_⟩, ?_⟩ - · exact he.bounded ((hr.bounded.set (fits_zero w)).set hw) - · simp +decide [y, Data.set, Data.write, prepare, hr.pointer] - · simp +decide [y, Data.set, Data.write, prepare] - · simpa using hv - · intro i - by_cases hi : i.val < vs.length - · have hne : base + i.val ≠ base + vs.length := by omega - simpa [y, Data.set, Data.write, prepare, List.reverse_cons, List.getElem_append, hi, - Function.update_of_ne hne] using hr.contents ⟨i.val, hi⟩ - · have hi' : i.val = vs.length := by - have := i.isLt - simp only [List.length_cons] at this - omega - simp [y, Data.set, Data.write, prepare, List.reverse_cons, hi'] - · exact Modifies.write_of_owns ⟨Nat.le_add_right _ _, Nat.add_lt_add_left hv _⟩ v - -/-- A pop from a nonempty stack returns its top in eleven instructions and writes no memory. -/ -theorem exec_pop {w base capacity : ℕ} (h2 : Fits w 2) (hcap : Fits w (base + capacity)) - {vs : List ℕ} {x : Data 9} {v : ℕ} (hr : Rep w base capacity (v :: vs) x) : - ∃ y, Exec w program (prepare 1 0 x) y 11 ∧ y.regs out = v ∧ - Rep w base capacity vs y ∧ Modifies (footprint base capacity) x.mem y.mem := by - have h1 : Fits w 1 := h2.of_le (by decide) - have hl : x.regs len = vs.length + 1 := hr.length_eq - have hsub : Fits w vs.length := hcap.of_le (by have := hr.capacity_le; simp_all; omega) - have ha : Fits w (base + vs.length) := hcap.of_le (by have := hr.capacity_le; simp_all; omega) - have hread : x.mem (base + vs.length) = v := by - have h := hr.contents ⟨vs.length, by simp⟩ - simpa [List.reverse_cons] using h - let y := (((((prepare 1 0 x).set zero 0).set one 1).set - len vs.length).set addr (base + vs.length)).set out v |>.set present 1 - have he : Exec w program (prepare 1 0 x) y 11 := by - ram_eval [program, prepare, readAt, y, hr.pointer, hl, h1, ha, hsub, Nat.add_sub_cancel, - Nat.add_eq_zero_iff, and_false, hread] - refine ⟨y, he, by simp +decide [y, Data.set], ⟨?_, ?_, ?_, ?_, ?_⟩, Modifies.refl _ _⟩ - · exact he.bounded ((hr.bounded.set (h2.of_le (by decide))).set (fits_zero w)) - · simp +decide [y, Data.set, prepare, hr.pointer] - · simp +decide [y, Data.set] - · have := hr.capacity_le - simp only [List.length_cons] at this - omega - · intro i - have hi : i.val < (v :: vs).length := by - simp only [List.length_cons] - omega - have h := hr.contents ⟨i.val, hi⟩ - simpa [y, Data.set, prepare, List.reverse_cons, List.getElem_append, i.isLt] using h - -end Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Traversal.WordStack diff --git a/Complexity/DataStructure/Stack/Controller.lean b/Complexity/DataStructure/Stack/Controller.lean index ad3c4a5656..fb44edb3cc 100644 --- a/Complexity/DataStructure/Stack/Controller.lean +++ b/Complexity/DataStructure/Stack/Controller.lean @@ -7,8 +7,6 @@ Authors: Samuel Schlesinger module -- upstream: candidate -public import Complexity.Computability.WordRAM.Function -public import Complexity.Computability.WordRAM.MapOp public import Complexity.Computability.WordRAM.Realization.Client.MemoryStack public import Complexity.Computability.WordRAM.Realization.Backend public import Complexity.Computability.WordRAM.Realization.Function @@ -174,7 +172,6 @@ public theorem program_spec {w n : ℕ} {s : Memory.State} (hf : Fits w (s.front rw [hlink, ← run_eq_link] at hvE hres hcost rw [habs', habs] at hres hcost have hrun := Stack.Memory.runClient_allocLayout_bind (w := w) 1 (fun L => run L n) s - have hrun0 := Stack.Memory.runClient_allocLayout_bind (w := 0) 1 (fun L => run L n) s refine ⟨?_, ?_, ?_⟩ · simp only [StateTransitionSpec.clientResult, program] at hres ⊢ rw [hrun, hres] @@ -184,9 +181,9 @@ public theorem program_spec {w n : ℕ} {s : Memory.State} (hf : Fits w (s.front exact ⟨hf.of_le (by omega), hf, hvE⟩ · rw [StateTransitionSpec.totalCost_const] at hcost have hlen := length_clientHistory_client w n - unfold memoryCost + rw [memoryCost_eq_totalCost w] simp only [StateTransitionSpec.clientHistory, program] - rw [hrun0, ← runClient_width w, ← totalCost_width w] + rw [hrun] change 2 + (1 + (Memory.spec w).totalCost Memory.charge ⟨clear s.mem s.frontier 1, s.frontier + 1 + 1⟩ ((Memory.spec w).clientHistory ⟨clear s.mem s.frontier 1, s.frontier + 1 + 1⟩ (run L n))) ≤ 15 diff --git a/Complexity/DataStructure/Stack/Native.lean b/Complexity/DataStructure/Stack/Native.lean index 67e7e79255..336e17169d 100644 --- a/Complexity/DataStructure/Stack/Native.lean +++ b/Complexity/DataStructure/Stack/Native.lean @@ -7,7 +7,6 @@ Authors: Samuel Schlesinger module -- upstream: candidate -public import Complexity.Computability.WordRAM.Heap public import Complexity.Computability.WordRAM.Realization.Backend public import Complexity.Computability.WordRAM.Realization.Client.MemoryStack @@ -23,23 +22,21 @@ with no result words. Correctness is that of `Stack.Memory.refinement`. Its memory charges are constant (`Stack.Memory.amortized`), so one constant, chosen before every width, capacity and layout, -bounds the instructions of every operation (`instruction_bound`), and an adaptive client with -`m` operations runs within `m` times that constant (`run_client`). Each operation writes only -the layout's cells (`modifies`, `frame`). +bounds the instructions of every operation (`instruction_bound`). An adaptive client with `m` +operations then runs within `m` times that constant (`Bound.exists_executes_const`). Each +operation leaves its method's final memory (`Backend.exec_memoryState`), so it writes only the +layout's cells (`Stack.Memory.modifies_methods`). ## Main definitions * `Tag`, `certificates`, `backend`: the operation constructors, their realizations, and the stack's operations on a layout. * `implementation`: the native stack. -* `initialLayout`, `initial`: an empty stack at a fixed address, as a calling-convention state. ## Main results * `implements`: the backend runs the methods of `Stack.Memory`. -* `instruction_bound`, `run_client`: a constant bound per operation. -* `modifies`, `frame`: operations write only the layout's cells. -* `initial_rep`: the initial state represents the empty stack. +* `instruction_bound`: a constant bound per operation. ## References @@ -121,61 +118,4 @@ public theorem instruction_bound : ∃ K, ∀ {w C : ℕ} {L : Stack.Memory.Layo Backend.bound (implements hL) (Stack.Memory.amortized hL) 19 fun o _ _ => by cases o <;> exact Nat.le_of_ble_eq_true rfl⟩ -/-- **Adaptive clients.** Every client whose history is admissible has a native execution, one -operation at a time, that returns the specified answer and represents the specified final stack, -within `K` instructions per operation. The client's own computation is outside the machine. -/ -public theorem run_client : ∃ K, ∀ {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C) - {α : Type} (p : (Stack.spec w C).Client α) {vs : List ℕ} - {x : Data (Backend.registers certificates)}, (implementation hL).rep vs x → - (Stack.spec w C).ValidHistory vs ((Stack.spec w C).clientHistory vs p) → - ∃ y traces, Executes ((backend w C L).exec w) p x ((Stack.spec w C).clientResult vs p) y - traces ∧ (implementation hL).rep ((Stack.spec w C).clientState vs p) y ∧ - total id traces ≤ ((Stack.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⟩ - -/-- Every operation writes only the layout's cells and keeps the frontier. -/ -public theorem modifies {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C) {vs : List ℕ} - {x y : Data (Backend.registers certificates)} {o : StackOp ℕ} - {a : (Stack.spec w C).Result o} {n : ℕ} (hr : (implementation hL).rep vs x) - (hv : (Stack.spec w C).valid vs o) (he : (backend w C L).exec w x o a y n) : - Modifies L.footprint x.mem y.mem ∧ - y.regs (Backend.frontier certificates) = x.regs (Backend.frontier certificates) := by - have hm : memoryState (Backend.frontier certificates) y = (Memory.spec w).clientState - (memoryState (Backend.frontier certificates) x) (Stack.Memory.methods L w C o) := - Backend.exec_memoryState (implements hL) hr hv he - rw [← ((Backend.rep_iff _ _ _).mp hr).2.1] at hv - obtain ⟨hmem, hfr⟩ := Stack.Memory.modifies_methods hL o hv - rw [← hm] at hmem hfr - exact ⟨hmem, hfr⟩ - -/-- Every operation preserves each heap assertion disjoint from the layout. -/ -public theorem frame {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C) {vs : List ℕ} - {x y : Data (Backend.registers certificates)} {o : StackOp ℕ} - {a : (Stack.spec w C).Result o} {n : ℕ} (hr : (implementation hL).rep vs x) - (hv : (Stack.spec w C).valid vs o) (he : (backend w C L).exec w x o a y n) - {Q : HeapAssertion} (hQ : Q.holds x.mem) (hd : Disjoint Q.footprint L.footprint) : - Q.holds y.mem := - HeapAssertion.frame hQ hd (modifies hL hr hv he).1 - -/-- The layout of a stack of capacity `C` at `base`: the count cell `base`, then the items. -/ -public def initialLayout (base C : ℕ) : Stack.Memory.Layout := ⟨⟨base, 1⟩, ⟨base + 1, C⟩⟩ - -/-- The initial layout is well formed when the end of the items fits a word. -/ -public theorem valid_initialLayout {w base C : ℕ} (hf : Fits w (base + 1 + C)) : - (initialLayout base C).Valid w C := Stack.Memory.valid_allocLayout hf - -/-- An empty stack of capacity `C` at `base`: zero memory, and the frontier just past the items. -This is a calling-convention state, not an uncharged allocation; `Stack.Memory.allocLayout` -allocates. -/ -public noncomputable def initial (base C : ℕ) : Data (Backend.registers certificates) := - Backend.data certificates ⟨fun _ => 0, base + 1 + C⟩ - -/-- The initial state represents the empty stack when the end of the items fits a word. -/ -public theorem initial_rep {w base C : ℕ} (hf : Fits w (base + 1 + C)) : - (implementation (valid_initialLayout hf)).rep [] (initial base C) := - Backend.rep_data _ ⟨hf, fun _ => fits_zero w⟩ - ⟨show base + 1 ≤ base + 1 + C by omega, le_rfl, Nat.zero_le _⟩ - (by simp [Stack.Memory.refinement, Stack.Memory.abs]) - end Cslib.DataStructure.Stack.Native diff --git a/ComplexityTests/DataStructureStack.lean b/ComplexityTests/DataStructureStack.lean deleted file mode 100644 index 0e8f913835..0000000000 --- a/ComplexityTests/DataStructureStack.lean +++ /dev/null @@ -1,49 +0,0 @@ -/- -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.DataStructure.Stack.Controller -import Complexity.DataStructure.Stack.Native - -/-! The bounded stack: admissibility at the boundary, an adaptive client through the native -stack from the initial state, and the compiled controller on concrete inputs. -/ - -namespace ComplexityTests.DataStructureStack - -open Cslib Cslib.WordRAM Cslib.DataStructure Cslib.DataStructure.Queue - -/-- A full stack rejects another push, while the largest fitting word remains a valid value. -/ -example : ¬(Stack.spec 4 2).valid [4, 7] (.push 2) ∧ (Stack.spec 4 2).valid [] (.push 15) := by - decide - -/-- An adaptive client chooses whether to pop from the observed size. -/ -def stackClient : (Stack.spec 4 2).Client (Option ℕ × ℕ) := - .liftBind (.push 7) fun _ => - .liftBind .size fun (n : ℕ) => - if n = 1 then - .liftBind .pop fun v => .liftBind .size fun (m : ℕ) => .pure (v, m) - else .pure (none, n) - -/-- The native stack runs the client from the initial state, one operation at a time: it pops -the pushed value, ends empty, and spends at most the constant `K` on each of its four calls. -/ -example : ∃ K, ∃ y traces, - Executes ((Stack.Native.backend 4 2 (Stack.Native.initialLayout 3 2)).exec 4) stackClient - (Stack.Native.initial 3 2) (some 7, 0) y traces ∧ - (Stack.Native.implementation - (Stack.Native.valid_initialLayout (w := 4) (base := 3) (C := 2) (by decide))).rep [] y ∧ - total id traces ≤ 4 * K := by - obtain ⟨K, hK⟩ := Stack.Native.run_client - exact ⟨K, hK _ stackClient (Stack.Native.initial_rep (by decide)) (by - change (0 < 2 ∧ Fits 4 7) ∧ True ∧ True ∧ True ∧ True - decide)⟩ - -/-- The compiled controller's memory program returns `max n 1`, here at both branches. -/ -example : (Memory.spec 4).clientResult (Realization.inputState [0]) (Stack.Controller.program 0) = - 1 ∧ - (Memory.spec 4).clientResult (Realization.inputState [5]) (Stack.Controller.program 5) = 5 := - ⟨(Stack.Controller.program_spec (by decide) (by decide)).1, - (Stack.Controller.program_spec (by decide) (by decide)).1⟩ - -end ComplexityTests.DataStructureStack diff --git a/ComplexityTests/WordRAMRealizationBackend.lean b/ComplexityTests/WordRAMRealizationBackend.lean index 11d8a3202f..265e2bfd7a 100644 --- a/ComplexityTests/WordRAMRealizationBackend.lean +++ b/ComplexityTests/WordRAMRealizationBackend.lean @@ -6,100 +6,22 @@ Authors: Samuel Schlesinger -- upstream: candidate -import Complexity.Computability.WordRAM.Realization.Backend -import Complexity.Computability.WordRAM.Realization.Client.MemoryStack +import Complexity.DataStructure.Stack.Native 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 +/-! The per-constructor backend on the array stack, `Stack.Native`. 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`. -/ +runtime argument of every tag. -/ open Cslib Cslib.DataStructure Cslib.WordRAM Cslib.WordRAM.Realization - Cslib.WordRAM.Realization.MemoryStack Cslib.DataStructure.Queue + Cslib.DataStructure.Queue Cslib.DataStructure.Stack.Native 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.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 - 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⟩ - -/-- The native stack on layout `L`. -/ -noncomputable def implementation {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C) : - StateTransitionImpl (Stack.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.spec w C).Client α) {vs : List ℕ} - {x : Data (Backend.registers certificates)}, - (implementation hL).rep vs x → - (Stack.spec w C).ValidHistory vs ((Stack.spec w C).clientHistory vs p) → - ∃ y traces, Executes ((backend w C L).exec w) p x - ((Stack.spec w C).clientResult vs p) y traces ∧ - (implementation hL).rep ((Stack.spec w C).clientState vs p) y ∧ - total id traces ≤ ((Stack.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⟩ +-- A full stack rejects another push, while the largest fitting word remains a valid value. +example : ¬(Stack.spec 4 2).valid [4, 7] (.push 2) ∧ (Stack.spec 4 2).valid [] (.push 15) := by + decide -- The push value never selects code: every push runs the body of the `push` tag. example (w C : ℕ) (L : Stack.Memory.Layout) : @@ -110,24 +32,48 @@ example (w C : ℕ) (L : Stack.Memory.Layout) : 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. +-- the method's final memory, so it writes only the layout's cells. example {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C) {vs : List ℕ} {x y : Data (Backend.registers certificates)} {o : StackOp ℕ} {a : (Stack.spec w C).Result o} {n : ℕ} (hr : (implementation hL).rep vs x) (hv : (Stack.spec w C).valid vs o) (he : (backend w C L).exec w x o a y n) : - a = ((Stack.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⟩ - --- 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) + a = ((Stack.spec w C).stepFn vs o).2 ∧ Modifies L.footprint x.mem y.mem := by + refine ⟨((implementation hL).correct_ofFn hr hv he).1, ?_⟩ + have hm : memoryState (Backend.frontier certificates) y = (Memory.spec w).clientState + (memoryState (Backend.frontier certificates) x) (Stack.Memory.methods L w C o) := + Backend.exec_memoryState (implements hL) hr hv he + rw [← ((Backend.rep_iff _ _ _).mp hr).2.1] at hv + have hmem := (Stack.Memory.modifies_methods hL o hv).1 + rw [← hm] at hmem + exact hmem + +/-- An empty stack of capacity two at address three, held in zero memory with the frontier at +six: the native data holding that memory state represents it. -/ +theorem empty_rep : (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]) +/-- An adaptive client chooses whether to pop from the observed size. -/ +def stackClient : (Stack.spec 4 2).Client (Option ℕ × ℕ) := + .liftBind (.push 7) fun _ => + .liftBind .size fun (n : ℕ) => + if n = 1 then + .liftBind .pop fun v => .liftBind .size fun (m : ℕ) => .pure (v, m) + else .pure (none, n) + +-- The native stack runs the client from the empty stack, one operation at a time: it pops the +-- pushed value, ends empty, and spends at most the constant `K` on each of its four calls. +example : ∃ K, ∃ y traces, + Executes ((backend 4 2 ⟨⟨3, 1⟩, ⟨4, 2⟩⟩).exec 4) stackClient + (Backend.data certificates ⟨fun _ => 0, 6⟩) (some 7, 0) y traces ∧ + total id traces ≤ 4 * K := by + obtain ⟨K, hK⟩ := instruction_bound + obtain ⟨y, traces, he, -, hb⟩ := Bound.exists_executes_const (hK _) stackClient empty_rep (by + change (0 < 2 ∧ Fits 4 7) ∧ True ∧ True ∧ True ∧ True + decide) + exact ⟨K, y, traces, he, hb⟩ + -- 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) : diff --git a/UPSTREAM.md b/UPSTREAM.md index f628392e39..f8215572ea 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -526,10 +526,10 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he 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. - `Stack.Native` runs those three certificates as a native stack with one constant - per-operation bound, and `Stack.Controller` compiles an adaptive stack client to one flat - program through `realize`; the hand-stepped stack dispatcher survives only inside Chan–He - traversal (`Traversal.WordStack`), which steps it with exact counts. + `Stack.Native` runs those three certificates through `Realization.Backend` as a native stack + with one constant per-operation bound, and `Stack.Controller` compiles an adaptive stack client + to one flat program through `realize`; the hand-stepped stack dispatcher survives only inside + Chan–He traversal (`Traversal.WordStack`), which steps it with exact counts. 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 42c2cb4840ccec0ba41525fd7440811de7c346cf Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 23:18:24 -0500 Subject: [PATCH 32/40] refactor(Stack): realize the controller in one call With #2280, realize certifies the whole controller program directly, so the split through realizes_alloc, realizes_run and Realizes.bind goes. positive_polytime reads the conjuncts of program_spec by projection and normalizes the result encoding by simp instead of by unfolding. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../DataStructure/Stack/Controller.lean | 51 +++++++------------ 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/Complexity/DataStructure/Stack/Controller.lean b/Complexity/DataStructure/Stack/Controller.lean index fb44edb3cc..f60ca58226 100644 --- a/Complexity/DataStructure/Stack/Controller.lean +++ b/Complexity/DataStructure/Stack/Controller.lean @@ -96,41 +96,23 @@ public def positive (n : ℕ) : ℕ := max n 1 local instance : WordEncoding Stack.Memory.Layout := ⟨4, layoutWords⟩ -private theorem realizes_alloc : Realizes .exact (fun _ : ℕ => Stack.Memory.allocLayout 1) - (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding layoutWords - (fun w _ s => Fits w (s.frontier + 2)) (fun _ _ => 0) := by - realize - exact h.of_le (by omega) - -private theorem realizes_run : Realizes .exact (fun d : ℕ × Stack.Memory.Layout => run d.2 d.1) - (((Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding.prodMap layoutWords).trans - (Fin.appendEquiv 1 4).toEmbedding) (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding - (fun w d s => d.2.Valid w 1 ∧ Stack.Memory.Inv d.2 s ∧ s.mem d.2.count.base = 0) - (fun _ _ => 0) := by - realize - all_goals - obtain ⟨hL, hI, h0⟩ := h - have h1 : Fits w 1 := hL.capacity_fits - have hne : d.2.items.base ≠ d.2.count.base := fun he => - hL.count_not_owns (he ▸ Block.owns_add (i := 0) (by rw [hL.items_length]; omega)) - have hlt : Fits w d.2.items.base := hmem.fits_of_lt_frontier (by - simpa using hI.item_lt (i := 0) (by rw [hL.items_length]; omega)) - simp only [StateTransitionSpec.clientState, StateTransitionSpec.clientResult, - Stack.Memory.runClient_push, h0, Nat.add_zero, Nat.zero_add, Function.update_self, - Function.update_of_ne hne, ne_eq, Nat.one_ne_zero, - not_false_eq_true, Stack.Memory.runClient_pop_of_pos, Nat.sub_self, optionWords_some, - Matrix.cons_val_zero, Matrix.cons_val_one] at * - all_goals first | exact hlt | exact h1 | exact le_rfl - /-- **The native certificate.** One uniform emitter, chosen before every width, input and memory, runs `program` from any memory whose frontier leaves room for the stack. -/ public theorem realizes_program : Realizes .exact program (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding - (fun w _ s => Fits w (s.frontier + 2)) (fun _ _ => 1) := by - refine (Realizes.bind realizes_alloc realizes_run).consequence ?_ (fun _ _ => le_rfl) - intro w n s _ _ hf - refine ⟨hf, Stack.Memory.valid_allocLayout hf, (Stack.Memory.inv_allocLayout s).1, ?_⟩ - exact clear_of_owns ⟨le_rfl, Nat.lt_add_one _⟩ + (fun w _ s => Fits w (s.frontier + 2)) (fun _ _ => 0) := by + realize + all_goals + try simp only [show (Memory.spec 0).clientResult s (Stack.Memory.allocLayout 1) = + ⟨⟨s.frontier, 1⟩, ⟨s.frontier + 1, 1⟩⟩ from rfl, + show (Memory.spec 0).clientState s (Stack.Memory.allocLayout 1) = + ⟨clear s.mem s.frontier 1, s.frontier + 1 + 1⟩ from rfl, + clear_of_owns (show (Block.mk s.frontier 1).Owns s.frontier from ⟨le_rfl, by simp⟩), + StateTransitionSpec.clientState, StateTransitionSpec.clientResult, + Stack.Memory.runClient_push, Stack.Memory.runClient_pop_of_pos, Nat.zero_add, + Nat.add_zero, Nat.sub_self, ne_eq, Nat.one_ne_zero, not_false_eq_true, + Function.update_self, optionWords_some, Matrix.cons_val_zero, Matrix.cons_val_one] at * + first | exact le_rfl | exact h.of_le (by omega) /-- The abstract client is admissible when its two values fit. -/ private theorem validHistory_client {w n : ℕ} (h1 : Fits w 1) (hn : Fits w n) : @@ -194,11 +176,12 @@ every width of at least three bits per input bit. -/ @[fun_prop, program_certificate] public theorem positive_polytime : UniformComputableInPolyTime .standard positive (fun _ n => [n]) id := - realizes_program.uniformComputableInPolyTime .exactStandard 0 (.const 16) 3 (by decide) + realizes_program.uniformComputableInPolyTime .exactStandard 0 (.const 15) 3 (by decide) (fun _ _ j => by fin_cases j; rfl) fun n w hw hin => by have h4 : Fits w 4 := (show Fits 3 4 by decide).mono (le_trans (by simp [logWidth]) hw) have hroom : Fits w ((inputState [n]).frontier + 2) := h4.of_le (by simp [inputState]) - obtain ⟨hres, hvalid, hcost⟩ := program_spec hroom (hin.2 n (by simp)) - exact ⟨by simp, h4.of_le (by simp), hroom, hvalid, hres, by omega⟩ + have hp := program_spec hroom (hin.2 n (by simp)) + refine ⟨by simp, h4.of_le (by simp), hroom, hp.2.1, ?_, by have := hp.2.2; omega⟩ + simpa only [Equiv.coe_toEmbedding, Equiv.funUnique_symm_apply, uniqueElim_const] using hp.1 end Cslib.DataStructure.Stack.Controller From 27bfd7c371bcc42d5dd45394cd5a7b900d96f6d9 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 23:23:06 -0500 Subject: [PATCH 33/40] perf(Stack): simplify only the controller's side goals The side goals after realize need no hypothesis rewriting; simp on the goal alone saves about 0.15 s and three simp arguments. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity/DataStructure/Stack/Controller.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Complexity/DataStructure/Stack/Controller.lean b/Complexity/DataStructure/Stack/Controller.lean index f60ca58226..d99bc0fcdc 100644 --- a/Complexity/DataStructure/Stack/Controller.lean +++ b/Complexity/DataStructure/Stack/Controller.lean @@ -111,7 +111,7 @@ public theorem realizes_program : Realizes .exact program StateTransitionSpec.clientState, StateTransitionSpec.clientResult, Stack.Memory.runClient_push, Stack.Memory.runClient_pop_of_pos, Nat.zero_add, Nat.add_zero, Nat.sub_self, ne_eq, Nat.one_ne_zero, not_false_eq_true, - Function.update_self, optionWords_some, Matrix.cons_val_zero, Matrix.cons_val_one] at * + Function.update_self] first | exact le_rfl | exact h.of_le (by omega) /-- The abstract client is admissible when its two values fit. -/ From cac0b0e3f42d6560e74ed522775af5ff2b3e3a40 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 23:58:35 -0500 Subject: [PATCH 34/40] chore: drop imports the merge duplicated in Complexity.lean Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Complexity.lean b/Complexity.lean index 1d42eb0c0c..b334a359d8 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2639,11 +2639,6 @@ 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.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 @@ -2969,8 +2964,6 @@ 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.Memory.Alloc public import Complexity.DataStructure.Memory.Field public import Complexity.DataStructure.Memory.Frame From f256f87c0d0ed53650b475fca25a724d34dbf86c Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Mon, 2 Nov 2026 00:07:15 -0500 Subject: [PATCH 35/40] feat(WordRAM): framed backends - frames, products over one memory, allocation A backend of a framed refinement carries its frame over: a native operation writes only the footprint and the reserved room (Backend.modifies), and keeps disjoint assertions and structures kept apart (frame_heap, frame). Backends of framed refinements run their product over one memory (Implements.prod_framed), each side within its own region (modifies_inl, modifies_inr). Backend.Allocation names the tag that runs a Memory.Allocator's program; Allocation.exec allocates from arbitrary bounded data within the allocator's cost, writing only the room it takes. Backend.extend adds such tags; install and exists_exec run any tag's body. The memory side is a new leaf, Memory/Region. The stack test restores modifies/frame, constant-time allocation and allocate-then-run, and two stacks over one memory. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 2 + .../WordRAM/Realization/Backend.lean | 107 ++++++--- .../WordRAM/Realization/BackendFrame.lean | 227 ++++++++++++++++++ Complexity/DataStructure/Memory/Region.lean | 85 +++++++ ComplexityTests/WordRAMRealizationFramed.lean | 200 +++++++++++++++ ROADMAP.md | 9 +- 6 files changed, 594 insertions(+), 36 deletions(-) create mode 100644 Complexity/Computability/WordRAM/Realization/BackendFrame.lean create mode 100644 Complexity/DataStructure/Memory/Region.lean create mode 100644 ComplexityTests/WordRAMRealizationFramed.lean diff --git a/Complexity.lean b/Complexity.lean index b334a359d8..104dea1910 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2624,6 +2624,7 @@ 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.BackendFrame public import Complexity.Computability.WordRAM.Realization.Basic public import Complexity.Computability.WordRAM.Realization.Bind public import Complexity.Computability.WordRAM.Realization.Branch @@ -2968,6 +2969,7 @@ public import Complexity.DataStructure.Memory.Alloc public import Complexity.DataStructure.Memory.Field public import Complexity.DataStructure.Memory.Frame public import Complexity.DataStructure.Memory.Record +public import Complexity.DataStructure.Memory.Region public import Complexity.DataStructure.Memory.Spec public import Complexity.DataStructure.Memory.Triple public import Complexity.DataStructure.ObliviousSequence diff --git a/Complexity/Computability/WordRAM/Realization/Backend.lean b/Complexity/Computability/WordRAM/Realization/Backend.lean index 881155547c..112bbe5717 100644 --- a/Complexity/Computability/WordRAM/Realization/Backend.lean +++ b/Complexity/Computability/WordRAM/Realization/Backend.lean @@ -51,9 +51,11 @@ so `memoryCost`, which is taken at width zero, is the charge that refinements st * `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.install`: a tag's arguments installed in its argument registers. * `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.extend`: a backend with further tags, such as an allocation's. * `Backend.exec`, `Backend.implementation`: native execution, and the native implementation. ## Main results @@ -61,6 +63,7 @@ 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.exists_exec`: the body of one tag runs its program on installed arguments. * `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. @@ -216,6 +219,58 @@ private theorem layout (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 +/-- Install arguments `d` of tag `i` in its argument registers. Installation is uncharged and +leaves memory and the frontier unchanged. -/ +def install (i : ι) (d : (C i).Arg) (x : Data (registers C)) : Data (registers C) where + regs j := if h : 0 < j.val ∧ j.val ≤ (C i).k then (C i).input d ⟨j.val - 1, by omega⟩ + else x.regs j + mem := x.mem + +/-- Installation leaves the memory and the frontier. -/ +@[simp] theorem memoryState_install (i : ι) (d : (C i).Arg) (x : Data (registers C)) : + memoryState (frontier C) (install C i d x) = memoryState (frontier C) x := by + apply Memory.State.ext rfl + simp [memoryState, install, frontier] + +private theorem install_src (i : ι) (d : (C i).Arg) (x : Data (registers C)) : + (install C i d x).regs ∘ src C i = (C i).input d := by + funext j + simp [install, src] + +private theorem install_bounded {w : ℕ} (i : ι) (d : (C i).Arg) {x : Data (registers C)} + (hx : x.Bounded w) (hin : ∀ j, Fits w ((C i).input d j)) : (install C i d x).Bounded w := by + refine ⟨fun j => ?_, hx.2⟩ + simp only [install] + split + · exact hin _ + · exact hx.1 j + +/-- **The body of one tag.** From bounded data, with arguments that fit words, inside the tag's +domain and with an admissible memory history, the body of tag `i` runs its program on the +installed arguments within the certified bound at the common exchange factor. It leaves the +program's final memory and encoded result, and every register and cell a word. -/ +theorem exists_exec {w : ℕ} (i : ι) (d : (C i).Arg) {x : Data (registers C)} (hx : x.Bounded w) + (hin : ∀ j, Fits w ((C i).input d j)) (hsafe : (C i).safe w d (memoryState (frontier C) x)) + (hv : (Memory.spec w).ValidHistory (memoryState (frontier C) x) + ((Memory.spec w).clientHistory (memoryState (frontier C) x) ((C i).prog d))) : + ∃ y n, Exec w (code C i) (install C i d x) y n ∧ + n ≤ factor C * (memoryCost ((C i).prog d) (memoryState (frontier C) x) + + (C i).work d (memoryState (frontier C) x) + 1) ∧ + memoryState (frontier C) y = + (Memory.spec w).clientState (memoryState (frontier C) x) ((C i).prog d) ∧ + (∀ j, y.regs (tmp C i (Fin.castAdd ((C i).realizes.scratchSize + 1) j)) = + (C i).output ((Memory.spec w).clientResult (memoryState (frontier C) x) ((C i).prog d)) + j) ∧ + y.Bounded w := by + have hb := install_bounded C i d hx hin + have hm := memoryState_install C i d x + obtain ⟨hi', hd, hf, hs⟩ := layout C i + obtain ⟨y, ⟨n, hn, he⟩, hm', hout, -⟩ := + ((C i).realizes.code_spec (frontier C) (src C i) (tmp C i) hi' hd hf hs).2 w d _ hb + (install_src C i d x) (hm ▸ hsafe) (hm ▸ hv) + rw [hm] at hn hm' hout + exact ⟨y, n, he, hn.trans (Nat.mul_le_mul_right _ (factor_le C i)), hm', hout, he.bounded hb⟩ + end Registers variable {C : ι → Certificate B} {D : StateTransitionSpec.{0, 0, 0}} (S : Backend D C) @@ -266,6 +321,19 @@ theorem Implements.prod {w : ℕ} {S : Backend D C} {S' : Backend D' C'} | .inl o, hw, hi, hv => hS.entry o hw hi.1 hv | .inr o, hw, hi, hv => hS'.entry o hw hi.2.1 hv +/-- The backend with the tags of a second family added, such as a realized allocation's +(`Backend.Allocation`). Every operation selects the body it selects in `S`. -/ +def extend (C' : ι' → Certificate B) : Backend D (Sum.elim C C') where + tag o := .inl (S.tag o) + arg := S.arg + answer := S.answer + +/-- An extended backend runs the methods its original runs. -/ +theorem Implements.extend {w : ℕ} {S : Backend D C} {R : Refinement D (Memory.spec w)} + (hS : S.Implements R) (C' : ι' → Certificate B) : (S.extend C').Implements R where + run := hS.run + entry := hS.entry + end Sum /-! ### Native execution -/ @@ -274,11 +342,8 @@ 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 +def prepare (o : D.Op) (x : Data (registers C)) : Data (registers C) := + install C (S.tag o) (S.arg o) x /-- 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. -/ @@ -296,24 +361,6 @@ theorem exec_iff (w : ℕ) (x y : Data (registers C)) (o : D.Op) (a : D.Result o 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)} @@ -339,20 +386,14 @@ private theorem exists_body (hS : S.Implements R) (o : D.Op) {x : Data (register 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⟩ + obtain ⟨y, n, he, hn, hm, hout, hb⟩ := + exists_exec C (S.tag o) (S.arg o) hx hin hsafe (hhist ▸ hvalid) + refine ⟨y, n, he, hn.trans_eq ?_, hm.trans (congrArg (fun t => t.2.1) hrun).symm, hout, + (congrArg Prod.fst hrun).symm, 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)} diff --git a/Complexity/Computability/WordRAM/Realization/BackendFrame.lean b/Complexity/Computability/WordRAM/Realization/BackendFrame.lean new file mode 100644 index 0000000000..03c993974c --- /dev/null +++ b/Complexity/Computability/WordRAM/Realization/BackendFrame.lean @@ -0,0 +1,227 @@ +/- +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.Backend +public import Complexity.DataStructure.Memory.Region + +/-! +# Framed backends: frames, products over one memory, and allocation + +A backend that implements a framed refinement (`Memory.Framed`) inherits its frame. A native +operation leaves its method's final memory (`Backend.exec_memoryState`), so it writes only the +refinement's region, its footprint and the room reserved above the frontier +(`Backend.modifies`). An assertion on other cells survives it (`Backend.frame_heap`), and so +does every framed structure kept apart from it (`Backend.frame`). Backends of two framed +refinements run their product over one memory (`Implements.prod_framed`). The product is framed +again by the union of the footprints and the sum of the rooms, so products iterate, and +`Memory.Framed.amortized_prod` adds their costs for `Backend.amortized`. + +**Allocation.** An allocator (`Memory.Allocator`) sets a structure up in memory with a program +that returns a handle, such as a layout of named fields. A `Backend.Allocation` names the tag +whose body runs that program, and reads the handle off the body's result. From arbitrary bounded +data with the allocator's precondition and room, the body runs within the certified bound, +returns the handle, writes only the room it takes above the frontier, and leaves data that the +handle's backend represents at the allocator's initial state (`Allocation.exec`). The bound is +the allocator's memory cost and the body's local work. When the allocation reserves memory +without clearing it (`Memory.allocDirty`), as the array stack does, both are constant. + +**Base-relative layouts.** The handle is a runtime value. A family of backends +`F : A.Handle → Backend D C` passes it to its bodies as an argument, and a body is chosen by its +tag alone (`Backend.code`), so one body serves every layout. Bounds stated for every handle are +therefore uniform in the structure's base address. + +The frame is that of the memory-level refinement (`Memory.Framed.region`). The frame argument +follows the block frame of #2273 (Codex), and allocation without clearing follows the queue +allocation of #2271 (Codex). + +## Main definitions + +* `Backend.Allocation`: the tag, argument and handle of a realized allocation. + +## Main results + +* `Backend.clientRun_exec`: a native operation runs its method between the memories it holds. +* `Backend.modifies`: a native operation writes only its structure's region. +* `Backend.frame_heap`, `Backend.frame`: disjoint assertions, and structures kept apart, survive. +* `Backend.Implements.prod_framed`: backends of framed refinements run their product over one + memory. +* `Backend.modifies_inl`, `Backend.modifies_inr`: each operation of a product writes only its own + structure's region. +* `Backend.Allocation.exec`: allocation from arbitrary memory, within the allocator's cost. +-/ + +public section + +namespace Cslib.WordRAM.Realization.Backend + +open Cslib.DataStructure + +variable {B : Basis} {ι : Type} {C : ι → Certificate B} {D : StateTransitionSpec.{0, 0, 0}} + {w : ℕ} {S : Backend D C} {M : Memory.Framed D w} + +/-! ## The frame of a native operation -/ + +section Frame + +variable [Fintype ι] + +/-- A native operation runs its method from the memory it holds to the memory it leaves. -/ +theorem clientRun_exec (hS : S.Implements M.toRefinement) {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) : + ∃ b tr, (Memory.spec w).ClientRun (memoryState (frontier C) x) (M.methods o) b + (memoryState (frontier C) y) tr := by + have hm := exec_memoryState hS hr hv he + obtain ⟨hi, rfl, -⟩ := (rep_iff hS s x).mp hr + rw [hm] + exact ⟨_, _, (Memory.spec w).clientRun_canonical _ _ + (((Memory.spec w).demonicValid_iff_validHistory_runClient _ _).mp (M.valid o hi hv))⟩ + +/-- **The frame of a native operation.** An operation of a backend that implements a framed +refinement writes only the refinement's region: its footprint, and the room reserved above the +frontier. Without room (`Memory.Framed.region_eq_footprint`) it writes only the footprint. -/ +theorem modifies (hS : S.Implements M.toRefinement) {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) : Modifies (M.region (memoryState (frontier C) x)) x.mem y.mem := by + obtain ⟨b, tr, hrun⟩ := clientRun_exec hS hr hv he + obtain ⟨hi, rfl, -⟩ := (rep_iff hS s x).mp hr + exact fun c hc => M.mem_eq_of_not_mem_region o hi hv hrun hc + +/-- A native operation keeps every assertion on cells outside its structure's region. -/ +theorem frame_heap (hS : S.Implements M.toRefinement) {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) {Q : HeapAssertion} (hQ : Q.holds x.mem) + (hd : Disjoint Q.footprint (M.region (memoryState (frontier C) x))) : Q.holds y.mem := + HeapAssertion.frame hQ hd (modifies hS hr hv he) + +/-- **A native operation keeps a structure apart from its own**: the other structure's +abstraction and invariant are unchanged, and the two stay apart, with disjoint footprints and +room for both. -/ +theorem frame (hS : S.Implements M.toRefinement) {D' : StateTransitionSpec.{0, 0, 0}} + (N : Memory.Framed D' w) {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) (hN : N.inv (memoryState (frontier C) x)) + (hJ : M.Apart N (memoryState (frontier C) x)) : + N.abs (memoryState (frontier C) y) = N.abs (memoryState (frontier C) x) ∧ + N.inv (memoryState (frontier C) y) ∧ M.Apart N (memoryState (frontier C) y) := by + obtain ⟨b, tr, hrun⟩ := clientRun_exec hS hr hv he + obtain ⟨hi, rfl, -⟩ := (rep_iff hS s x).mp hr + exact Memory.Framed.prod_frame_left M N o hi hN hJ hv hrun + +end Frame + +/-! ## Products over one memory -/ + +section Product + +variable {ι' : Type} {C' : ι' → Certificate B} {D' : StateTransitionSpec.{0, 0, 0}} + {S' : Backend D' C'} {M' : Memory.Framed D' w} + +/-- **Products over one memory.** Backends of two framed refinements run their product +(`Memory.Framed.prod`), whose frame is the union of the footprints and the sum of the rooms. -/ +theorem Implements.prod_framed (hS : S.Implements M.toRefinement) + (hS' : S'.Implements M'.toRefinement) : (S.sum S').Implements (M.prod M').toRefinement := + hS.prod (frame₁ := Memory.Framed.prod_frame_left M M') + (frame₂ := Memory.Framed.prod_frame_right M M') hS' + +variable [Fintype ι] [Fintype ι'] + +/-- **Each side keeps its own frame.** An operation of the left structure of a product writes +only that structure's region. -/ +theorem modifies_inl (hS : S.Implements M.toRefinement) (hS' : S'.Implements M'.toRefinement) + {s : (product D D').State} {x y : Data (registers (Sum.elim C C'))} {o : D.Op} + {a : (product D D').Result (.inl o)} {n : ℕ} + (hr : (implementation (hS.prod_framed hS')).rep s x) (hv : (product D D').valid s (.inl o)) + (he : (S.sum S').exec w x (.inl o) a y n) : + Modifies (M.region (memoryState (frontier (Sum.elim C C')) x)) x.mem y.mem := by + obtain ⟨b, tr, hrun⟩ := clientRun_exec (hS.prod_framed hS') hr hv he + obtain ⟨⟨hi, -, -⟩, rfl, -⟩ := (rep_iff _ s x).mp hr + exact fun c hc => M.mem_eq_of_not_mem_region o hi hv hrun hc + +/-- An operation of the right structure of a product writes only that structure's region. -/ +theorem modifies_inr (hS : S.Implements M.toRefinement) (hS' : S'.Implements M'.toRefinement) + {s : (product D D').State} {x y : Data (registers (Sum.elim C C'))} {o : D'.Op} + {a : (product D D').Result (.inr o)} {n : ℕ} + (hr : (implementation (hS.prod_framed hS')).rep s x) (hv : (product D D').valid s (.inr o)) + (he : (S.sum S').exec w x (.inr o) a y n) : + Modifies (M'.region (memoryState (frontier (Sum.elim C C')) x)) x.mem y.mem := by + obtain ⟨b, tr, hrun⟩ := clientRun_exec (hS.prod_framed hS') hr hv he + obtain ⟨⟨-, hi, -⟩, rfl, -⟩ := (rep_iff _ s x).mp hr + exact fun c hc => M'.mem_eq_of_not_mem_region o hi hv hrun hc + +end Product + +/-! ## Allocation -/ + +/-- **A realized allocation** for the allocator `A`: the body of tag `tag`, on arguments `arg`, +runs the allocation program, and `handle` reads the allocated handle off its result. From memory +the allocator can allocate from, the arguments fit words and the tag's domain holds. -/ +structure Allocation (A : Memory.Allocator D w) (C : ι → Certificate B) where + /-- The tag whose body allocates. -/ + tag : ι + /-- Its arguments, such as a capacity. -/ + arg : (C tag).Arg + /-- The handle, read off the body's result. -/ + handle : (C tag).Res → A.Handle + /-- The tag's program is the allocation, with the handle read off its result. -/ + run : ∀ s, (Memory.spec w).runClient s A.alloc = + Prod.map handle id ((Memory.spec w).runClient s ((C tag).prog arg)) + /-- From memory the allocator can allocate from, the arguments fit words and the tag's domain + holds. -/ + entry : ∀ {s}, Memory.WellFormed w s → A.Pre s → Fits w (s.frontier + A.room) → + (∀ j, Fits w ((C tag).input arg j)) ∧ (C tag).safe w arg s + +variable [Fintype ι] + +/-- **Allocation from arbitrary memory.** From bounded data whose memory the allocator can +allocate from, with its room fitting a word, the allocation body runs within +`factor C * (A.cost + work + 1)` instructions, where `work` is its local work. It returns the +allocated handle in its result registers, writes only the room it takes above the frontier, and +leaves data that the handle's backend represents at the allocator's initial state. -/ +theorem Allocation.exec {A : Memory.Allocator D w} (I : Allocation A C) + {F : A.Handle → Backend D C} + (hF : ∀ s (hs : A.Pre s) (hroom : Fits w (s.frontier + A.room)), + (F ((Memory.spec w).clientResult s A.alloc)).Implements (A.framed s hs hroom).toRefinement) + {x : Data (registers C)} (hx : x.Bounded w) (hs : A.Pre (memoryState (frontier C) x)) + (hroom : Fits w ((memoryState (frontier C) x).frontier + A.room)) : + ∃ y n b, Exec w (code C I.tag) (install C I.tag I.arg x) y n ∧ + n ≤ factor C * (A.cost + (C I.tag).work I.arg (memoryState (frontier C) x) + 1) ∧ + (∀ j, y.regs (tmp C I.tag (Fin.castAdd ((C I.tag).realizes.scratchSize + 1) j)) = + (C I.tag).output b j) ∧ + I.handle b = (Memory.spec w).clientResult (memoryState (frontier C) x) A.alloc ∧ + Modifies (Finset.Ico (memoryState (frontier C) x).frontier + ((memoryState (frontier C) x).frontier + A.room)) x.mem y.mem ∧ + (implementation (hF _ hs hroom)).rep A.init y := by + have hal := A.allocates _ hs hroom + have hrun := I.run (memoryState (frontier C) x) + have hhist : (Memory.spec w).clientHistory (memoryState (frontier C) x) A.alloc = + (Memory.spec w).clientHistory (memoryState (frontier C) x) ((C I.tag).prog I.arg) := + congrArg (fun t => t.2.2) hrun + have hstate : (Memory.spec w).clientState (memoryState (frontier C) x) A.alloc = + (Memory.spec w).clientState (memoryState (frontier C) x) ((C I.tag).prog I.arg) := + congrArg (fun t => t.2.1) hrun + obtain ⟨hin, hsafe⟩ := I.entry (wellFormed_memoryState hx) hs hroom + obtain ⟨y, n, hex, hn, hm, hout, hb⟩ := + exists_exec C I.tag I.arg hx hin hsafe (hhist ▸ hal.validHistory) + have hy : memoryState (frontier C) y = + (Memory.spec w).clientState (memoryState (frontier C) x) A.alloc := hm.trans hstate.symm + refine ⟨y, n, _, hex, hn.trans (Nat.mul_le_mul_left _ ?_), hout, + (congrArg Prod.fst hrun).symm, fun c hc => ?_, (rep_iff _ _ _).mpr ⟨?_, ?_, hb⟩⟩ + · have := hal.cost_le + rw [memoryCost_eq_totalCost w, ← hhist] + omega + · have := hal.mem_eq_of_not_mem hc + rw [← hy] at this + exact this + · rw [hy] + exact hal.inv + · rw [hy] + exact hal.abs_eq + +end Cslib.WordRAM.Realization.Backend diff --git a/Complexity/DataStructure/Memory/Region.lean b/Complexity/DataStructure/Memory/Region.lean new file mode 100644 index 0000000000..f4f48bfd43 --- /dev/null +++ b/Complexity/DataStructure/Memory/Region.lean @@ -0,0 +1,85 @@ +/- +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.Alloc + +/-! +# The cells a framed method or an allocation writes + +A method run of a framed refinement changes an allocated cell only in its footprint, and it +advances the frontier by at most the room it gives up, so the cells it allocates lie in the room +reserved above the old frontier. An admissible run changes no cell at or above its final +frontier. So a method run writes only the footprint and the reserved room, the region of the +structure (`Framed.region`, `Framed.mem_eq_of_not_mem_region`). An allocation changes no +allocated cell, and allocates only within the room it takes, so it writes only that room +(`Allocates.mem_eq_of_not_mem`). These are the memory side of the native frame theorems of +`Realization.Backend`. + +The argument is the block frame of `Realization.product_exec` in #2273 (Codex), stated here once +for every framed refinement. + +## Main definitions + +* `Memory.Framed.region`: the footprint and the room reserved above the frontier. + +## Main results + +* `Memory.Framed.mem_eq_of_not_mem_region`: a method run writes only the region. +* `Memory.Framed.region_eq_footprint`: without room, the region is the footprint. +* `Memory.Allocates.mem_eq_of_not_mem`: an allocation writes only the room it takes. +-/ + +@[expose] public section + +namespace Cslib.DataStructure.Memory + +open Cslib.WordRAM + +variable {w : ℕ} {D : StateTransitionSpec.{0, 0, 0}} + +/-- The cells that a method run of `M` from `s` may write: the footprint, and the room reserved +above the frontier. -/ +def Framed.region (M : Framed D w) (s : State) : Finset ℕ := + M.footprint s ∪ Finset.Ico s.frontier (s.frontier + M.room s) + +/-- Without room, the region is the footprint. -/ +theorem Framed.region_eq_footprint (M : Framed D w) {s : State} (h : M.room s = 0) : + M.region s = M.footprint s := by + simp [Framed.region, h] + +/-- **A method run writes only the region**: an allocated cell outside the footprint is kept +by `Framed.modifies`, and a cell beyond the reserved room lies at or above the final frontier. -/ +theorem Framed.mem_eq_of_not_mem_region (M : Framed D w) {s : State} (o : D.Op) + {a : D.Result o} {s' : State} {tr : (spec w).Transcript} (hs : M.inv s) + (hv : D.valid (M.abs s) o) (hrun : (spec w).ClientRun s (M.methods o) a s' tr) {b : ℕ} + (hb : b ∉ M.region s) : s'.mem b = s.mem b := by + obtain ⟨hmod, -, hroom⟩ := M.modifies o hs hv hrun + simp only [Framed.region, Finset.mem_union, Finset.mem_Ico, not_or, not_and, not_lt] at hb + obtain ⟨hfp, hico⟩ := hb + by_cases hlt : b < s.frontier + · exact hmod b hlt hfp + · obtain ⟨hvh, -, rfl, -⟩ := ((spec w).clientRun_iff_runClient _ _ _ _ _).mp hrun + have := hico (Nat.le_of_not_lt hlt) + exact mem_clientState_of_frontier_le s _ hvh (by omega) + +/-- **An allocation writes only the room it takes**: it keeps every allocated cell, and every +cell beyond its room lies at or above its final frontier. -/ +theorem Allocates.mem_eq_of_not_mem {H : Type} {alloc : Prog H} {methods : H → Methods D (spec w)} + {init : D.State} {owned : Finset ℕ} {room cost : ℕ} {s : State} {M : Framed D w} + (h : Allocates alloc methods init owned room cost s M) {b : ℕ} + (hb : b ∉ Finset.Ico s.frontier (s.frontier + room)) : + ((spec w).clientState s alloc).mem b = s.mem b := by + rw [Finset.mem_Ico, not_and, not_lt] at hb + by_cases hlt : b < s.frontier + · exact h.mem_eq b hlt + · have := h.room_le + have := hb (Nat.le_of_not_lt hlt) + exact mem_clientState_of_frontier_le s alloc h.validHistory (by omega) + +end Cslib.DataStructure.Memory diff --git a/ComplexityTests/WordRAMRealizationFramed.lean b/ComplexityTests/WordRAMRealizationFramed.lean new file mode 100644 index 0000000000..ce1cf7a20b --- /dev/null +++ b/ComplexityTests/WordRAMRealizationFramed.lean @@ -0,0 +1,200 @@ +/- +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.BackendFrame +import Complexity.Tactic.Realize +import ComplexityTests.WordRAMRealizationBackend + +/-! The array stack's backend from `WordRAMRealizationBackend`, framed. Its operations write only +their layout's cells (`modifies`, `frame`), as the hand-stepped `Stack.WordRAM.modifies` and +`frame` did. An allocation tag, added by `Backend.extend`, sets up an empty stack from arbitrary +bounded data in constant time, clearing only the count cell (`allocate`), and a client then runs +on it (`run_allocated`). Two stacks share one memory with two frames (`modifies_left`, +`instruction_bound_pair`). -/ + +open Cslib Cslib.DataStructure Cslib.WordRAM Cslib.WordRAM.Realization + Cslib.WordRAM.Realization.MemoryStack Cslib.DataStructure.Queue + +namespace ComplexityTests.WordRAMRealizationFramed + +open ComplexityTests.WordRAMRealizationBackend (Tag certificates backend implements) + +/-- The stack's operations implement the framed stack. -/ +theorem implements_framed {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C) : + (backend w C L).Implements (Stack.Memory.framed hL).toRefinement := + implements hL + +/-! ## The frame of the stack -/ + +/-- **Modifies.** A native stack operation writes only its layout's cells. -/ +theorem modifies {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 : (Backend.implementation (implements_framed 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) : + Modifies L.footprint x.mem y.mem := by + have h := Backend.modifies (implements_framed hL) hr hv he + rwa [Memory.Framed.region_eq_footprint _ rfl, Stack.Memory.framed_footprint] at h + +/-- **Frame.** A native stack operation keeps every assertion on cells outside its layout. -/ +theorem frame {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 : (Backend.implementation (implements_framed 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) + {Q : HeapAssertion} (hQ : Q.holds x.mem) (hd : Disjoint Q.footprint L.footprint) : + Q.holds y.mem := + HeapAssertion.frame hQ hd (modifies hL hr hv he) + +/-! ## Allocation from dirty memory -/ + +/-- Allocating a stack of capacity `C` zeroes one count cell and leaves the item array as it +was. -/ +theorem realizes_allocLayout : Realizes .exact Stack.Memory.allocLayout + (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding layoutWords + (fun w C s => Fits w (s.frontier + 1 + C)) (fun _ _ => 0) := by + realize + all_goals exact h.of_le (by omega) + +/-- The allocation's body. -/ +noncomputable def allocCertificate : Unit → Certificate .exact := fun _ => ⟨realizes_allocLayout⟩ + +/-- The stack's bodies and the allocation's. -/ +noncomputable abbrev withAlloc : Tag ⊕ Unit → Certificate .exact := + Sum.elim certificates allocCertificate + +/-- The stack on layout `L`, with the allocation tag. -/ +noncomputable def allocated (w C : ℕ) (L : Stack.Memory.Layout) : + Backend (Stack.WordRAM.spec w C) withAlloc := + (backend w C L).extend allocCertificate + +/-- The allocation tag allocates a stack of capacity `C` and returns its layout. -/ +noncomputable def allocation (w C : ℕ) : + Backend.Allocation (Stack.Memory.allocator w C) withAlloc where + tag := .inr () + arg := C + handle := id + run _ := rfl + entry _ _ hroom := by + change Fits w (_ + (C + 1)) at hroom + exact ⟨fun _ => (hroom.of_le (by omega) : Fits w C), hroom.of_le (by omega)⟩ + +/-- The layout that allocation returns at frontier `f`. -/ +def layoutAt (f C : ℕ) : Stack.Memory.Layout := ⟨⟨f, 1⟩, ⟨f + 1, C⟩⟩ + +/-- **Allocation in constant time.** One constant `K`, chosen before every width, capacity and +memory, bounds the allocation of an empty stack from any bounded data whose frontier leaves room +for it. It writes only the `C + 1` cells above the frontier, returns the layout, and leaves data +that represents the empty stack on it. -/ +theorem allocate : ∃ K, ∀ {w C : ℕ} {x : Data (Backend.registers withAlloc)}, x.Bounded w → + ∀ (hroom : Fits w (x.regs (Backend.frontier withAlloc) + 1 + C)), + ∃ y n, Exec w (Backend.code withAlloc (.inr ())) (Backend.install withAlloc (.inr ()) C x) + y n ∧ n ≤ K ∧ + (∀ j, y.regs (Backend.tmp withAlloc (.inr ()) + (Fin.castAdd ((withAlloc (.inr ())).realizes.scratchSize + 1) j)) = + (withAlloc (.inr ())).output (layoutAt (x.regs (Backend.frontier withAlloc)) C) j) ∧ + Modifies (Finset.Ico (x.regs (Backend.frontier withAlloc)) + (x.regs (Backend.frontier withAlloc) + (C + 1))) x.mem y.mem ∧ + (Backend.implementation + ((implements_framed (Stack.Memory.valid_allocLayout hroom)).extend allocCertificate)).rep + [] y := by + refine ⟨Backend.factor withAlloc * 4, fun {w C x} hx hroom => ?_⟩ + obtain ⟨y, n, b, he, hn, hout, hb, hmod, hrep⟩ := (allocation w C).exec (F := allocated w C) + (fun s _ hr => (implements_framed (Stack.Memory.valid_allocLayout + ((show Fits w (s.frontier + (C + 1)) from hr).of_le (by omega)))).extend allocCertificate) + hx trivial (show Fits w (x.regs (Backend.frontier withAlloc) + (C + 1)) from + hroom.of_le (by omega)) + cases hb + exact ⟨y, n, he, hn, hout, hmod, hrep⟩ + +/-- Every operation of the stack with the allocation tag has one constant bound. -/ +theorem instruction_bound : ∃ K, ∀ {w C : ℕ} {L : Stack.Memory.Layout} (hL : L.Valid w C), + Bound (Backend.implementation ((implements_framed hL).extend allocCertificate)) id + fun _ _ => K := + ⟨Backend.factor withAlloc * 23, fun hL => + Backend.bound ((implements_framed hL).extend allocCertificate) (Stack.Memory.amortized hL) 19 + fun o _ _ => by + cases o <;> exact Nat.le_of_ble_eq_true rfl⟩ + +/-- **Allocate, then run any admissible client**, as the hand-stepped `run_initialized`: from +arbitrary bounded data with room, the allocation and `m` operations take at most `K₀ + m * K` +instructions. -/ +theorem run_allocated : ∃ K₀ K, ∀ {w C : ℕ} {x : Data (Backend.registers withAlloc)}, + x.Bounded w → Fits w (x.regs (Backend.frontier withAlloc) + 1 + C) → ∀ {α : Type} + (p : (Stack.WordRAM.spec w C).Client α), + (Stack.WordRAM.spec w C).ValidHistory [] ((Stack.WordRAM.spec w C).clientHistory [] p) → + ∃ ready y n traces, + Exec w (Backend.code withAlloc (.inr ())) (Backend.install withAlloc (.inr ()) C x) ready n ∧ + Executes ((allocated w C (layoutAt (x.regs (Backend.frontier withAlloc)) C)).exec w) p ready + ((Stack.WordRAM.spec w C).clientResult [] p) y traces ∧ + n + total id traces ≤ K₀ + ((Stack.WordRAM.spec w C).clientHistory [] p).length * K := by + obtain ⟨K₀, h₀⟩ := allocate + obtain ⟨K, hK⟩ := instruction_bound + refine ⟨K₀, K, fun {w C x} hx hroom {α} p hv => ?_⟩ + obtain ⟨ready, n, he, hn, -, -, hrep⟩ := h₀ hx hroom + obtain ⟨y, traces, hex, -, htot⟩ := Bound.exists_executes_const (hK _) p hrep hv + exact ⟨ready, y, n, traces, he, hex, by omega⟩ + +/-! ## Two stacks in one memory -/ + +/-- Two stacks run as the product of two stacks over one memory, each operation selecting a body +of its own side. -/ +theorem implements_pair {w C : ℕ} {L₁ L₂ : Stack.Memory.Layout} (h₁ : L₁.Valid w C) + (h₂ : L₂.Valid w C) : + ((backend w C L₁).sum (backend w C L₂)).Implements + ((Stack.Memory.framed h₁).prod (Stack.Memory.framed h₂)).toRefinement := + (implements_framed h₁).prod_framed (implements_framed h₂) + +/-- **Two frames.** An operation of the left stack writes only the left layout's cells. -/ +theorem modifies_left {w C : ℕ} {L₁ L₂ : Stack.Memory.Layout} (h₁ : L₁.Valid w C) + (h₂ : L₂.Valid w C) {vs₁ vs₂ : List ℕ} + {x y : Data (Backend.registers (Sum.elim certificates certificates))} {o : StackOp ℕ} + {a : (Stack.WordRAM.spec w C).Result o} {n : ℕ} + (hr : (Backend.implementation (implements_pair h₁ h₂)).rep (vs₁, vs₂) x) + (hv : (Stack.WordRAM.spec w C).valid vs₁ o) + (he : ((backend w C L₁).sum (backend w C L₂)).exec w x (.inl o) a y n) : + Modifies L₁.footprint x.mem y.mem := by + have h := Backend.modifies_inl (implements_framed h₁) (implements_framed h₂) hr hv he + rwa [Memory.Framed.region_eq_footprint _ rfl, Stack.Memory.framed_footprint] at h + +/-- The whole pair writes only the two layouts' cells. -/ +theorem modifies_pair {w C : ℕ} {L₁ L₂ : Stack.Memory.Layout} (h₁ : L₁.Valid w C) + (h₂ : L₂.Valid w C) {vs : List ℕ × List ℕ} + {x y : Data (Backend.registers (Sum.elim certificates certificates))} + {o : (product (Stack.WordRAM.spec w C) (Stack.WordRAM.spec w C)).Op} + {a : (product (Stack.WordRAM.spec w C) (Stack.WordRAM.spec w C)).Result o} {n : ℕ} + (hr : (Backend.implementation (implements_pair h₁ h₂)).rep vs x) + (hv : (product (Stack.WordRAM.spec w C) (Stack.WordRAM.spec w C)).valid vs o) + (he : ((backend w C L₁).sum (backend w C L₂)).exec w x o a y n) : + Modifies (L₁.footprint ∪ L₂.footprint) x.mem y.mem := by + have h := Backend.modifies (implements_pair h₁ h₂) hr hv he + rwa [Memory.Framed.region_eq_footprint _ rfl] at h + +/-- One constant bounds every operation of the pair, before every width, capacity and pair of +layouts. The stacks' charges add across the product (`Memory.Framed.amortized_prod`). -/ +theorem instruction_bound_pair : ∃ K, ∀ {w C : ℕ} {L₁ L₂ : Stack.Memory.Layout} + (h₁ : L₁.Valid w C) (h₂ : L₂.Valid w C), + Bound (Backend.implementation (implements_pair h₁ h₂)) id fun _ _ => K := by + refine ⟨Backend.factor (Sum.elim certificates certificates) * (3 + 19 + 1), + fun {w C L₁ L₂} h₁ h₂ => ?_⟩ + have A : ((Stack.Memory.framed h₁).prod (Stack.Memory.framed h₂)).Amortized Memory.charge + (fun _ _ => 3) fun _ => 0 := by + have A := Memory.Framed.amortized_prod (Stack.Memory.amortized h₁) + (Stack.Memory.amortized h₂) (Memory.Framed.Local.const _ 0) (Memory.Framed.Local.const _ 0) + rwa [productCharge_const] at A + have hwork : ∀ {e} o, ((Stack.Memory.framed h₁).prod (Stack.Memory.framed h₂)).inv e → + (product (Stack.WordRAM.spec _ _) (Stack.WordRAM.spec _ _)).valid + (((Stack.Memory.framed h₁).prod (Stack.Memory.framed h₂)).abs e) o → + (Sum.elim certificates certificates (((backend _ _ L₁).sum (backend _ _ L₂)).tag o)).work + (((backend _ _ L₁).sum (backend _ _ L₂)).arg o) e ≤ 19 := by + intro e o _ _ + rcases o with o | o <;> cases o <;> exact Nat.le_of_ble_eq_true rfl + intro s x o a y n hr hv he + exact Backend.bound (implements_pair h₁ h₂) A 19 hwork hr hv he + +end ComplexityTests.WordRAMRealizationFramed diff --git a/ROADMAP.md b/ROADMAP.md index bc3680623c..d394b1b7c2 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -648,9 +648,12 @@ reusing graph and matroid foundations and the shared data-structure interfaces. 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. 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. + have uniform native certificates over runtime layouts, with local work 0/10/19. Backends of + framed refinements export their frame (`modifies`, `frame`), run products over one memory, + and allocate from arbitrary memory through `Memory.Allocator` (`Backend.Allocation`); the stack + checks all three. Remaining: the queue and binary-heap migrations onto them, allocation of + products in one body, and the full C2/M1b objective; 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 From ed43a2c2352a8a557337e0213323c80bf7da4f27 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Mon, 2 Nov 2026 00:10:36 -0500 Subject: [PATCH 36/40] test(WordRAM): allocate a pair of stacks in one body realizes_allocTwo certifies the allocation of two stacks, one after the other, as Memory.Allocator.prod does; with Backend.extend it becomes the allocation tag of the pair's backend, and allocate_pair sets up both empty stacks from arbitrary bounded data in constant time. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- ComplexityTests/WordRAMRealizationFramed.lean | 72 ++++++++++++++++++- ROADMAP.md | 5 +- 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/ComplexityTests/WordRAMRealizationFramed.lean b/ComplexityTests/WordRAMRealizationFramed.lean index ce1cf7a20b..b3ed207b76 100644 --- a/ComplexityTests/WordRAMRealizationFramed.lean +++ b/ComplexityTests/WordRAMRealizationFramed.lean @@ -15,7 +15,7 @@ their layout's cells (`modifies`, `frame`), as the hand-stepped `Stack.WordRAM.m `frame` did. An allocation tag, added by `Backend.extend`, sets up an empty stack from arbitrary bounded data in constant time, clearing only the count cell (`allocate`), and a client then runs on it (`run_allocated`). Two stacks share one memory with two frames (`modifies_left`, -`instruction_bound_pair`). -/ +`instruction_bound_pair`), and one body allocates both (`allocate_pair`). -/ open Cslib Cslib.DataStructure Cslib.WordRAM Cslib.WordRAM.Realization Cslib.WordRAM.Realization.MemoryStack Cslib.DataStructure.Queue @@ -197,4 +197,74 @@ theorem instruction_bound_pair : ∃ K, ∀ {w C : ℕ} {L₁ L₂ : Stack.Memor intro s x o a y n hr hv he exact Backend.bound (implements_pair h₁ h₂) A 19 hwork hr hv he +/-! ## Allocating two stacks in one body -/ + +/-- The four layout words, for a bind that returns a layout. -/ +instance : WordEncoding Stack.Memory.Layout := ⟨4, layoutWords⟩ + +/-- Allocate two stacks of capacity `C`, one after the other, as `Memory.Allocator.prod` does. -/ +def allocTwo (C : ℕ) : Memory.Prog (Stack.Memory.Layout × Stack.Memory.Layout) := do + let L₁ ← Stack.Memory.allocLayout C + let L₂ ← Stack.Memory.allocLayout C + pure (L₁, L₂) + +/-- Allocating two stacks clears only their two count cells. -/ +theorem realizes_allocTwo : Realizes .exact allocTwo (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + (appendWords layoutWords layoutWords) (fun w C s => Fits w (s.frontier + 2 * (C + 1))) + (fun _ _ => 0) := by + realize + all_goals exact h.of_le (by omega) + +/-- The body allocating both stacks. -/ +noncomputable def pairAllocCertificate : Unit → Certificate .exact := fun _ => ⟨realizes_allocTwo⟩ + +/-- The pair's bodies and the allocation's. -/ +noncomputable abbrev pairWithAlloc : (Tag ⊕ Tag) ⊕ Unit → Certificate .exact := + Sum.elim (Sum.elim certificates certificates) pairAllocCertificate + +/-- Two stacks on a pair of layouts, with the allocation tag. -/ +noncomputable def pairAllocated (w C : ℕ) : + Stack.Memory.Layout × Stack.Memory.Layout → + Backend (product (Stack.WordRAM.spec w C) (Stack.WordRAM.spec w C)) pairWithAlloc + | (L₁, L₂) => ((backend w C L₁).sum (backend w C L₂)).extend pairAllocCertificate + +/-- The allocation tag allocates both stacks. -/ +noncomputable def pairAllocation (w C : ℕ) : + Backend.Allocation ((Stack.Memory.allocator w C).prod (Stack.Memory.allocator w C)) + pairWithAlloc where + tag := .inr () + arg := C + handle := id + run _ := rfl + entry _ _ hroom := by + change Fits w (_ + ((C + 1) + (C + 1))) at hroom + exact ⟨fun _ => (hroom.of_le (by omega) : Fits w C), hroom.of_le (by omega)⟩ + +/-- **Two stacks from dirty memory in constant time.** One constant bounds the allocation of two +empty stacks, one after the other, from any bounded data with room for both; the data then +represents the pair of empty stacks, each on its own layout. -/ +theorem allocate_pair : ∃ K, ∀ {w C : ℕ} {x : Data (Backend.registers pairWithAlloc)}, + x.Bounded w → Fits w (x.regs (Backend.frontier pairWithAlloc) + 2 * (C + 1)) → + ∃ y n, Exec w (Backend.code pairWithAlloc (.inr ())) + (Backend.install pairWithAlloc (.inr ()) C x) y n ∧ n ≤ K ∧ + ∃ (h₁ : (layoutAt (x.regs (Backend.frontier pairWithAlloc)) C).Valid w C) + (h₂ : (layoutAt (x.regs (Backend.frontier pairWithAlloc) + 1 + C) C).Valid w C), + (Backend.implementation (((implements_framed h₁).prod_framed + (implements_framed h₂)).extend pairAllocCertificate)).rep ([], []) y := by + refine ⟨Backend.factor pairWithAlloc * 7, fun {w C x} hx hroom => ?_⟩ + have hF : ∀ s (hs : ((Stack.Memory.allocator w C).prod (Stack.Memory.allocator w C)).Pre s) + (hr : Fits w (s.frontier + ((Stack.Memory.allocator w C).prod + (Stack.Memory.allocator w C)).room)), + (pairAllocated w C ((Memory.spec w).clientResult s + ((Stack.Memory.allocator w C).prod (Stack.Memory.allocator w C)).alloc)).Implements + (((Stack.Memory.allocator w C).prod (Stack.Memory.allocator w C)).framed s hs + hr).toRefinement := + fun s hs hr => + ((implements_framed _).prod_framed (implements_framed _)).extend pairAllocCertificate + obtain ⟨y, n, b, he, hn, -, -, -, hrep⟩ := (pairAllocation w C).exec hF hx + ⟨trivial, trivial, Finset.disjoint_empty_left _⟩ + (show Fits w (x.regs (Backend.frontier pairWithAlloc) + ((C + 1) + (C + 1))) from + hroom.of_le (by omega)) + exact ⟨y, n, he, hn, _, _, hrep⟩ + end ComplexityTests.WordRAMRealizationFramed diff --git a/ROADMAP.md b/ROADMAP.md index d394b1b7c2..afa7df1be1 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -651,9 +651,8 @@ reusing graph and matroid foundations and the shared data-structure interfaces. have uniform native certificates over runtime layouts, with local work 0/10/19. Backends of framed refinements export their frame (`modifies`, `frame`), run products over one memory, and allocate from arbitrary memory through `Memory.Allocator` (`Backend.Allocation`); the stack - checks all three. Remaining: the queue and binary-heap migrations onto them, allocation of - products in one body, and the full C2/M1b objective; arbitrary host continuations do not acquire - runtime bounds. + and a pair of stacks check all three. Remaining: the queue and binary-heap migrations onto them, + and the full C2/M1b objective; 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 From d39ffc00f04bd1d6687b73b23426f419416fa073 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Mon, 2 Nov 2026 04:36:11 -0500 Subject: [PATCH 37/40] feat(WordRAM): retain Backend register frames for live-input execution Expose existing shared packing facts and install identity under actual live words; derive a full-frame execution result and raw-live corollary from the same Realizes code_spec. Existing definitions and all previous theorem types are unchanged; exists_exec projects the stronger proof. Tests exercise different result widths, paid zero staging, zero/max-word inputs and a genuine memory-read provider. Under #2298 for queue #2284 and heap #2282; inherited Claude source credit retained. Strict focused3628 and hygiene pass; full-six/outside review pending. --- .../WordRAM/Realization/Backend.lean | 141 ++++++++++- .../WordRAMRealizationBackendLive.lean | 222 ++++++++++++++++++ 2 files changed, 355 insertions(+), 8 deletions(-) create mode 100644 ComplexityTests/WordRAMRealizationBackendLive.lean diff --git a/Complexity/Computability/WordRAM/Realization/Backend.lean b/Complexity/Computability/WordRAM/Realization/Backend.lean index 112bbe5717..4f189cc3cc 100644 --- a/Complexity/Computability/WordRAM/Realization/Backend.lean +++ b/Complexity/Computability/WordRAM/Realization/Backend.lean @@ -64,6 +64,9 @@ so `memoryCost`, which is taken at width zero, is the charge that refinements st depend on the word width. * `fits_appendWords`: appended argument words fit when both parts do. * `Backend.exists_exec`: the body of one tag runs its program on installed arguments. +* `Backend.exists_exec_frame`, `Backend.exists_exec_live`: retain the register frame and run + directly from arguments already live in the source bank. +* `Backend.install_eq_self`: installation is the identity on actual live arguments. * `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. @@ -182,6 +185,61 @@ noncomputable def tmp (i : ι) (j : Fin ((C i).l + (C i).realizes.scratchSize + unfold registers scratchWords at * omega⟩ +/-- Distinct argument coordinates use distinct source registers. -/ +theorem src_injective (i : ι) : Function.Injective (src C i) := by + intro a b h + apply Fin.ext + have := congrArg Fin.val h + simp only [src] at this + omega + +/-- Distinct result/scratch coordinates use distinct registers. -/ +theorem tmp_injective (i : ι) : Function.Injective (tmp C i) := by + intro a b h + apply Fin.ext + have := congrArg Fin.val h + simp only [tmp] at this + omega + +/-- Source coordinates at the same position agree across tags. -/ +theorem src_eq_of_val_eq (i i' : ι) {a : Fin (C i).k} {b : Fin (C i').k} + (h : a.val = b.val) : src C i a = src C i' b := by + apply Fin.ext + simp only [src, h] + +/-- Result/scratch coordinates at the same position agree across tags. -/ +theorem tmp_eq_of_val_eq (i i' : ι) + {a : Fin ((C i).l + (C i).realizes.scratchSize + 1)} + {b : Fin ((C i').l + (C i').realizes.scratchSize + 1)} + (h : a.val = b.val) : tmp C i a = tmp C i' b := by + apply Fin.ext + simp only [tmp, h] + +/-- No tag's argument register is used by any tag's result/scratch bank. -/ +theorem src_ne_tmp (i i' : ι) (a : Fin (C i).k) + (b : Fin ((C i').l + (C i').realizes.scratchSize + 1)) : src C i a ≠ tmp C i' b := by + intro h + have hk := Finset.le_sup (f := fun i => (C i).k) (Finset.mem_univ i) + have ha := a.isLt + have := congrArg Fin.val h + simp only [src, tmp, argWords] at this + omega + +/-- Argument installation never uses the frontier register. -/ +theorem src_ne_frontier (i : ι) (a : Fin (C i).k) : src C i a ≠ frontier C := by + intro h + have := congrArg Fin.val h + simp only [src, frontier] at this + omega + +/-- Native result/scratch writes never use the frontier register. -/ +theorem tmp_ne_frontier (i : ι) (a : Fin ((C i).l + (C i).realizes.scratchSize + 1)) : + tmp C i a ≠ frontier C := by + intro h + have := congrArg Fin.val h + simp only [tmp, frontier] at this + 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) @@ -237,6 +295,29 @@ private theorem install_src (i : ι) (d : (C i).Arg) (x : Data (registers C)) : funext j simp [install, src] +/-- Installing arguments already present in their source registers changes nothing. +This identity requires actual live words; it supplies no native argument-staging code. -/ +theorem install_eq_self (i : ι) (d : (C i).Arg) (x : Data (registers C)) + (hin : x.regs ∘ src C i = (C i).input d) : install C i d x = x := by + apply Data.ext + · funext j + by_cases hj : 0 < j.val ∧ j.val ≤ (C i).k + · let a : Fin (C i).k := ⟨j.val - 1, by omega⟩ + have hsrc : src C i a = j := by + apply Fin.ext + simp only [src, a] + omega + change (if h : 0 < j.val ∧ j.val ≤ (C i).k then + (C i).input d ⟨j.val - 1, by omega⟩ else x.regs j) = _ + rw [dite_eq_left hj] + have h := congrFun hin a + change x.regs (src C i a) = (C i).input d a at h + exact h.symm.trans (congrArg x.regs hsrc) + · change (if h : 0 < j.val ∧ j.val ≤ (C i).k then + (C i).input d ⟨j.val - 1, by omega⟩ else x.regs j) = _ + rw [dite_eq_right hj] + · rfl + private theorem install_bounded {w : ℕ} (i : ι) (d : (C i).Arg) {x : Data (registers C)} (hx : x.Bounded w) (hin : ∀ j, Fits w ((C i).input d j)) : (install C i d x).Bounded w := by refine ⟨fun j => ?_, hx.2⟩ @@ -245,11 +326,10 @@ private theorem install_bounded {w : ℕ} (i : ι) (d : (C i).Arg) {x : Data (re · exact hin _ · exact hx.1 j -/-- **The body of one tag.** From bounded data, with arguments that fit words, inside the tag's -domain and with an admissible memory history, the body of tag `i` runs its program on the -installed arguments within the certified bound at the common exchange factor. It leaves the -program's final memory and encoded result, and every register and cell a word. -/ -theorem exists_exec {w : ℕ} (i : ι) (d : (C i).Arg) {x : Data (registers C)} (hx : x.Bounded w) +/-- The selected body's complete execution guarantee, including its register frame. +Only the frontier and its result/scratch bank may change relative to installed arguments. -/ +theorem exists_exec_frame {w : ℕ} (i : ι) (d : (C i).Arg) {x : Data (registers C)} + (hx : x.Bounded w) (hin : ∀ j, Fits w ((C i).input d j)) (hsafe : (C i).safe w d (memoryState (frontier C) x)) (hv : (Memory.spec w).ValidHistory (memoryState (frontier C) x) ((Memory.spec w).clientHistory (memoryState (frontier C) x) ((C i).prog d))) : @@ -261,15 +341,60 @@ theorem exists_exec {w : ℕ} (i : ι) (d : (C i).Arg) {x : Data (registers C)} (∀ j, y.regs (tmp C i (Fin.castAdd ((C i).realizes.scratchSize + 1) j)) = (C i).output ((Memory.spec w).clientResult (memoryState (frontier C) x) ((C i).prog d)) j) ∧ - y.Bounded w := by + y.Bounded w ∧ + ∀ j, j ≠ frontier C → (∀ a, tmp C i a ≠ j) → + y.regs j = (install C i d x).regs j := by have hb := install_bounded C i d hx hin have hm := memoryState_install C i d x obtain ⟨hi', hd, hf, hs⟩ := layout C i - obtain ⟨y, ⟨n, hn, he⟩, hm', hout, -⟩ := + obtain ⟨y, ⟨n, hn, he⟩, hm', hout, hframe⟩ := ((C i).realizes.code_spec (frontier C) (src C i) (tmp C i) hi' hd hf hs).2 w d _ hb (install_src C i d x) (hm ▸ hsafe) (hm ▸ hv) rw [hm] at hn hm' hout - exact ⟨y, n, he, hn.trans (Nat.mul_le_mul_right _ (factor_le C i)), hm', hout, he.bounded hb⟩ + exact ⟨y, n, he, hn.trans (Nat.mul_le_mul_right _ (factor_le C i)), hm', hout, + he.bounded hb, hframe⟩ + +/-- **The body of one tag.** From bounded data, with arguments that fit words, inside the tag's +domain and with an admissible memory history, the body of tag `i` runs its program on the +installed arguments within the certified bound at the common exchange factor. It leaves the +program's final memory and encoded result, and every register and cell a word. -/ +theorem exists_exec {w : ℕ} (i : ι) (d : (C i).Arg) {x : Data (registers C)} (hx : x.Bounded w) + (hin : ∀ j, Fits w ((C i).input d j)) (hsafe : (C i).safe w d (memoryState (frontier C) x)) + (hv : (Memory.spec w).ValidHistory (memoryState (frontier C) x) + ((Memory.spec w).clientHistory (memoryState (frontier C) x) ((C i).prog d))) : + ∃ y n, Exec w (code C i) (install C i d x) y n ∧ + n ≤ factor C * (memoryCost ((C i).prog d) (memoryState (frontier C) x) + + (C i).work d (memoryState (frontier C) x) + 1) ∧ + memoryState (frontier C) y = + (Memory.spec w).clientState (memoryState (frontier C) x) ((C i).prog d) ∧ + (∀ j, y.regs (tmp C i (Fin.castAdd ((C i).realizes.scratchSize + 1) j)) = + (C i).output ((Memory.spec w).clientResult (memoryState (frontier C) x) ((C i).prog d)) + j) ∧ + y.Bounded w := by + obtain ⟨y, n, he, hn, hm, hout, hb, -⟩ := exists_exec_frame C i d hx hin hsafe hv + exact ⟨y, n, he, hn, hm, hout, hb⟩ + +/-- Run the same fixed body directly from bounded data whose input words are already live. +No installation or loader is executed. Safety and valid history remain explicit, and the +register frame is relative to the actual initial data. -/ +theorem exists_exec_live {w : ℕ} (i : ι) (d : (C i).Arg) {x : Data (registers C)} (hx : x.Bounded w) + (hin : x.regs ∘ src C i = (C i).input d) (hsafe : (C i).safe w d (memoryState (frontier C) x)) + (hv : (Memory.spec w).ValidHistory (memoryState (frontier C) x) + ((Memory.spec w).clientHistory (memoryState (frontier C) x) ((C i).prog d))) : + ∃ y n, Exec w (code C i) x y n ∧ + n ≤ factor C * (memoryCost ((C i).prog d) (memoryState (frontier C) x) + + (C i).work d (memoryState (frontier C) x) + 1) ∧ + memoryState (frontier C) y = + (Memory.spec w).clientState (memoryState (frontier C) x) ((C i).prog d) ∧ + (∀ j, y.regs (tmp C i (Fin.castAdd ((C i).realizes.scratchSize + 1) j)) = + (C i).output ((Memory.spec w).clientResult (memoryState (frontier C) x) ((C i).prog d)) + j) ∧ + y.Bounded w ∧ + ∀ j, j ≠ frontier C → (∀ a, tmp C i a ≠ j) → + y.regs j = x.regs j := by + have hfit j : Fits w ((C i).input d j) := congrFun hin j ▸ hx.1 (src C i j) + simpa only [install_eq_self C i d x hin] using + exists_exec_frame C i d hx hfit hsafe hv end Registers diff --git a/ComplexityTests/WordRAMRealizationBackendLive.lean b/ComplexityTests/WordRAMRealizationBackendLive.lean new file mode 100644 index 0000000000..0cb7c9e878 --- /dev/null +++ b/ComplexityTests/WordRAMRealizationBackendLive.lean @@ -0,0 +1,222 @@ +/- +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 (live-input Backend and register-frame regressions) + +public import Complexity.Computability.WordRAM.Realization.Backend +public import Complexity.Computability.WordRAM.Realization.Client.MemoryStack +public import Complexity.Tactic.Realize + +/-! +# Already resident arguments and the common Backend register banks + +The two tags have the same five-word input and different result widths, as the queue's count +and count/optional-answer methods do. These query-free fixtures check that native execution +still has its positive per-call allowance, starts from the actual live input, and preserves the +other registers and complete dirty memory. They add no queue dispatcher or initialization claim. +-/ + +public section + +namespace Cslib.WordRAM.Realization.Tests.BackendLive + +open Cslib.DataStructure + +abbrev Input := Fin 5 → ℕ +abbrev counts : ℕ × ℕ ↪ (Fin 2 → ℕ) := WordEncoding.encode +abbrev answer : (ℕ × ℕ) × Option ℕ ↪ (Fin 4 → ℕ) := WordEncoding.encode + +private theorem pure_counts : Realizes .exact + (fun d : Input => (pure (d 0, d 1) : Memory.Prog (ℕ × ℕ))) (.refl (Fin 5 → ℕ)) counts + (fun _ _ _ => True) (fun _ _ => 0) := by + realize + +private theorem pure_answer : Realizes .exact + (fun d : Input => (pure ((d 0, d 1), none) : Memory.Prog ((ℕ × ℕ) × Option ℕ))) + (.refl (Fin 5 → ℕ)) answer (fun _ _ _ => True) (fun _ _ => 0) := by + realize + +/-- A fixed finite family; the second tag has a larger output bank. -/ +noncomputable def certificates : Bool → Certificate .exact + | false => ⟨pure_counts⟩ + | true => ⟨pure_answer⟩ + +-- Constructor tags use the same resident count/layout coordinates. +example (i : Fin 5) : Backend.src certificates false i = Backend.src certificates true i := + Backend.src_eq_of_val_eq certificates false true rfl + +-- The dispatcher may consume the first two shared scratch words before the body reclaims them. +example (i : Fin 2) : + Backend.tmp certificates false ⟨i.val, by + change i.val < 2 + pure_counts.scratchSize + 1 + have := i.isLt + omega⟩ = + Backend.tmp certificates true ⟨i.val, by + change i.val < 4 + pure_answer.scratchSize + 1 + have := i.isLt + omega⟩ := + Backend.tmp_eq_of_val_eq certificates false true rfl + +-- Materializing zero cannot overwrite the delivered constructor tag. +example : Backend.tmp certificates false ⟨0, by + change 0 < 2 + pure_counts.scratchSize + 1 + omega⟩ ≠ + Backend.tmp certificates false ⟨1, by + change 1 < 2 + pure_counts.scratchSize + 1 + omega⟩ := by + intro h + have h' := Backend.tmp_injective certificates false h + have := congrArg Fin.val h' + change 0 = 1 at this + omega + +example (i : Fin 5) (j : Fin (4 + pure_answer.scratchSize + 1)) : + Backend.src certificates false i ≠ Backend.tmp certificates true j := + Backend.src_ne_tmp certificates false true i j + +/-- One actual constructor-tag coordinate, shared with the other tag's scratch prefix. -/ +noncomputable def tagSlot : Fin (Backend.registers certificates) := + Backend.tmp certificates false ⟨0, by + change 0 < 2 + pure_counts.scratchSize + 1 + omega⟩ + +/-- A different shared coordinate used for the paid zero materialization. -/ +noncomputable def zeroSlot : Fin (Backend.registers certificates) := + Backend.tmp certificates true ⟨1, by + change 1 < 4 + pure_answer.scratchSize + 1 + omega⟩ + +private theorem tag_ne_zero : tagSlot ≠ zeroSlot := by + have hz : zeroSlot = Backend.tmp certificates false ⟨1, by + change 1 < 2 + pure_counts.scratchSize + 1 + omega⟩ := Backend.tmp_eq_of_val_eq certificates true false rfl + intro h + have h' := h.trans hz + have hh := Backend.tmp_injective certificates false h' + have := congrArg Fin.val hh + change 0 = 1 at this + omega + +-- The real paid zero instruction retains the delivered tag, resident arguments and memory. +example {w : ℕ} {x : Data (Backend.registers certificates)} (hx : x.Bounded w) : + ∃ y, Exec (B := .exact) w (.op (.const zeroSlot 0)) x y 1 ∧ y.Bounded w ∧ + y.regs tagSlot = x.regs tagSlot ∧ + (∀ j : Fin 5, y.regs (Backend.src certificates false j) = + x.regs (Backend.src certificates false j)) ∧ + memoryState (Backend.frontier certificates) y = + memoryState (Backend.frontier certificates) x := by + let y := x.set zeroSlot 0 + have he : Exec (B := .exact) w (.op (.const zeroSlot 0)) x y 1 := Exec.const (fits_zero w) + refine ⟨y, he, he.bounded hx, ?_, ?_, ?_⟩ + · exact Function.update_of_ne tag_ne_zero _ _ + · intro j + exact Function.update_of_ne (Backend.src_ne_tmp certificates false true j _) _ _ + · apply Memory.State.ext + · exact Data.set_mem x zeroSlot 0 + · exact Function.update_of_ne (Backend.tmp_ne_frontier certificates true _).symm _ _ + +-- The identity is conditional on the supplied words actually occupying the input bank. +example (x : Data (Backend.registers certificates)) (d : Input) + (hin : x.regs ∘ Backend.src certificates false = d) : + Backend.install certificates false d x = x := + Backend.install_eq_self certificates false d x hin + +-- Raw data with different input words does not meet that entry condition. +example : ¬(fun _ : Fin 5 => 0) = (fun _ : Fin 5 => 1) := by + intro h + have := congrFun h 0 + omega + +/-- The same fixed body runs at zero width and on maximum three-bit input words. Neither +query-free execution is assigned zero native charge; both use the common exchange factor. -/ +theorem live_zero_and_max : ∃ r, ∃ code : Code .exact r, ∃ C, ∃ out : Fin 2 → Fin r, + ∃ x₀ y₀ x₇ y₇ : Data r, + x₀.Bounded 0 ∧ x₇.Bounded 3 ∧ + ExecWithin 0 code x₀ y₀ C ∧ (∀ j, y₀.regs (out j) = 0) ∧ y₀.mem = x₀.mem ∧ + ExecWithin 3 code x₇ y₇ C ∧ (∀ j, y₇.regs (out j) = 7) ∧ y₇.mem = x₇.mem := by + let x₀ : Data (Backend.registers certificates) := ⟨fun _ => 0, fun _ => 0⟩ + let x₇ : Data (Backend.registers certificates) := ⟨fun _ => 7, fun _ => 7⟩ + let d₀ : Input := fun _ => 0 + let d₇ : Input := fun _ => 7 + have hx₀ : x₀.Bounded 0 := ⟨fun _ => fits_zero _, fun _ => fits_zero _⟩ + have hx₇ : x₇.Bounded 3 := + ⟨fun _ => by change Fits 3 7; decide, fun _ => by change Fits 3 7; decide⟩ + obtain ⟨y₀, n₀, he₀, hn₀, hm₀, ho₀, -, hf₀⟩ := + Backend.exists_exec_live certificates false d₀ hx₀ rfl trivial (by trivial) + obtain ⟨y₇, n₇, he₇, hn₇, hm₇, ho₇, -, hf₇⟩ := + Backend.exists_exec_live certificates false d₇ hx₇ rfl trivial (by trivial) + have hc₀ : memoryCost ((certificates false).prog d₀) + (memoryState (Backend.frontier certificates) x₀) = 0 := rfl + have hc₇ : memoryCost ((certificates false).prog d₇) + (memoryState (Backend.frontier certificates) x₇) = 0 := rfl + have hw₀ : (certificates false).work d₀ + (memoryState (Backend.frontier certificates) x₀) = 0 := rfl + have hw₇ : (certificates false).work d₇ + (memoryState (Backend.frontier certificates) x₇) = 0 := rfl + have hn₀' : n₀ ≤ Backend.factor certificates := by + simpa only [hc₀, hw₀, Nat.zero_add, Nat.mul_one] using hn₀ + have hn₇' : n₇ ≤ Backend.factor certificates := by + simpa only [hc₇, hw₇, Nat.zero_add, Nat.mul_one] using hn₇ + refine ⟨Backend.registers certificates, Backend.code certificates false, + Backend.factor certificates, + (fun j => Backend.tmp certificates false + (Fin.castAdd ((certificates false).realizes.scratchSize + 1) j)), + x₀, y₀, x₇, y₇, hx₀, hx₇, ⟨n₀, hn₀', he₀⟩, ?_, + congrArg Memory.State.mem hm₀, ⟨n₇, hn₇', he₇⟩, ?_, congrArg Memory.State.mem hm₇⟩ + · intro j + have h := ho₀ j + change _ = counts (0, 0) j at h + fin_cases j <;> exact h + · intro j + have h := ho₇ j + change _ = counts (7, 7) j at h + fin_cases j <;> exact h + +-- The full frame recovers every live source coordinate, including resident layout words. +example {w : ℕ} (d : Input) {x : Data (Backend.registers certificates)} (hx : x.Bounded w) + (hin : x.regs ∘ Backend.src certificates false = d) : + ∃ y n, Exec w (Backend.code certificates false) x y n ∧ + (∀ j : Fin 5, y.regs (Backend.src certificates false j) = + x.regs (Backend.src certificates false j)) := by + obtain ⟨y, n, he, -, -, -, -, hf⟩ := + Backend.exists_exec_live certificates false d hx hin trivial (by trivial) + refine ⟨y, n, he, fun j => hf _ (Backend.src_ne_frontier certificates false j) ?_⟩ + intro a + exact (Backend.src_ne_tmp certificates false false j a).symm + +-- The larger result retains the same frame; its absent answer is represented by two zero words. +example {w : ℕ} (d : Input) {x : Data (Backend.registers certificates)} (hx : x.Bounded w) + (hin : x.regs ∘ Backend.src certificates true = d) : + ∃ y n, Exec w (Backend.code certificates true) x y n ∧ y.mem = x.mem := by + obtain ⟨y, n, he, -, hm, -, -, -⟩ := + Backend.exists_exec_live certificates true d hx hin trivial (by trivial) + exact ⟨y, n, he, congrArg Memory.State.mem hm⟩ + +/-- A genuine memory-reading provider, as used by raw-entry layout/counter consumers. -/ +noncomputable def sizeCertificate (_ : Unit) : Certificate .exact := + ⟨MemoryStack.realizes_size⟩ + +-- The live rule also runs a real query and retains all resident layout words. +example {w : ℕ} (L : Stack.Memory.Layout) + {x : Data (Backend.registers sizeCertificate)} (hx : x.Bounded w) + (hin : x.regs ∘ Backend.src sizeCertificate () = MemoryStack.layoutWords L) + (haddr : L.count.base < x.regs (Backend.frontier sizeCertificate)) : + ∃ y n, Exec w (Backend.code sizeCertificate ()) x y n ∧ y.mem = x.mem ∧ + ∀ j : Fin 4, y.regs (Backend.src sizeCertificate () j) = + x.regs (Backend.src sizeCertificate () j) := by + have hv : (Memory.spec w).ValidHistory (memoryState (Backend.frontier sizeCertificate) x) + ((Memory.spec w).clientHistory (memoryState (Backend.frontier sizeCertificate) x) + ((sizeCertificate ()).prog L)) := by + exact ⟨haddr, trivial⟩ + obtain ⟨y, n, he, -, hm, -, -, hf⟩ := + Backend.exists_exec_live sizeCertificate () L hx hin trivial hv + refine ⟨y, n, he, congrArg Memory.State.mem hm, fun j => + hf _ (Backend.src_ne_frontier sizeCertificate () j) ?_⟩ + intro a + exact (Backend.src_ne_tmp sizeCertificate () () j a).symm + +end Cslib.WordRAM.Realization.Tests.BackendLive From 73f64a78d6b2c6575ba03ffb990a960b945dfc49 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Mon, 2 Nov 2026 05:36:02 -0500 Subject: [PATCH 38/40] fix(WordRAM): expose Backend extension and preparation metadata Expose only the existing pure extend record and prepare alias so real heap bounds and conditional already-live entry elaborate without local record copies or dependent observation wrappers. All definition bodies/types, code/arg/rep/resource meanings are unchanged. Generic bound_extend and actual-live operation-entry regressions pass with the old clients; full new-head validation/review pending under #2298. --- .../WordRAM/Realization/Backend.lean | 4 +- .../WordRAMRealizationBackendLive.lean | 40 +++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/Complexity/Computability/WordRAM/Realization/Backend.lean b/Complexity/Computability/WordRAM/Realization/Backend.lean index 4f189cc3cc..1f7bc44120 100644 --- a/Complexity/Computability/WordRAM/Realization/Backend.lean +++ b/Complexity/Computability/WordRAM/Realization/Backend.lean @@ -448,7 +448,7 @@ theorem Implements.prod {w : ℕ} {S : Backend D C} {S' : Backend D' C'} /-- The backend with the tags of a second family added, such as a realized allocation's (`Backend.Allocation`). Every operation selects the body it selects in `S`. -/ -def extend (C' : ι' → Certificate B) : Backend D (Sum.elim C C') where +@[expose] def extend (C' : ι' → Certificate B) : Backend D (Sum.elim C C') where tag o := .inl (S.tag o) arg := S.arg answer := S.answer @@ -467,7 +467,7 @@ 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) := +@[expose] def prepare (o : D.Op) (x : Data (registers C)) : Data (registers C) := install C (S.tag o) (S.arg o) x /-- An operation runs the body of its tag from the prepared data, in the counted number of diff --git a/ComplexityTests/WordRAMRealizationBackendLive.lean b/ComplexityTests/WordRAMRealizationBackendLive.lean index 0cb7c9e878..48cb8d15c0 100644 --- a/ComplexityTests/WordRAMRealizationBackendLive.lean +++ b/ComplexityTests/WordRAMRealizationBackendLive.lean @@ -219,4 +219,44 @@ example {w : ℕ} (L : Stack.Memory.Layout) intro a exact (Backend.src_ne_tmp sizeCertificate () () j a).symm +section Metadata + +variable {B : Basis} {ι ι' : Type} [Fintype ι] [Fintype ι'] + {D : StateTransitionSpec.{0, 0, 0}} {C : ι → Certificate B} + +-- Adding a fixed family preserves dependent method arguments, results and observations. +example (S : Backend D C) (extra : ι' → Certificate B) (o : D.Op) + (a : (C (S.tag o)).Res) : (S.extend extra).answer o a = S.answer o a := rfl + +-- This is the extended-bank bound pattern needed by the heap's actual bound_extend caller. +example {w : ℕ} (S : Backend D C) (extra : ι' → Certificate B) + {R : Refinement D (Memory.spec w)} (hS : S.Implements R) + {upper : D.State → D.Op → ℕ} (A : R.Amortized Memory.charge upper (fun _ => 0)) + (c : ℕ) (hw : ∀ {e} o, R.inv e → D.valid (R.abs e) o → + (C (S.tag o)).work (S.arg o) e ≤ c) : + Bound (Backend.implementation (hS.extend extra)) id + (fun s o => Backend.factor (Sum.elim C extra) * (upper s o + c + 1)) := + Backend.bound (hS.extend extra) A c (fun o hi hv => hw o hi hv) + +-- Preparation is the identity only when the selected method's actual words are live. +example (S : Backend D C) (o : D.Op) (x : Data (Backend.registers C)) + (hin : x.regs ∘ Backend.src C (S.tag o) = (C (S.tag o)).input (S.arg o)) : + S.prepare o x = x := + Backend.install_eq_self C (S.tag o) (S.arg o) x hin + +-- A real live-input body execution enters the existing operation relation with its decoder. +example (S : Backend D C) (w : ℕ) (o : D.Op) (a : D.Result o) + (x y : Data (Backend.registers C)) (n : ℕ) + (hin : x.regs ∘ Backend.src C (S.tag o) = (C (S.tag o)).input (S.arg o)) + (he : Exec w (Backend.code C (S.tag o)) x y n) + (hout : ∃ b, a = S.answer o b ∧ ∀ j, + y.regs (Backend.tmp C (S.tag o) + (Fin.castAdd ((C (S.tag o)).realizes.scratchSize + 1) j)) = + (C (S.tag o)).output b j) : + S.exec w x o a y n := by + have hp : S.prepare o x = x := Backend.install_eq_self C (S.tag o) (S.arg o) x hin + exact (S.exec_iff w x y o a n).mpr ⟨by simpa only [hp] using he, hout⟩ + +end Metadata + end Cslib.WordRAM.Realization.Tests.BackendLive From fc383e53b3b352a5cb0a9c10b9af2da67100a1f2 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Mon, 2 Nov 2026 05:47:16 -0500 Subject: [PATCH 39/40] Fix Stack migration name resolution and framed proof view Append only to frozen #2272 head97bd. Explicitly qualify Doubling's eighty Memory.spec references shadowed by the relocated bounded Stack.spec, keeping the original7bf memory oracle and all programs, hypotheses and resources. Wrap four newly long lines. Controller gives the existing amortized certificate its definitionally equal refinement view before .client. No public lemma, definition, capacity premise, program or bound is added. Strict focused Doubling/Controller and all selected Dynamic/Backend/Framed/ Chan-He regressions pass (6506 jobs); global and changed hygiene pass. Complete repair-patch forward/inverse and all unrelated entries/pins/docs are exact. Full-six and independent delta review remain before publication. Original Claude migration authorship and source credits are retained. --- .../DataStructure/Stack/Controller.lean | 3 +- Complexity/DataStructure/Stack/Doubling.lean | 150 +++++++++--------- 2 files changed, 79 insertions(+), 74 deletions(-) diff --git a/Complexity/DataStructure/Stack/Controller.lean b/Complexity/DataStructure/Stack/Controller.lean index d99bc0fcdc..48a450d801 100644 --- a/Complexity/DataStructure/Stack/Controller.lean +++ b/Complexity/DataStructure/Stack/Controller.lean @@ -150,7 +150,8 @@ public theorem program_spec {w n : ℕ} {s : Memory.State} (hf : Fits w (s.front have hlink : (Stack.Memory.refinement hL).methods = Stack.Memory.methods L w 1 := rfl have habs' : (Stack.Memory.refinement hL).abs = Stack.Memory.abs L := rfl obtain ⟨hvE, hres, -, -⟩ := (Stack.Memory.refinement hL).simulates_client (client w n) hinv hv - have hcost := (Stack.Memory.amortized hL).client (client w n) hinv hv + have hcost := (show (Stack.Memory.refinement hL).Amortized Memory.charge + (fun _ _ => 3) (fun _ => 0) from Stack.Memory.amortized hL).client (client w n) hinv hv rw [hlink, ← run_eq_link] at hvE hres hcost rw [habs', habs] at hres hcost have hrun := Stack.Memory.runClient_allocLayout_bind (w := w) 1 (fun L => run L n) s diff --git a/Complexity/DataStructure/Stack/Doubling.lean b/Complexity/DataStructure/Stack/Doubling.lean index da78d22ced..338fd2199b 100644 --- a/Complexity/DataStructure/Stack/Doubling.lean +++ b/Complexity/DataStructure/Stack/Doubling.lean @@ -164,22 +164,23 @@ def grown (n : ℕ) (s : State) : State := /-- Growing moves the items to a fresh array of twice the size. -/ theorem clientState_grow (n : ℕ) (s : State) (h : base hdr s + n ≤ s.frontier) : - (spec w).clientState s (grow hdr n) = grown hdr n s := by - change (spec w).clientState ⟨s.mem, s.frontier + 2 * n⟩ + (Memory.spec w).clientState s (grow hdr n) = grown hdr n s := by + change (Memory.spec w).clientState ⟨s.mem, s.frontier + 2 * n⟩ (copy (s.mem (hdr + 2)) s.frontier n >>= fun _ => write (hdr + 2) s.frontier >>= fun _ => write (hdr + 1) (2 * n)) = _ - rw [(spec w).clientState_bind, + rw [(Memory.spec w).clientState_bind, clientState_copy (s.mem (hdr + 2)) s.frontier n ⟨s.mem, s.frontier + 2 * n⟩ (.inl h)] rfl /-- Growing costs `4 + 2 n` memory units. -/ theorem totalCost_grow (n : ℕ) (s : State) : - (spec w).totalCost charge s ((spec w).clientHistory s (grow hdr n)) = 4 + 2 * n := by - change 1 + (1 + (spec w).totalCost charge ⟨s.mem, s.frontier + 2 * n⟩ - ((spec w).clientHistory ⟨s.mem, s.frontier + 2 * n⟩ + (Memory.spec w).totalCost charge s ((Memory.spec w).clientHistory s (grow hdr n)) = + 4 + 2 * n := by + change 1 + (1 + (Memory.spec w).totalCost charge ⟨s.mem, s.frontier + 2 * n⟩ + ((Memory.spec w).clientHistory ⟨s.mem, s.frontier + 2 * n⟩ (copy (s.mem (hdr + 2)) s.frontier n >>= fun _ => write (hdr + 2) s.frontier >>= fun _ => write (hdr + 1) (2 * n)))) = _ - rw [(spec w).clientHistory_bind, StateTransitionSpec.totalCost_append, totalCost_copy] + rw [(Memory.spec w).clientHistory_bind, StateTransitionSpec.totalCost_append, totalCost_copy] change 1 + (1 + (2 * n + 2)) = _ omega @@ -188,13 +189,13 @@ fits a word. -/ theorem validHistory_grow (n : ℕ) (s : State) (hh : hdr + 3 ≤ s.frontier) (h : base hdr s + n ≤ s.frontier) (hfit : Fits w (s.frontier + 2 * n)) (hitems : ∀ i < n, Fits w (s.mem (base hdr s + i))) : - (spec w).ValidHistory s ((spec w).clientHistory s (grow hdr n)) := by + (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s (grow hdr n)) := by change hdr + 2 < s.frontier ∧ Fits w (s.frontier + 2 * n) ∧ - (spec w).ValidHistory ⟨s.mem, s.frontier + 2 * n⟩ - ((spec w).clientHistory ⟨s.mem, s.frontier + 2 * n⟩ + (Memory.spec w).ValidHistory ⟨s.mem, s.frontier + 2 * n⟩ + ((Memory.spec w).clientHistory ⟨s.mem, s.frontier + 2 * n⟩ (copy (s.mem (hdr + 2)) s.frontier n >>= fun _ => write (hdr + 2) s.frontier >>= fun _ => write (hdr + 1) (2 * n))) - rw [(spec w).clientHistory_bind, StateTransitionSpec.validHistory_append, + rw [(Memory.spec w).clientHistory_bind, StateTransitionSpec.validHistory_append, StateTransitionSpec.run_runClient, clientState_copy (s.mem (hdr + 2)) s.frontier n ⟨s.mem, s.frontier + 2 * n⟩ (.inl h)] refine ⟨by omega, hfit, validHistory_copy _ _ _ ⟨s.mem, s.frontier + 2 * n⟩ (.inl h) @@ -206,13 +207,13 @@ theorem validHistory_grow (n : ℕ) (s : State) (hh : hdr + 3 ≤ s.frontier) /-- Appending reads the base, writes the item and the new count. -/ theorem runClient_append (n v : ℕ) (s : State) : - (spec w).runClient s (append hdr n v) = + (Memory.spec w).runClient s (append hdr n v) = ((), ⟨Function.update (Function.update s.mem (base hdr s + n) v) hdr (n + 1), s.frontier⟩, [.read (hdr + 2), .write (base hdr s + n) v, .write hdr (n + 1)]) := rfl /-- A push into an array with room reads the count and capacity, then appends. -/ theorem runClient_push_not_full (v : ℕ) (s : State) (h : count hdr s ≠ capacity hdr s) : - (spec w).runClient s (push hdr v) = + (Memory.spec w).runClient s (push hdr v) = ((), ⟨Function.update (Function.update s.mem (base hdr s + count hdr s) v) hdr (count hdr s + 1), s.frontier⟩, [.read hdr, .read (hdr + 1), .read (hdr + 2), .write (base hdr s + count hdr s) v, @@ -226,32 +227,33 @@ theorem runClient_push_not_full (v : ℕ) (s : State) (h : count hdr s ≠ capac /-- A push into a full array grows it, then appends. -/ theorem push_full_eq (v : ℕ) (s : State) (h : count hdr s = capacity hdr s) : - (spec w).clientState s (push hdr v) = - (spec w).clientState s (grow hdr (count hdr s) >>= fun _ => append hdr (count hdr s) v) ∧ - (spec w).clientHistory s (push hdr v) = + (Memory.spec w).clientState s (push hdr v) = + (Memory.spec w).clientState s (grow hdr (count hdr s) >>= fun _ => + append hdr (count hdr s) v) ∧ + (Memory.spec w).clientHistory s (push hdr v) = .read hdr :: .read (hdr + 1) :: - (spec w).clientHistory s (grow hdr (count hdr s) >>= fun _ => + (Memory.spec w).clientHistory s (grow hdr (count hdr s) >>= fun _ => append hdr (count hdr s) v) := by have he : ensure hdr (count hdr s) (capacity hdr s) = grow hdr (count hdr s) := by simp only [ensure, h, ↓reduceIte] exact ⟨by - change (spec w).clientState s (ensure hdr (count hdr s) (capacity hdr s) >>= fun _ => + change (Memory.spec w).clientState s (ensure hdr (count hdr s) (capacity hdr s) >>= fun _ => append hdr (count hdr s) v) = _ rw [he], by - change _ :: _ :: (spec w).clientHistory s (ensure hdr (count hdr s) (capacity hdr s) >>= + change _ :: _ :: (Memory.spec w).clientHistory s (ensure hdr (count hdr s) (capacity hdr s) >>= fun _ => append hdr (count hdr s) v) = _ rw [he]⟩ /-- A pop from an empty stack reads the count only. -/ theorem runClient_pop_of_eq_zero (s : State) (h : count hdr s = 0) : - (spec w).runClient s (pop hdr) = (none, s, [.read hdr]) := by + (Memory.spec w).runClient s (pop hdr) = (none, s, [.read hdr]) := by simp only [count] at h simp [pop, h] /-- A pop from a nonempty stack reads the count, the base and the top item, and writes the new count. -/ theorem runClient_pop_of_ne_zero (s : State) (h : count hdr s ≠ 0) : - (spec w).runClient s (pop hdr) = + (Memory.spec w).runClient s (pop hdr) = (some (s.mem (base hdr s + (count hdr s - 1))), ⟨Function.update s.mem hdr (count hdr s - 1), s.frontier⟩, [.read hdr, .read (hdr + 2), .read (base hdr s + (count hdr s - 1)), @@ -264,7 +266,7 @@ theorem runClient_pop_of_ne_zero (s : State) (h : count hdr s ≠ 0) : /-- A size query reads the count. -/ theorem runClient_size (s : State) : - (spec w).runClient s (size hdr) = (count hdr s, s, [.read hdr]) := rfl + (Memory.spec w).runClient s (size hdr) = (count hdr s, s, [.read hdr]) := rfl /-! ## The operations' effects -/ @@ -296,13 +298,13 @@ theorem abs_push {s s' : State} (hn : count hdr s' = count hdr s + 1) /-- A push into an array with room writes the item and the count, and keeps the rest. -/ theorem push_not_full {s : State} (hs : Inv hdr w C s) (h : count hdr s ≠ capacity hdr s) (v : ℕ) : - count hdr ((spec w).clientState s (push hdr v)) = count hdr s + 1 ∧ - capacity hdr ((spec w).clientState s (push hdr v)) = capacity hdr s ∧ - base hdr ((spec w).clientState s (push hdr v)) = base hdr s ∧ - ((spec w).clientState s (push hdr v)).frontier = s.frontier ∧ - ((spec w).clientState s (push hdr v)).mem (base hdr s + count hdr s) = v ∧ + count hdr ((Memory.spec w).clientState s (push hdr v)) = count hdr s + 1 ∧ + capacity hdr ((Memory.spec w).clientState s (push hdr v)) = capacity hdr s ∧ + base hdr ((Memory.spec w).clientState s (push hdr v)) = base hdr s ∧ + ((Memory.spec w).clientState s (push hdr v)).frontier = s.frontier ∧ + ((Memory.spec w).clientState s (push hdr v)).mem (base hdr s + count hdr s) = v ∧ ∀ a, a ≠ hdr → a ≠ base hdr s + count hdr s → - ((spec w).clientState s (push hdr v)).mem a = s.mem a := by + ((Memory.spec w).clientState s (push hdr v)).mem a = s.mem a := by have hb := hs.header_le simp only [StateTransitionSpec.clientState, runClient_push_not_full hdr v s h, count, capacity, base] at hb ⊢ @@ -321,18 +323,19 @@ theorem base_grown (n : ℕ) (s : State) : base hdr (grown hdr n s) = s.frontier frontier, writes the item and the count, and keeps every other allocated cell. -/ theorem push_full {s : State} (hs : Inv hdr w C s) (h : count hdr s = capacity hdr s) (v : ℕ) : - count hdr ((spec w).clientState s (push hdr v)) = count hdr s + 1 ∧ - capacity hdr ((spec w).clientState s (push hdr v)) = 2 * count hdr s ∧ - base hdr ((spec w).clientState s (push hdr v)) = s.frontier ∧ - ((spec w).clientState s (push hdr v)).frontier = s.frontier + 2 * count hdr s ∧ - ((spec w).clientState s (push hdr v)).mem (s.frontier + count hdr s) = v ∧ + count hdr ((Memory.spec w).clientState s (push hdr v)) = count hdr s + 1 ∧ + capacity hdr ((Memory.spec w).clientState s (push hdr v)) = 2 * count hdr s ∧ + base hdr ((Memory.spec w).clientState s (push hdr v)) = s.frontier ∧ + ((Memory.spec w).clientState s (push hdr v)).frontier = s.frontier + 2 * count hdr s ∧ + ((Memory.spec w).clientState s (push hdr v)).mem (s.frontier + count hdr s) = v ∧ (∀ i < count hdr s, - ((spec w).clientState s (push hdr v)).mem (s.frontier + i) = s.mem (base hdr s + i)) ∧ + ((Memory.spec w).clientState s (push hdr v)).mem (s.frontier + i) = + s.mem (base hdr s + i)) ∧ ∀ a < s.frontier, a ≠ hdr → a ≠ hdr + 1 → a ≠ hdr + 2 → - ((spec w).clientState s (push hdr v)).mem a = s.mem a := by + ((Memory.spec w).clientState s (push hdr v)).mem a = s.mem a := by have hb := hs.header_le have hi := hs.items_le - rw [(push_full_eq hdr v s h).1, (spec w).clientState_bind, + rw [(push_full_eq hdr v s h).1, (Memory.spec w).clientState_bind, clientState_grow hdr _ s (by rw [h]; exact hi)] simp only [StateTransitionSpec.clientState, runClient_append, base_grown] simp only [grown, count, capacity, base] at hb hi h ⊢ @@ -344,9 +347,10 @@ theorem push_full {s : State} (hs : Inv hdr w C s) (h : count hdr s = capacity h /-- A push into a full array of `n` items costs `9 + 2 n` memory units. -/ theorem totalCost_push_full {s : State} (h : count hdr s = capacity hdr s) (v : ℕ) : - (spec w).totalCost charge s ((spec w).clientHistory s (push hdr v)) = 9 + 2 * count hdr s := by - rw [(push_full_eq hdr v s h).2, (spec w).clientHistory_bind] - change 1 + (1 + (spec w).totalCost charge s (_ ++ _)) = _ + (Memory.spec w).totalCost charge s ((Memory.spec w).clientHistory s (push hdr v)) = + 9 + 2 * count hdr s := by + rw [(push_full_eq hdr v s h).2, (Memory.spec w).clientHistory_bind] + change 1 + (1 + (Memory.spec w).totalCost charge s (_ ++ _)) = _ rw [StateTransitionSpec.totalCost_append, totalCost_grow, StateTransitionSpec.run_runClient] change 1 + (1 + (4 + 2 * count hdr s + 3)) = _ omega @@ -355,15 +359,15 @@ theorem totalCost_push_full {s : State} (h : count hdr s = capacity hdr s) (v : theorem validHistory_push_full {s : State} (hs : Inv hdr w C s) (h : count hdr s = capacity hdr s) {v : ℕ} (hv : Fits w v) (hroom : 2 * count hdr s ≤ room hdr C s) : - (spec w).ValidHistory s ((spec w).clientHistory s (push hdr v)) := by + (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s (push hdr v)) := by have hb := hs.header_le have hi := hs.items_le have h1 := hs.one_le_capacity have hfit : Fits w (s.frontier + 2 * count hdr s) := hs.fits_room.of_le (by omega) - rw [(push_full_eq hdr v s h).2, (spec w).clientHistory_bind] + rw [(push_full_eq hdr v s h).2, (Memory.spec w).clientHistory_bind] refine ⟨by simp only [valid_read, base] at hb hi ⊢; omega, by simp only [StateTransitionSpec.stepFn, step_read, valid_read, base] at hb hi ⊢; omega, ?_⟩ - change (spec w).ValidHistory s (_ ++ _) + change (Memory.spec w).ValidHistory s (_ ++ _) rw [StateTransitionSpec.validHistory_append, StateTransitionSpec.run_runClient, clientState_grow hdr _ s (by rw [h]; exact hi)] refine ⟨validHistory_grow hdr _ s (by omega) (by rw [h]; exact hi) hfit @@ -377,11 +381,11 @@ theorem validHistory_push_full {s : State} (hs : Inv hdr w C s) /-- A pop from a nonempty stack decrements the count and keeps the rest. -/ theorem pop_of_ne_zero {s : State} (h : count hdr s ≠ 0) : - count hdr ((spec w).clientState s (pop hdr)) = count hdr s - 1 ∧ - capacity hdr ((spec w).clientState s (pop hdr)) = capacity hdr s ∧ - base hdr ((spec w).clientState s (pop hdr)) = base hdr s ∧ - ((spec w).clientState s (pop hdr)).frontier = s.frontier ∧ - ∀ a, a ≠ hdr → ((spec w).clientState s (pop hdr)).mem a = s.mem a := by + count hdr ((Memory.spec w).clientState s (pop hdr)) = count hdr s - 1 ∧ + capacity hdr ((Memory.spec w).clientState s (pop hdr)) = capacity hdr s ∧ + base hdr ((Memory.spec w).clientState s (pop hdr)) = base hdr s ∧ + ((Memory.spec w).clientState s (pop hdr)).frontier = s.frontier ∧ + ∀ a, a ≠ hdr → ((Memory.spec w).clientState s (pop hdr)).mem a = s.mem a := by simp only [StateTransitionSpec.clientState, runClient_pop_of_ne_zero hdr s h, count, capacity, base] exact ⟨by simp, Function.update_of_ne (by omega) _ _, Function.update_of_ne (by omega) _ _, @@ -401,7 +405,7 @@ theorem Inv.fits_frontier {s : State} (hs : Inv hdr w C s) : Fits w s.frontier : /-- The stack before a pop is the popped item on top of the stack after it. -/ theorem abs_of_ne_zero {s : State} (hs : Inv hdr w C s) (h : count hdr s ≠ 0) : abs hdr s = s.mem (base hdr s + (count hdr s - 1)) :: - abs hdr ((spec w).clientState s (pop hdr)) := by + abs hdr ((Memory.spec w).clientState s (pop hdr)) := by obtain ⟨hn, -, hb, -, hmem⟩ := pop_of_ne_zero (w := w) h have hh := hs.header_le refine abs_push (by omega) (fun i _ => ?_) (by rw [hn]) @@ -455,7 +459,7 @@ def refinement (hdr w C : ℕ) : Refinement (Stack.spec w C) (Memory.spec w) := by_cases heq : count hdr s = capacity hdr s · obtain ⟨hn, hcap, hbase, hfr, hv', hcopy, -⟩ := push_full hs heq v refine ⟨?_, ⟨by omega, by omega, by omega, by omega, ?_, fun i hi' => ?_⟩⟩ - · change (v :: abs hdr s, ()) = (abs hdr ((spec w).clientState s (push hdr v)), ()) + · change (v :: abs hdr s, ()) = (abs hdr ((Memory.spec w).clientState s (push hdr v)), ()) rw [abs_push hn (fun i hi' => by rw [hbase, hcopy i hi']) (by rw [hbase, hv'])] · rw [hfr] exact hs.fits_room.of_le (by simp only [room] at *; omega) @@ -466,12 +470,12 @@ def refinement (hdr w C : ℕ) : Refinement (Stack.spec w C) (Memory.spec w) := · rwa [hv'] · obtain ⟨hn, hcap, hbase, hfr, hv', hmem⟩ := push_not_full hs heq v have hlt : count hdr s < capacity hdr s := Nat.lt_of_le_of_ne hc heq - have hold : ∀ i < count hdr s, ((spec w).clientState s (push hdr v)).mem + have hold : ∀ i < count hdr s, ((Memory.spec w).clientState s (push hdr v)).mem (base hdr s + i) = s.mem (base hdr s + i) := fun i hi' => hmem _ (by omega) (by omega) refine ⟨?_, ⟨by omega, by omega, by omega, by omega, ?_, fun i hi' => ?_⟩⟩ - · change (v :: abs hdr s, ()) = (abs hdr ((spec w).clientState s (push hdr v)), ()) + · change (v :: abs hdr s, ()) = (abs hdr ((Memory.spec w).clientState s (push hdr v)), ()) rw [abs_push hn (fun i hi' => by rw [hbase]; exact hold i hi') (by rw [hbase, hv'])] - · rw [hfr, show room hdr C ((spec w).clientState s (push hdr v)) = + · rw [hfr, show room hdr C ((Memory.spec w).clientState s (push hdr v)) = room hdr C s by simp only [room]; rw [hcap]] exact hs.fits_room · rw [hbase] @@ -492,7 +496,7 @@ def refinement (hdr w C : ℕ) : Refinement (Stack.spec w C) (Memory.spec w) := · rw [abs_of_ne_zero hs h0] simp only [StateTransitionSpec.clientResult, runClient_pop_of_ne_zero hdr s h0] rfl - · rw [hfr, show room hdr C ((spec w).clientState s (pop hdr)) = room hdr C s by + · rw [hfr, show room hdr C ((Memory.spec w).clientState s (pop hdr)) = room hdr C s by simp only [room]; rw [hcap]] exact hs.fits_room · rw [hbase, hmem _ (by omega)] @@ -538,11 +542,11 @@ footprint only into fresh cells, and advances the frontier by at most the room i theorem modifies_methods {s : State} (hs : Inv hdr w C s) (o : StackOp ℕ) (hv : (Stack.spec w C).valid (abs hdr s) o) : (∀ b < s.frontier, b ∉ footprint hdr s → - ((spec w).clientState s (methods hdr w C o)).mem b = s.mem b) ∧ - (∀ b ∈ footprint hdr ((spec w).clientState s (methods hdr w C o)), + ((Memory.spec w).clientState s (methods hdr w C o)).mem b = s.mem b) ∧ + (∀ b ∈ footprint hdr ((Memory.spec w).clientState s (methods hdr w C o)), b ∈ footprint hdr s ∨ s.frontier ≤ b) ∧ - ((spec w).clientState s (methods hdr w C o)).frontier + - room hdr C ((spec w).clientState s (methods hdr w C o)) ≤ + ((Memory.spec w).clientState s (methods hdr w C o)).frontier + + room hdr C ((Memory.spec w).clientState s (methods hdr w C o)) ≤ s.frontier + room hdr C s := by have hc := hs.count_le cases o with @@ -571,7 +575,7 @@ theorem modifies_methods {s : State} (hs : Inv hdr w C s) (o : StackOp ℕ) | pop => simp only [methods] by_cases h0 : count hdr s = 0 - · have hs' : (spec w).clientState s (pop hdr) = s := by + · have hs' : (Memory.spec w).clientState s (pop hdr) = s := by simp only [StateTransitionSpec.clientState, runClient_pop_of_eq_zero hdr s h0] rw [hs'] exact ⟨fun _ _ _ => rfl, fun _ hb => .inl hb, le_rfl⟩ @@ -594,7 +598,7 @@ def framed (hdr w C : ℕ) : Memory.Framed (Stack.spec w C) w where rcases mem_footprint.mp ha with ha | ha <;> omega congr hs h hf hr := congr hs h hf hr modifies {s} o {a s' tr} hs hv hrun := by - obtain ⟨_, _, rfl, _⟩ := ((spec w).clientRun_iff_runClient _ _ _ _ _).mp hrun + obtain ⟨_, _, rfl, _⟩ := ((Memory.spec w).clientRun_iff_runClient _ _ _ _ _).mp hrun exact modifies_methods hs o hv /-- The potential reads only the header. -/ @@ -613,8 +617,8 @@ theorem amortized : (framed hdr w C).Amortized charge (fun _ _ => 13) (potential have hc := hs.count_le cases o with | push v => - change (spec w).totalCost charge s ((spec w).clientHistory s (push hdr v)) + - potential hdr ((spec w).clientState s (push hdr v)) ≤ 13 + potential hdr s + change (Memory.spec w).totalCost charge s ((Memory.spec w).clientHistory s (push hdr v)) + + potential hdr ((Memory.spec w).clientState s (push hdr v)) ≤ 13 + potential hdr s by_cases heq : count hdr s = capacity hdr s · obtain ⟨hn, hcap, -⟩ := push_full hs heq v rw [totalCost_push_full heq] @@ -628,8 +632,8 @@ theorem amortized : (framed hdr w C).Amortized charge (fun _ _ => 13) (potential StateTransitionSpec.totalCost_nil] omega | pop => - change (spec w).totalCost charge s ((spec w).clientHistory s (pop hdr)) + - potential hdr ((spec w).clientState s (pop hdr)) ≤ 13 + potential hdr s + change (Memory.spec w).totalCost charge s ((Memory.spec w).clientHistory s (pop hdr)) + + potential hdr ((Memory.spec w).clientState s (pop hdr)) ≤ 13 + potential hdr s by_cases h0 : count hdr s = 0 · simp [StateTransitionSpec.clientHistory, StateTransitionSpec.clientState, runClient_pop_of_eq_zero hdr s h0] @@ -640,8 +644,8 @@ theorem amortized : (framed hdr w C).Amortized charge (fun _ _ => 13) (potential StateTransitionSpec.totalCost_nil] omega | size => - change (spec w).totalCost charge s ((spec w).clientHistory s (size hdr)) + - potential hdr ((spec w).clientState s (size hdr)) ≤ 13 + potential hdr s + change (Memory.spec w).totalCost charge s ((Memory.spec w).clientHistory s (size hdr)) + + potential hdr ((Memory.spec w).clientState s (size hdr)) ≤ 13 + potential hdr s simp [StateTransitionSpec.clientHistory, StateTransitionSpec.clientState, runClient_size] @@ -658,7 +662,7 @@ def create : Prog ℕ := do /-- Creation allocates the header at the frontier and the array of one cell above it. -/ theorem runClient_create (s : State) : - (spec w).runClient s create = + (Memory.spec w).runClient s create = (s.frontier, ⟨Function.update (Function.update (clear s.mem s.frontier 3) (s.frontier + 1) 1) (s.frontier + 2) (s.frontier + 3), s.frontier + 3 + 1⟩, [.alloc 3, .allocDirty 1, .write (s.frontier + 1) 1, @@ -666,9 +670,9 @@ theorem runClient_create (s : State) : /-- The header of a created stack: no items, capacity one, and the array just above. -/ theorem create_header (s : State) : - count s.frontier ((spec w).clientState s create) = 0 ∧ - capacity s.frontier ((spec w).clientState s create) = 1 ∧ - base s.frontier ((spec w).clientState s create) = s.frontier + 3 := by + count s.frontier ((Memory.spec w).clientState s create) = 0 ∧ + capacity s.frontier ((Memory.spec w).clientState s create) = 1 ∧ + base s.frontier ((Memory.spec w).clientState s create) = s.frontier + 3 := by simp only [StateTransitionSpec.clientState, runClient_create, count, capacity, base] refine ⟨?_, ?_, ?_⟩ · rw [Function.update_of_ne (by omega), Function.update_of_ne (by omega), @@ -688,7 +692,7 @@ def allocator (w C : ℕ) : Allocator (Stack.spec w C) w where framed s _ _ := framed s.frontier w C allocates s _ hroom := by obtain ⟨hn, hcap, hbase⟩ := create_header (w := w) s - have hfr : ((spec w).clientState s create).frontier = s.frontier + 3 + 1 := rfl + have hfr : ((Memory.spec w).clientState s create).frontier = s.frontier + 3 + 1 := rfl refine { validHistory := ?_, cost_le := ?_, mem_eq := ?_, methods_eq := rfl, inv := ?_, abs_eq := ?_, of_mem_footprint := ?_, room_le := ?_ } @@ -711,8 +715,8 @@ def allocator (w C : ℕ) : Allocator (Stack.spec w C) w where change a ∈ footprint _ _ at ha right rcases mem_footprint.mp ha with ha | ha <;> omega - · change ((spec w).clientState s create).frontier + - room s.frontier C ((spec w).clientState s create) ≤ s.frontier + (4 * C + 4) + · change ((Memory.spec w).clientState s create).frontier + + room s.frontier C ((Memory.spec w).clientState s create) ≤ s.frontier + (4 * C + 4) simp only [room, hcap, hfr] omega @@ -725,7 +729,7 @@ theorem amortized_allocator : (allocator w C).Amortized (fun _ _ => 13) 0 := by intro s _ _ obtain ⟨hn, hcap, -⟩ := create_header (w := w) s refine ⟨potential s.frontier, amortized, local_potential, ?_⟩ - change potential s.frontier ((spec w).clientState s create) ≤ 0 + change potential s.frontier ((Memory.spec w).clientState s create) ≤ 0 simp only [potential, hn, hcap] omega From 57b267aaea15269bfe7dcca7af530e1bae984919 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Mon, 2 Nov 2026 06:28:17 -0500 Subject: [PATCH 40/40] refactor(WordRAM): realize the queue with resident register metadata Use Memory.Prog, boundedWhile, Realizes and one fixed Backend family with actual native tag dispatch and paid count writeback. Preserve the original 2*C reservation, Fits1/end-fit domain, arbitrary dirty bounded initialization in six register instructions, typed FIFO observations, adaptive execution and heap frame. Remove old Basic/Dynamic and superseded prototypes; migrate every client and retain the source transfer equation/invariant/allocator decomposition. Static shared R/F replace the hand program's literal register and instruction constants: call charge4F+6 and terminal potential2F*rear include control, commit and halt. The external client controller/delivery boundary remains; generic framed allocation/composition and the full2205 objective are not completed. Default scripts/check.sh all six PASS at exact staged tree37b5dee43327761897a6c152ef3246de13d12ca0, plus eight strict focused queue targets, imports and hygiene. Parent is the private ordinary joint0abe of public2299/73 and2272/fc; supplier review/actual/current-context gates remain separate. This own layer changes exactly21 paths. Source credit Samuel Schlesinger retained. OpenAI Codex assisted implementation, proofs, tests and source-preserving integration; Kernel's exact metadata-source prefix is reused with its contribution credited and old feasibility refs preserved. Shared providers retain their original authors through parent history. Refs #2284; broader #2205/#2213 remain open. --- Complexity.lean | 10 +- .../DataStructure/Queue/Memory/Transfer.lean | 154 ++++++ .../DataStructure/Queue/WordRAM/Basic.lean | 395 --------------- .../DataStructure/Queue/WordRAM/Dynamic.lean | 165 ------- .../Queue/WordRAM/RegisterCorrectness.lean | 141 ++++++ .../Queue/WordRAM/RegisterDynamic.lean | 112 +++++ .../Queue/WordRAM/RegisterExecution.lean | 448 ++++++++++++++++++ .../Queue/WordRAM/RegisterMetadata.lean | 95 ++++ .../Queue/WordRAM/RegisterMethods.lean | 289 +++++++++++ .../Queue/WordRAM/RegisterProgram.lean | 152 ++++++ .../Queue/WordRAM/RegisterTransfer.lean | 221 +++++++++ ComplexityTests/QueueMemoryTransfer.lean | 69 +++ ComplexityTests/QueueRegisterCorrectness.lean | 72 +++ ComplexityTests/QueueRegisterDynamic.lean | 83 ++++ ComplexityTests/QueueRegisterExecution.lean | 106 +++++ ComplexityTests/QueueRegisterMethods.lean | 129 +++++ ComplexityTests/QueueRegisterProgram.lean | 44 ++ ComplexityTests/QueueRegisterTransfer.lean | 112 +++++ ComplexityTests/WordRAMQueue.lean | 41 +- ROADMAP.md | 8 +- UPSTREAM.md | 13 +- 21 files changed, 2275 insertions(+), 584 deletions(-) create mode 100644 Complexity/DataStructure/Queue/Memory/Transfer.lean delete mode 100644 Complexity/DataStructure/Queue/WordRAM/Basic.lean delete mode 100644 Complexity/DataStructure/Queue/WordRAM/Dynamic.lean create mode 100644 Complexity/DataStructure/Queue/WordRAM/RegisterCorrectness.lean create mode 100644 Complexity/DataStructure/Queue/WordRAM/RegisterDynamic.lean create mode 100644 Complexity/DataStructure/Queue/WordRAM/RegisterExecution.lean create mode 100644 Complexity/DataStructure/Queue/WordRAM/RegisterMetadata.lean create mode 100644 Complexity/DataStructure/Queue/WordRAM/RegisterMethods.lean create mode 100644 Complexity/DataStructure/Queue/WordRAM/RegisterProgram.lean create mode 100644 Complexity/DataStructure/Queue/WordRAM/RegisterTransfer.lean create mode 100644 ComplexityTests/QueueMemoryTransfer.lean create mode 100644 ComplexityTests/QueueRegisterCorrectness.lean create mode 100644 ComplexityTests/QueueRegisterDynamic.lean create mode 100644 ComplexityTests/QueueRegisterExecution.lean create mode 100644 ComplexityTests/QueueRegisterMethods.lean create mode 100644 ComplexityTests/QueueRegisterProgram.lean create mode 100644 ComplexityTests/QueueRegisterTransfer.lean diff --git a/Complexity.lean b/Complexity.lean index 50e848e649..9ab10d761d 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -3013,10 +3013,16 @@ 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.Memory.Transfer public import Complexity.DataStructure.Queue.Spec -public import Complexity.DataStructure.Queue.WordRAM.Basic -public import Complexity.DataStructure.Queue.WordRAM.Dynamic public import Complexity.DataStructure.Queue.WordRAM.Model +public import Complexity.DataStructure.Queue.WordRAM.RegisterCorrectness +public import Complexity.DataStructure.Queue.WordRAM.RegisterDynamic +public import Complexity.DataStructure.Queue.WordRAM.RegisterExecution +public import Complexity.DataStructure.Queue.WordRAM.RegisterMetadata +public import Complexity.DataStructure.Queue.WordRAM.RegisterMethods +public import Complexity.DataStructure.Queue.WordRAM.RegisterProgram +public import Complexity.DataStructure.Queue.WordRAM.RegisterTransfer public import Complexity.DataStructure.Retroactive.Basic public import Complexity.DataStructure.SearchTree.Basic public import Complexity.DataStructure.SearchTree.Cost diff --git a/Complexity/DataStructure/Queue/Memory/Transfer.lean b/Complexity/DataStructure/Queue/Memory/Transfer.lean new file mode 100644 index 0000000000..1e01f9a4c3 --- /dev/null +++ b/Complexity/DataStructure/Queue/Memory/Transfer.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: candidate + +public import Complexity.Computability.WordRAM.Realization.While +public import Complexity.DataStructure.Queue.Memory + +/-! +# Early-exit transfer for the memory queue + +The remaining number of rear elements is explicit loop state. A rear pop returning `none` +sets it to zero, so the shared bounded while loop stops immediately. The source equalities +preserve the original linked queue program, including its operation history. + +## Main definitions + +* `transferBody`: one rear pop and, when successful, one front push. + +## Main results + +* `transfer_eq_boundedWhile`: the existing transfer program is the shared early-exit loop. +* `run_eq_allocPair_bind`: allocation followed by the original linked queue client. +* `transfer_invariant`: the existing framed product preserves both stacks and moves the rear. + +## References + +The queue and its algorithmic references are in `Cslib.DataStructure.Queue.Spec`. +-/ + +@[expose] public section + +namespace Cslib.DataStructure.Queue.Memory + +open Cslib.WordRAM Cslib.WordRAM.Realization Cslib.DataStructure.Memory + +/-- Pop one rear element. An empty rear stops the loop; a successful move decreases the +explicit remaining count. The loop guard excludes a zero count from this body. -/ +def transferBody (d : (Stack.Memory.Layout × Stack.Memory.Layout) × ℕ) : Prog ℕ := do + let a ← Stack.Memory.pop d.1.2 + match a with + | none => pure 0 + | some v => do + Stack.Memory.push d.1.1 v + pure (d.2 - 1) + +/-- Forgetting the loop's remaining count leaves exactly one step of the original transfer. -/ +theorem transferBody_forget (L₁ L₂ : Stack.Memory.Layout) (w C r : ℕ) : + (transferBody ((L₁, L₂), r)).bind (fun _ => pure ()) = + link (Methods.prod (Stack.Memory.methods L₁ w C) (Stack.Memory.methods L₂ w C)) + (Queue.transfer 1) := by + simp only [transferBody, Queue.transfer, link, Methods.prod, Stack.Memory.methods, + PFunctor.FreeM.liftM, PFunctor.FreeM.bind_eq_bind, bind_assoc] + congr 1 + funext a + cases a <;> simp [PFunctor.FreeM.liftM, Methods.prod, Stack.Memory.methods] + +/-- The bounded while has exactly the original source program, with no inactive tail queries +or extra memory operations after an early empty pop. The cutoff bounds the remaining count. -/ +theorem transfer_eq_boundedWhile (L₁ L₂ : Stack.Memory.Layout) (w C fuel n : ℕ) + (hn : n ≤ fuel) : + link (Methods.prod (Stack.Memory.methods L₁ w C) (Stack.Memory.methods L₂ w C)) + (Queue.transfer n) = + (boundedWhile (fun _ r => 0 < r) transferBody (L₁, L₂) fuel n).bind + (fun _ => pure ()) := by + induction fuel generalizing n with + | zero => + have : n = 0 := by omega + subst n + rfl + | succ fuel ih => + cases n with + | zero => simp [Queue.transfer, boundedWhile, link] + | succ n => + simp only [Queue.transfer, link, PFunctor.FreeM.liftM, Methods.prod, + Stack.Memory.methods, boundedWhile, Nat.zero_lt_succ, ↓reduceIte, transferBody, + Nat.add_sub_cancel, PFunctor.FreeM.bind_eq_bind, bind_assoc] + congr 1 + funext a + cases a with + | none => simp [boundedWhile_of_not] + | some v => + simp only [PFunctor.FreeM.liftM] + exact congrArg (fun p => (Stack.Memory.push L₁ v).bind (fun _ => p)) + (ih n (by omega)) + +/-- The current allocator allocates its two stack handles and then links the unchanged queue +methods through their product. This is equality of programs, before selecting a memory state. -/ +theorem run_eq_allocPair_bind (w C : ℕ) {α : Type} + (p : (Queue.WordRAM.spec w C).Client α) : + run w C p = (Allocator.allocPair (Stack.Memory.allocator w C) + (Stack.Memory.allocator w C)).bind (fun L => link ((allocator w C).methods L) p) := rfl + +/-- The source transfer preserves the two existing storage invariants and reverses the rear +into the front. The generic framed product supplies noninterference; no replacement invariant +or instruction-level proof is needed. -/ +theorem transfer_invariant {L₁ L₂ : Stack.Memory.Layout} {w C n : ℕ} + (h₁ : L₁.Valid w C) (h₂ : L₂.Valid w C) (hd : Disjoint L₁.footprint L₂.footprint) + {s : State} (hw : WellFormed w s) + (hs₁ : Stack.Memory.Inv L₁ s) (hs₂ : Stack.Memory.Inv L₂ s) + (hn : s.mem L₂.count.base = n) + (hlen : s.mem L₁.count.base + s.mem L₂.count.base ≤ C) : + let p := link (Methods.prod (Stack.Memory.methods L₁ w C) + (Stack.Memory.methods L₂ w C)) (Queue.transfer n) + let s' := (spec w).clientState s p + (spec w).ValidHistory s ((spec w).clientHistory s p) ∧ + WellFormed w s' ∧ Stack.Memory.Inv L₁ s' ∧ Stack.Memory.Inv L₂ s' ∧ + s'.mem L₂.count.base = 0 ∧ s'.mem L₁.count.base = s.mem L₁.count.base + n ∧ + Stack.Memory.abs L₁ s' = (Stack.Memory.abs L₂ s).reverse ++ Stack.Memory.abs L₁ s := by + let M := (Stack.Memory.framed h₁).prod (Stack.Memory.framed h₂) + have hi : M.inv s := ⟨hs₁, hs₂, hd, by + change Cslib.WordRAM.Fits w (s.frontier + (0 + 0)) + simpa only [Nat.add_zero] using hw.frontier_fits⟩ + have hn' : n = (Stack.Memory.abs L₂ s).length := by + rw [Stack.Memory.length_abs, hn] + have hv : (boundedStacks w C).ValidHistory (M.abs s) + ((boundedStacks w C).clientHistory (M.abs s) (Queue.transfer n)) := by + rw [clientHistory_boundedStacks, hn'] + change (boundedStacks w C).ValidHistory (Stack.Memory.abs L₁ s, Stack.Memory.abs L₂ s) + ((stackPair ℕ).runClient (Stack.Memory.abs L₁ s, Stack.Memory.abs L₂ s) + (Queue.transfer (Stack.Memory.abs L₂ s).length)).2.2 + rw [Queue.transfer_run] + apply validHistory_transferTrace + · simpa only [Stack.Memory.length_abs] using hlen + · intro v hv + simp only [Stack.Memory.abs, List.mem_reverse, Cslib.WordRAM.Block.entries, + List.mem_map] at hv + obtain ⟨i, _, rfl⟩ := hv + exact hw.mem_fits _ + obtain ⟨hvE, _, habs, hsE⟩ := M.toRefinement.simulates_client (Queue.transfer n) hi hv + have habs' : M.abs ((spec w).clientState s (link M.methods (Queue.transfer n))) = + ((Stack.Memory.abs L₂ s).reverse ++ Stack.Memory.abs L₁ s, []) := by + rw [habs, clientState_boundedStacks, hn'] + exact congrArg (fun t => t.2.1) (Queue.transfer_run (Stack.Memory.abs L₁ s) + (Stack.Memory.abs L₂ s)) + let s' := (spec w).clientState s (link (Methods.prod (Stack.Memory.methods L₁ w C) + (Stack.Memory.methods L₂ w C)) (Queue.transfer n)) + have hfront : Stack.Memory.abs L₁ s' = + (Stack.Memory.abs L₂ s).reverse ++ Stack.Memory.abs L₁ s := congrArg Prod.fst habs' + have hrear : Stack.Memory.abs L₂ s' = [] := congrArg Prod.snd habs' + have hfrontLength := congrArg List.length hfront + have hrearLength := congrArg List.length hrear + simp only [Stack.Memory.length_abs, List.length_append, List.length_reverse, + List.length_nil, hn] at hfrontLength hrearLength + refine ⟨hvE, ?_, hsE.1, hsE.2.1, hrearLength, ?_, hfront⟩ + · rw [← (spec w).run_runClient] + exact hw.run hvE + · simpa only [Nat.add_comm] using hfrontLength + +end Cslib.DataStructure.Queue.Memory diff --git a/Complexity/DataStructure/Queue/WordRAM/Basic.lean b/Complexity/DataStructure/Queue/WordRAM/Basic.lean deleted file mode 100644 index b573762272..0000000000 --- a/Complexity/DataStructure/Queue/WordRAM/Basic.lean +++ /dev/null @@ -1,395 +0,0 @@ -/- -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.Computability.WordRAM.Dynamic - -/-! -# Two stacks implementing a queue in shared RAM - -Two disjoint reserved blocks hold bottom-first stack arrays. Enqueue pushes onto the rear; -dequeue transfers the rear into an empty front before popping. All address arithmetic, memory -accesses, loop tests and operation dispatch are machine instructions. Empty live prefixes do -not require clearing the reserved memory. - -## Main definitions - -* `tag`, `arg`, `frontLen`, `frontBase`, `backLen`, `backBase`, `addr`, `out`, `present`, `zero`, - `one`: register allocation for the two-stack queue. -* `Rep`, `footprint`: the bounded shared-memory representation of the two top-first lists and its - reserved memory footprint. -* `enqueue`, `move`, `transfer`, `pop`, `dequeue`, `program`, `bodyCost`: structured queue - operations, the fixed operation dispatcher, and its per-operation instruction count. -* `implementation`: its argument and result conventions. -* `initializeCode`: six charged writes installing an empty representation. - -## Main results - -* `execute`: exact instruction execution, observations, representation and memory frame. -* `initialize_exec`: initialization from arbitrary bounded machine data without clearing memory. - -## References - -* [Cormen, Leiserson, Rivest and Stein, *Introduction to Algorithms*][CLRS2009]. --/ - -@[expose] public section - -namespace Cslib.DataStructure.Queue.WordRAM - -open Cslib.WordRAM Cslib.DataStructure.Queue - -/-- Operation tag: enqueue zero, dequeue one. -/ -abbrev tag : Fin 11 := 0 -/-- Enqueued word. -/ -abbrev arg : Fin 11 := 1 -/-- Front stack length. -/ -abbrev frontLen : Fin 11 := 2 -/-- Front block base. -/ -abbrev frontBase : Fin 11 := 3 -/-- Rear stack length. -/ -abbrev backLen : Fin 11 := 4 -/-- Rear block base. -/ -abbrev backBase : Fin 11 := 5 -/-- Temporary address. -/ -abbrev addr : Fin 11 := 6 -/-- Returned word. -/ -abbrev out : Fin 11 := 7 -/-- Returned presence flag. -/ -abbrev present : Fin 11 := 8 -/-- Constant zero. -/ -abbrev zero : Fin 11 := 9 -/-- Constant one. -/ -abbrev one : Fin 11 := 10 - -/-- Both stack arrays live in one bounded memory, with one common capacity bound. -/ -structure Rep (w base capacity : ℕ) (front back : List ℕ) (x : Data 11) : Prop where - bounded : x.Bounded w - frontBase_eq : x.regs frontBase = base - backBase_eq : x.regs backBase = base + capacity - frontLen_eq : x.regs frontLen = front.length - backLen_eq : x.regs backLen = back.length - zero_eq : x.regs zero = 0 - one_eq : x.regs one = 1 - size_le : front.length + back.length ≤ capacity - front_contents : ∀ i : Fin front.length, x.mem (base + i.val) = front.reverse[i.val] - back_contents : ∀ i : Fin back.length, - x.mem (base + capacity + i.val) = back.reverse[i.val] - -/-- The fixed reserved region, including currently unused cells. -/ -def footprint (base capacity : ℕ) : Finset ℕ := - (Block.mk base (2 * capacity)).footprint - -/-- Enqueue writes the next rear cell and advances the rear length. -/ -def enqueue : Code .standard 11 := ram[ - addr ← backBase + backLen; *addr ← arg; backLen ← backLen + one; out ← zero ] - -/-- Move the top rear word onto the front stack. -/ -def move : Code .standard 11 := ram[ - backLen ← backLen - one; addr ← backBase + backLen; out ← *addr; - addr ← frontBase + frontLen; *addr ← out; frontLen ← frontLen + one ] - -/-- Reverse the rear stack onto the front, testing each iteration on machine registers. -/ -def transfer : Code .standard 11 := ram[while zero < backLen do [ ⟪move⟫ ]] - -/-- Pop the front, distinguishing the empty queue from a returned zero. -/ -def pop : Code .standard 11 := ram[ - if frontLen = zero then [ out ← zero; present ← zero ] else [ - frontLen ← frontLen - one; addr ← frontBase + frontLen; out ← *addr; present ← one ] ] - -/-- Transfer only when the front is empty. -/ -def dequeue : Code .standard 11 := ram[ - if frontLen = zero then [ ⟪transfer⟫; ⟪pop⟫ ] else [ ⟪pop⟫ ] ] - -/-- One fixed dispatcher, independent of queue contents, capacity, and operation arguments. -/ -def program : Code .standard 11 := ram[ - if tag = zero then [ ⟪enqueue⟫ ] else [ ⟪dequeue⟫ ] ] - -/-- Register calling convention for the fixed queue dispatcher. -/ -def implementation (w capacity : ℕ) : DynamicProgram (spec w capacity) 11 where - code := program - output := out - input i := i == tag || i == arg - encode o i := if i = tag then - match o with | .enqueue _ => 0 | .dequeue => 1 - else match o with | .enqueue v => v | .dequeue => 0 - decode o regs := match o with - | .enqueue _ => some () - | .dequeue => some (if regs present = 0 then none else some (regs out)) - -/-- Enqueue body correctness with an exact four-instruction execution. -/ -private theorem enqueue_exec {w base capacity v : ℕ} {front back : List ℕ} {x : Data 11} - (hc : Fits w (base + 2 * capacity)) (hr : Rep w base capacity front back x) - (hs : front.length + back.length < capacity) (hv : x.regs arg = v) : - ∃ y, Exec w enqueue x y 4 ∧ Rep w base capacity front (v :: back) y ∧ - Modifies (footprint base capacity) x.mem y.mem := by - have ha : Fits w (base + capacity + back.length) := hc.of_le (by omega) - have hl : Fits w (back.length + 1) := hc.of_le (by omega) - let y := ((x.set addr (base + capacity + back.length)).write - (base + capacity + back.length) v).set backLen (back.length + 1) |>.set out 0 - have he : Exec w enqueue x y 4 := by - ram_eval [enqueue, y, hr.backBase_eq, hr.backLen_eq, hr.one_eq, hr.zero_eq, hv, ha, hl] - refine ⟨y, he, ⟨he.bounded hr.bounded, ?_, ?_, ?_, ?_, ?_, ?_, - by simp only [List.length_cons]; omega, ?_, ?_⟩, ?_⟩ - · simpa +decide [y, Data.set, Data.write] using hr.frontBase_eq - · simpa +decide [y, Data.set, Data.write] using hr.backBase_eq - · simpa +decide [y, Data.set, Data.write] using hr.frontLen_eq - · simp +decide [y, Data.set, Data.write] - · simpa +decide [y, Data.set, Data.write] using hr.zero_eq - · simpa +decide [y, Data.set, Data.write] using hr.one_eq - · intro i - have hne : base + i.val ≠ base + capacity + back.length := by have := i.isLt; omega - simpa [y, Data.set, Data.write, Function.update_of_ne hne] using hr.front_contents i - · intro i - by_cases hi : i.val < back.length - · have hne : base + capacity + i.val ≠ base + capacity + back.length := by omega - simpa [y, Data.set, Data.write, List.reverse_cons, List.getElem_append, hi, - Function.update_of_ne hne] using hr.back_contents ⟨i.val, hi⟩ - · have hi' : i.val = back.length := by - have := i.isLt - simp only [List.length_cons] at this - omega - simp [y, Data.set, Data.write, List.reverse_cons, hi'] - · exact Modifies.write_of_owns ⟨by omega, by omega⟩ v - -/-- A rear-to-front move performs six instructions and preserves the reserved-region frame. -/ -private theorem move_exec {w base capacity v : ℕ} {front back : List ℕ} {x : Data 11} - (hc : Fits w (base + 2 * capacity)) (hr : Rep w base capacity front (v :: back) x) : - ∃ y, Exec w move x y 6 ∧ Rep w base capacity (v :: front) back y ∧ - Modifies (footprint base capacity) x.mem y.mem := by - have hs := hr.size_le - simp only [List.length_cons] at hs - have hl : x.regs backLen = back.length + 1 := hr.backLen_eq - have hb : Fits w back.length := hc.of_le (by omega) - have ha : Fits w (base + capacity + back.length) := hc.of_le (by omega) - have hf : Fits w (base + front.length) := hc.of_le (by omega) - have hi : Fits w (front.length + 1) := hc.of_le (by omega) - have hread : x.mem (base + capacity + back.length) = v := by - simpa [List.reverse_cons] using hr.back_contents ⟨back.length, by simp⟩ - let y := (((((x.set backLen back.length).set addr (base + capacity + back.length)).set - out v).set addr (base + front.length)).write (base + front.length) v).set - frontLen (front.length + 1) - have he : Exec w move x y 6 := by - ram_eval [move, y, hr.frontBase_eq, hr.backBase_eq, hr.frontLen_eq, hl, hr.one_eq, - Nat.add_sub_cancel, hb, ha, hf, hi, hread] - refine ⟨y, he, ⟨he.bounded hr.bounded, ?_, ?_, ?_, ?_, ?_, ?_, - by simp only [List.length_cons]; omega, ?_, ?_⟩, ?_⟩ - · simpa +decide [y, Data.set, Data.write] using hr.frontBase_eq - · simpa +decide [y, Data.set, Data.write] using hr.backBase_eq - · simp +decide [y, Data.set, Data.write] - · simp +decide [y, Data.set, Data.write] - · simpa +decide [y, Data.set, Data.write] using hr.zero_eq - · simpa +decide [y, Data.set, Data.write] using hr.one_eq - · intro i - by_cases hij : i.val < front.length - · have hne : base + i.val ≠ base + front.length := by omega - simpa [y, Data.set, Data.write, List.reverse_cons, List.getElem_append, hij, - Function.update_of_ne hne] using hr.front_contents ⟨i.val, hij⟩ - · have hi' : i.val = front.length := by - have := i.isLt - simp only [List.length_cons] at this - omega - simp [y, Data.set, Data.write, List.reverse_cons, hi'] - · intro i - have hne : base + capacity + i.val ≠ base + front.length := by omega - have hr' := hr.back_contents ⟨i.val, by simp⟩ - simpa [y, Data.set, Data.write, List.reverse_cons, List.getElem_append, i.isLt, - Function.update_of_ne hne] using hr' - · exact Modifies.write_of_owns ⟨by omega, by omega⟩ v - -/-- Transfer costs eight instructions per moved word and two instructions to exit the loop. -/ -private theorem transfer_exec {w base capacity : ℕ} {front back : List ℕ} {x : Data 11} - (hc : Fits w (base + 2 * capacity)) (hr : Rep w base capacity front back x) : - ∃ y, Exec w transfer x y (8 * back.length + 2) ∧ - Rep w base capacity (back.reverse ++ front) [] y ∧ - Modifies (footprint base capacity) x.mem y.mem := by - induction back generalizing front x with - | nil => - refine ⟨x, ?_, hr, Modifies.refl _ _⟩ - apply Exec.loop_exit - change ¬x.regs zero < x.regs backLen - rw [hr.zero_eq, hr.backLen_eq] - simp - | cons v back ih => - obtain ⟨y, he, hy, hf⟩ := move_exec hc hr - obtain ⟨z, hz, hrz, hfz⟩ := ih hy - refine ⟨z, ?_, ?_, ?_⟩ - · have he' := Exec.loop_iter (c := Cond.lt zero backLen) (by - change x.regs zero < x.regs backLen - rw [hr.zero_eq, hr.backLen_eq] - simp) he hz - convert he' using 1 - · rfl - · simp only [List.length_cons] - omega - · simpa only [List.reverse_cons, List.append_assoc, List.singleton_append] using hrz - · exact fun a ha => (hfz a ha).trans (hf a ha) - -/-- Front-pop execution counts include the empty/nonempty branch and distinguish returned zero. -/ -private theorem pop_exec {w base capacity : ℕ} {front back : List ℕ} {x : Data 11} - (hc : Fits w (base + 2 * capacity)) (hr : Rep w base capacity front back x) : - ∃ y, Exec w pop x y (if front = [] then 3 else 6) ∧ - Rep w base capacity front.tail back y ∧ - (if y.regs present = 0 then none else some (y.regs out)) = front.head? ∧ - y.mem = x.mem := by - cases front with - | nil => - let y := (x.set out 0).set present 0 - have he : Exec w pop x y 3 := by - ram_eval [pop, y, hr.frontLen_eq, hr.zero_eq] - refine ⟨y, he, ⟨he.bounded hr.bounded, ?_, ?_, ?_, ?_, ?_, ?_, hr.size_le, - fun i => Fin.elim0 i, hr.back_contents⟩, ?_, rfl⟩ - · simpa +decide [y, Data.set] using hr.frontBase_eq - · simpa +decide [y, Data.set] using hr.backBase_eq - · simpa +decide [y, Data.set] using hr.frontLen_eq - · simpa +decide [y, Data.set] using hr.backLen_eq - · simpa +decide [y, Data.set] using hr.zero_eq - · simpa +decide [y, Data.set] using hr.one_eq - · simp [y, Data.set] - | cons v front => - have hs := hr.size_le - simp only [List.length_cons] at hs - have hl : x.regs frontLen = front.length + 1 := hr.frontLen_eq - have hf : Fits w front.length := hc.of_le (by omega) - have ha : Fits w (base + front.length) := hc.of_le (by omega) - have hread : x.mem (base + front.length) = v := by - simpa [List.reverse_cons] using hr.front_contents ⟨front.length, by simp⟩ - let y := (((x.set frontLen front.length).set addr (base + front.length)).set out v).set - present 1 - have he : Exec w pop x y 6 := by - ram_eval [pop, y, hl, hr.zero_eq, hr.one_eq, hr.frontBase_eq, hread, hf, ha, - Nat.add_sub_cancel, Nat.add_eq_zero_iff, and_false] - refine ⟨y, he, ⟨he.bounded hr.bounded, ?_, ?_, ?_, ?_, ?_, ?_, - by change front.length + back.length ≤ capacity; omega, - ?_, hr.back_contents⟩, ?_, rfl⟩ - · simpa +decide [y, Data.set] using hr.frontBase_eq - · simpa +decide [y, Data.set] using hr.backBase_eq - · simp +decide [y, Data.set] - · simpa +decide [y, Data.set] using hr.backLen_eq - · simpa +decide [y, Data.set] using hr.zero_eq - · simpa +decide [y, Data.set] using hr.one_eq - · intro i - have hij : i.val < front.length := i.isLt - have hr' := hr.front_contents ⟨i.val, by simp only [List.length_cons]; omega⟩ - simpa [y, Data.set, List.reverse_cons, List.getElem_append, hij] using hr' - · simp +decide [y, Data.set] - -/-- Exact instruction count of a dispatched operation, excluding the final halt. -/ -def bodyCost (front back : List ℕ) : QueueOp ℕ → ℕ - | .enqueue _ => 5 - | .dequeue => if front = [] then 8 * back.length + (if back = [] then 8 else 11) else 10 - -/-- Encoded operation arguments for any valid queue operation fit in a `w`-bit word. -/ -theorem arguments_fit {w capacity : ℕ} (h1 : Fits w 1) {q : List ℕ} (o : QueueOp ℕ) - (hv : (spec w capacity).valid q o) (i : Fin 11) : - Fits w ((implementation w capacity).encode o i) := by - cases o with - | enqueue v => - change Fits w (if i = tag then 0 else v) - split - · exact fits_zero w - · exact hv.2 - | dequeue => - change Fits w (if i = tag then 1 else 0) - split - · exact h1 - · exact fits_zero w - -private theorem prepare_rep {w base capacity : ℕ} {front back : List ℕ} {x : Data 11} - (hr : Rep w base capacity front back x) (o : QueueOp ℕ) - (hv : (spec w capacity).valid (contents (front, back)) o) : - Rep w base capacity front back ((implementation w capacity).prepare o x) := by - have hb : ((implementation w capacity).prepare o x).Bounded w := - (implementation w capacity).prepare_bounded hr.bounded - (fun i _ => arguments_fit (hr.one_eq ▸ hr.bounded.1 one) o hv i) - refine ⟨hb, ?_, ?_, ?_, ?_, ?_, ?_, hr.size_le, hr.front_contents, hr.back_contents⟩ - all_goals first - | simpa +decide [implementation, DynamicProgram.prepare] using hr.frontBase_eq - | simpa +decide [implementation, DynamicProgram.prepare] using hr.backBase_eq - | simpa +decide [implementation, DynamicProgram.prepare] using hr.frontLen_eq - | simpa +decide [implementation, DynamicProgram.prepare] using hr.backLen_eq - | simpa +decide [implementation, DynamicProgram.prepare] using hr.zero_eq - | simpa +decide [implementation, DynamicProgram.prepare] using hr.one_eq - -/-- Every valid operation executes the fixed dispatcher with its exact instruction count, -implements the two-stack transition, and preserves every cell outside the reserved region. -/ -theorem execute {w base capacity : ℕ} {front back : List ℕ} {x : Data 11} - (hc : Fits w (base + 2 * capacity)) (hr : Rep w base capacity front back x) - (o : QueueOp ℕ) (hv : (spec w capacity).valid (contents (front, back)) o) : - ∃ y, Exec w program ((implementation w capacity).prepare o x) y (bodyCost front back o) ∧ - (implementation w capacity).decode o y.regs = some (pairStep (front, back) o).2 ∧ - Rep w base capacity (pairStep (front, back) o).1.1 (pairStep (front, back) o).1.2 y ∧ - Modifies (footprint base capacity) x.mem y.mem := by - have hp := prepare_rep hr o hv - cases o with - | enqueue v => - have hs : front.length + back.length < capacity := by - simpa only [contents_length] using hv.1 - have ha : ((implementation w capacity).prepare (.enqueue v) x).regs arg = v := by - simp +decide [implementation, DynamicProgram.prepare] - obtain ⟨y, he, hy, hf⟩ := enqueue_exec hc hp hs ha - refine ⟨y, ?_, rfl, hy, hf⟩ - exact Exec.branch_pos (by - change _ = _ - simp +decide [implementation, DynamicProgram.prepare, hr.zero_eq]) he - | dequeue => - have ht : ¬(Cond.eq tag zero).Holds ((implementation w capacity).prepare .dequeue x) := by - change ¬_ = _ - simp +decide [implementation, DynamicProgram.prepare, hr.zero_eq] - cases front with - | nil => - obtain ⟨y, he, hy, hf⟩ := transfer_exec hc hp - have hy' : Rep w base capacity back.reverse [] y := by simpa using hy - obtain ⟨z, hz, hrz, ha, hm⟩ := pop_exec hc hy' - refine ⟨z, ?_, ?_, hrz, ?_⟩ - · have hbranch := Exec.branch_neg (pos := enqueue) ht - (Exec.branch_pos (c := Cond.eq frontLen zero) (neg := pop) (by - change _ = _ - rw [hp.frontLen_eq, hp.zero_eq] - rfl) (Exec.seq he hz)) - convert hbranch using 1 - · rfl - · simp only [bodyCost, ↓reduceIte, List.reverse_eq_nil_iff] - split <;> omega - · exact congrArg some ha - · intro a hab - rw [hm] - exact hf a hab - | cons v front => - obtain ⟨y, he, hy, ha, hm⟩ := pop_exec hc hp - refine ⟨y, ?_, congrArg some ha, hy, ?_⟩ - · exact Exec.branch_neg ht (Exec.branch_neg (by - change ¬_ = _ - rw [hp.frontLen_eq, hp.zero_eq] - simp) he) - · intro a _ - exact congrFun hm a - -/-- Install an empty queue using six register instructions, with no writes to reserved memory. -/ -def initializeCode (base capacity : ℕ) : Code .standard 11 := ram[ - frontBase ← #base; backBase ← #(base + capacity); frontLen ← #0; backLen ← #0; - zero ← #0; one ← #1 ] - -/-- Initialization accepts arbitrary bounded registers and memory. Only the six representation -registers change; both reserved live prefixes are empty, so memory remains untouched. -/ -theorem initialize_exec {w base capacity : ℕ} {x : Data 11} (hx : x.Bounded w) - (h1 : Fits w 1) (hc : Fits w (base + 2 * capacity)) : - ∃ y, Exec w (initializeCode base capacity) x y 6 ∧ Rep w base capacity [] [] y ∧ - y.mem = x.mem := by - let y := (((((x.set frontBase base).set backBase (base + capacity)).set frontLen 0).set - backLen 0).set zero 0).set one 1 - have hf : Fits w base := hc.of_le (by omega) - have hb : Fits w (base + capacity) := hc.of_le (by omega) - have he : Exec w (initializeCode base capacity) x y 6 := by - ram_eval [initializeCode, y, hf, hb, h1] - refine ⟨y, he, ⟨he.bounded hx, ?_, ?_, ?_, ?_, ?_, ?_, Nat.zero_le _, - fun i => Fin.elim0 i, fun i => Fin.elim0 i⟩, rfl⟩ - all_goals simp +decide [y, Data.set] - -end Cslib.DataStructure.Queue.WordRAM diff --git a/Complexity/DataStructure/Queue/WordRAM/Dynamic.lean b/Complexity/DataStructure/Queue/WordRAM/Dynamic.lean deleted file mode 100644 index e37644998e..0000000000 --- a/Complexity/DataStructure/Queue/WordRAM/Dynamic.lean +++ /dev/null @@ -1,165 +0,0 @@ -/- -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.Basic -public import Complexity.DataStructure.StateTransition.Amortized - -/-! -# Correctness and amortized instruction cost of a RAM queue - -The mathematical client sees only a FIFO list. The realization hides how that list is split -between the two arrays. A dequeue can move every rear element, but each enqueue deposits eight -credits: six instructions move an element and two instructions control that loop iteration. -Consequently every admissible history costs at most fourteen instructions per operation, -plus the initial rear credit. The final unused credit is retained in the inequality. - -These are actual RAM operation instructions, including each call's halt. The operation interface -still uses externally delivered argument registers; arbitrary pure computation in a client's -Lean continuation is not included in this bound. Initialization is a separate six-instruction -program. The arrays reserve twice the queue capacity, while at most one capacity's worth of -values are live. No allocator or dynamically growing capacity is assumed. - -## Main definitions - -* `QueueRep`: the FIFO representation hiding the split into two stacks. -* `certificate`: a semantic realization of the bounded queue interface. -* `potential`: rear-stack credit read from the persistent length register. -* `amortized`: the separate instruction-cost certificate with rear-stack potential. - -## Main results - -* `run_client`: correct adaptive execution and the amortized native instruction bound. -* `frame`: every operation preserves any disjoint heap assertion. - -## References - -* [Cormen, Leiserson, Rivest and Stein, *Introduction to Algorithms*, Chapters 10 and 17][CLRS2009]. --/ - -@[expose] public section - -namespace Cslib.DataStructure.Queue.WordRAM - -open Cslib.WordRAM Cslib.DataStructure.Queue - -/-- The two reserved blocks contain exactly twice the queue capacity in word cells. -/ -theorem footprint_card (base capacity : ℕ) : (footprint base capacity).card = 2 * capacity := - Block.card_footprint _ - -/-- A client observes a FIFO list, while the machine retains a hidden split between two stacks. -/ -def QueueRep (w base capacity : ℕ) (q : List ℕ) (x : Data 11) : Prop := - ∃ front back, q = contents (front, back) ∧ Rep w base capacity front back x - -/-- The fixed dispatcher implements the bounded FIFO specification. -/ -def certificate {w base capacity : ℕ} (h1 : Fits w 1) - (hc : Fits w (base + 2 * capacity)) : - DynamicCertificate (implementation w capacity) w := - DynamicCertificate.ofFn - (rep := QueueRep w base capacity) - (bounded := fun h => by obtain ⟨_, _, _, hr⟩ := h; exact hr.bounded) - (arguments_fit := fun o hv i _ => arguments_fit h1 o hv i) - (execute := fun o hr hv => by - obtain ⟨front, back, rfl, hr⟩ := hr - obtain ⟨y, he, ha, hy, _⟩ := execute hc hr o hv - have hs := pairStep_correct (front, back) o - refine ⟨y, _, he, ?_, ?_⟩ - · exact ha.trans (congrArg some (congrArg Prod.snd hs)) - · exact ⟨_, _, (congrArg Prod.fst hs).symm, hy⟩) - -/-- The expensive transfer consumes eight credits per rear element, including loop control. -The final halt contributes the extra one in the left side. -/ -private theorem bodyCost_potential (front back : List ℕ) (o : QueueOp ℕ) : - bodyCost front back o + 1 + 8 * (pairStep (front, back) o).1.2.length ≤ - 14 + 8 * back.length := by - cases o with - | enqueue v => simp only [bodyCost, pairStep, List.length_cons]; omega - | dequeue => cases front <;> cases back <;> - simp only [bodyCost, pairStep, List.cons_ne_nil, ↓reduceIte, - List.length_nil, List.length_cons] <;> omega - -/-- Every native operation has the exact charged body cost plus its final halt. -/ -theorem instructions_eq {w base capacity : ℕ} (hc : Fits w (base + 2 * capacity)) - {front back : List ℕ} {x y : Data 11} {o : QueueOp ℕ} - {a : (spec w capacity).Result o} {trace : DynamicProgram.OperationTrace} - (hr : Rep w base capacity front back x) - (hv : (spec w capacity).valid (contents (front, back)) o) - (he : (implementation w capacity).ExecOp w x o a y trace) : - trace.instructions = bodyCost front back o + 1 := by - obtain ⟨z, hz, _, _, _⟩ := execute hc hr o hv - exact (he.eq_of_exec hz).2 - -/-- Rear-stack credit is read from the concrete persistent length register. -/ -def potential (x : Data 11) : ℕ := 8 * x.regs backLen - -/-- Amortized constant instruction cost is certified separately from FIFO correctness. -/ -theorem amortized {w base capacity : ℕ} (h1 : Fits w 1) - (hc : Fits w (base + 2 * capacity)) : - DataStructure.Amortized (certificate h1 hc).impl - DynamicProgram.OperationTrace.instructions (fun _ x => potential x) (fun _ _ => 14) := - DataStructure.Amortized.ofFn fun hr hv he => by - obtain ⟨front, back, rfl, hr⟩ := hr - obtain ⟨z, hz, _, hrz, _⟩ := execute hc hr _ hv - obtain ⟨rfl, hn⟩ := he.eq_of_exec hz - rw [hn] - simpa only [potential, hr.backLen_eq, hrz.backLen_eq] using - bodyCost_potential front back _ - -/-- Every admissible adaptive client has a correct native execution with a telescoping bound. -This sums operation calls; the externally supplied client controller is not machine code. -/ -theorem run_client {w base capacity : ℕ} (h1 : Fits w 1) - (hc : Fits w (base + 2 * capacity)) {α : Type} (p : (spec w capacity).Client α) - {q : List ℕ} {x : Data 11} (hr : QueueRep w base capacity q x) - (hv : (spec w capacity).ValidHistory q ((spec w capacity).runClient q p).2.2) : - ∃ y traces, DataStructure.Executes ((implementation w capacity).ExecOp w) p x - ((spec w capacity).runClient q p).1 y traces ∧ - QueueRep w base capacity ((spec w capacity).runClient q p).2.1 y ∧ - DataStructure.total DynamicProgram.OperationTrace.instructions traces + potential y ≤ - ((spec w capacity).runClient q p).2.2.length * 14 + potential x := - (amortized h1 hc).exists_executes_const p hr hv - -/-- Starting with arbitrary bounded data, charge the six initialization instructions before -summing the actual queue calls. Empty regions need no zeroing; controller computation and -external argument delivery retain the boundary stated in `run_client`. -/ -theorem run_initialized {w base capacity : ℕ} (h1 : Fits w 1) - (hc : Fits w (base + 2 * capacity)) {α : Type} (p : (spec w capacity).Client α) - {x : Data 11} (hx : x.Bounded w) - (hv : (spec w capacity).ValidHistory [] ((spec w capacity).runClient [] p).2.2) : - ∃ ready y traces, Exec w (initializeCode base capacity) x ready 6 ∧ - DataStructure.Executes ((implementation w capacity).ExecOp w) p ready - ((spec w capacity).runClient [] p).1 y traces ∧ - QueueRep w base capacity ((spec w capacity).runClient [] p).2.1 y ∧ - 6 + DataStructure.total DynamicProgram.OperationTrace.instructions traces + potential y ≤ - 6 + ((spec w capacity).runClient [] p).2.2.length * 14 := by - obtain ⟨ready, hi, hr, _⟩ := initialize_exec hx h1 hc - have hp : potential ready = 0 := by simp [potential, hr.backLen_eq] - obtain ⟨y, traces, he, hy, hb⟩ := run_client (q := []) h1 hc p ⟨[], [], rfl, hr⟩ hv - exact ⟨ready, y, traces, hi, he, hy, by omega⟩ - -/-- Actual queue calls modify only the two reserved array regions. -/ -theorem modifies {w base capacity : ℕ} - (hc : Fits w (base + 2 * capacity)) {q : List ℕ} {x y : Data 11} {o : QueueOp ℕ} - {a : (spec w capacity).Result o} {trace : DynamicProgram.OperationTrace} - (hr : QueueRep w base capacity q x) (hv : (spec w capacity).valid q o) - (he : (implementation w capacity).ExecOp w x o a y trace) : - Modifies (footprint base capacity) x.mem y.mem := by - obtain ⟨front, back, rfl, hr⟩ := hr - obtain ⟨z, hz, _, _, hf⟩ := execute hc hr o hv - exact (he.eq_of_exec hz).1 ▸ hf - -/-- Disjoint heap representations survive every queue call, including a full rear transfer. -/ -theorem frame {w base capacity : ℕ} - (hc : Fits w (base + 2 * capacity)) {q : List ℕ} {x y : Data 11} {o : QueueOp ℕ} - {a : (spec w capacity).Result o} {trace : DynamicProgram.OperationTrace} - (hr : QueueRep w base capacity q x) (hv : (spec w capacity).valid q o) - (he : (implementation w capacity).ExecOp w x o a y trace) - {Q : HeapAssertion} (hQ : Q.holds x.mem) - (hd : Disjoint Q.footprint (footprint base capacity)) : Q.holds y.mem := - HeapAssertion.frame hQ hd (modifies hc hr hv he) - -end Cslib.DataStructure.Queue.WordRAM diff --git a/Complexity/DataStructure/Queue/WordRAM/RegisterCorrectness.lean b/Complexity/DataStructure/Queue/WordRAM/RegisterCorrectness.lean new file mode 100644 index 0000000000..914d9d6ad8 --- /dev/null +++ b/Complexity/DataStructure/Queue/WordRAM/RegisterCorrectness.lean @@ -0,0 +1,141 @@ +/- +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.RegisterMethods + +/-! +# FIFO correctness of the physical-array memory programs + +The count inputs and results are ordinary values; the physical memory assertions reuse +`Block.HoldsList`. The conclusions identify the existing `pairStep` on the front and rear +lists, including empty dequeue and a returned zero. No count cell or stronger room premise is +introduced. The native register representation and paid dispatcher are separate consumers. + +## Main results + +* `enqueue_contents`: the rear write implements a logical stack push and frames the front. +* `popFront_contents`: the top read implements a logical stack pop without clearing memory. +* `dequeue_contents`: transfer and pop implement the existing FIFO transition `pairStep`. + +## References + +The queue and its algorithmic references are in `Cslib.DataStructure.Queue.Spec`. +-/ + +@[expose] public section + +namespace Cslib.DataStructure.Queue.WordRAM.RegisterMetadata + +open Cslib.WordRAM Cslib.WordRAM.Realization + +/-- Enqueue appends one physical rear word while retaining the entire represented front. -/ +theorem enqueue_contents {d : Input} (front back : List ℕ) (s : Memory.State) + (hF : d 0 = front.length) (hB : d 1 = back.length) + (hc : front.length + back.length < d 3) + (hfront : Block.HoldsList (d 2) front.reverse s.mem) + (hback : Block.HoldsList (d 2 + d 3) back.reverse s.mem) : + let t := (Memory.spec 0).clientState s (enqueue d) + (Memory.spec 0).clientResult s (enqueue d) = (front.length, (d 4 :: back).length) ∧ + Block.HoldsList (d 2) front.reverse t.mem ∧ + Block.HoldsList (d 2 + d 3) (d 4 :: back).reverse t.mem := by + refine ⟨?_, ?_, ?_⟩ + · change (d 0, d 1 + 1) = _ + rw [hF, hB] + rfl + · change Block.HoldsList (d 2) front.reverse + (Function.update s.mem (d 2 + d 3 + d 1) (d 4)) + apply hfront.frame + intro a ha hb + rw [Function.update_of_ne] + simp only [List.length_reverse] at hb + omega + · change Block.HoldsList (d 2 + d 3) (d 4 :: back).reverse + (Function.update s.mem (d 2 + d 3 + d 1) (d 4)) + simpa only [hB, List.reverse_cons, List.length_reverse] using hback.append_one (d 4) + +/-- Pop observes the top represented value and retains the unused dirty cell. No memory +clearing, capacity fit or nonzero-value hypothesis is needed for this source equation. -/ +theorem popFront_contents {d : Input} (front back : List ℕ) (s : Memory.State) + (hF : d 0 = front.length) (hB : d 1 = back.length) + (hfront : Block.HoldsList (d 2) front.reverse s.mem) + (hback : Block.HoldsList (d 2 + d 3) back.reverse s.mem) : + (Memory.spec 0).clientResult s (popFront d) = + ((front.tail.length, back.length), front.head?) ∧ + Block.HoldsList (d 2) front.tail.reverse s.mem ∧ + Block.HoldsList (d 2 + d 3) back.reverse s.mem := by + cases front with + | nil => + refine ⟨?_, Block.holdsList_nil _ _, hback⟩ + have hz : d 0 = 0 := hF + rw [clientResult_popFront, ite_eq_left hz, hF, hB] + rfl + | cons a front => + have hn : d 0 ≠ 0 := by rw [hF]; exact Nat.succ_ne_zero _ + have hlen : d 0 - 1 = front.length := by rw [hF]; rfl + have hp : Block.HoldsList (d 2) (front.reverse ++ [a]) s.mem := by + simpa only [List.reverse_cons] using hfront + have hv : s.mem (d 2 + front.length) = a := by + have hv : s.mem (d 2 + front.reverse.length) = a := + (hp.get front.reverse.length (by simp)).trans (List.getElem_concat_length rfl _) + simpa only [List.length_reverse] using hv + refine ⟨?_, ?_, hback⟩ + · rw [clientResult_popFront, ite_eq_right hn, hlen, hB, hv] + rfl + · simpa only [List.tail_cons, List.take_left] using + hp.take front.reverse.length + +/-- The interpreter's actual counts, option answer and physical arrays implement the +existing two-stack transition. The only capacity premise is the original sum bound. -/ +theorem dequeue_contents {w : ℕ} {d : Input} (front back : List ℕ) (s : Memory.State) + (hw : Memory.WellFormed w s) (hF : d 0 = front.length) (hB : d 1 = back.length) + (hc : front.length + back.length ≤ d 3) (hf : d 2 + 2 * d 3 ≤ s.frontier) + (hfront : Block.HoldsList (d 2) front.reverse s.mem) + (hback : Block.HoldsList (d 2 + d 3) back.reverse s.mem) : + let p := pairStep (front, back) .dequeue + let t := (Memory.spec 0).clientState s (dequeue d) + (Memory.spec 0).clientResult s (dequeue d) = ((p.1.1.length, p.1.2.length), p.2) ∧ + Block.HoldsList (d 2) p.1.1.reverse t.mem ∧ + Block.HoldsList (d 2 + d 3) p.1.2.reverse t.mem := by + cases front with + | nil => + have hz : d 0 = 0 := hF + have hdom : transferDomain w d s := by + exact ⟨by rw [hF, hB]; exact hc, hf⟩ + have hi := transfer_invariant hw hdom + have hr : (Memory.spec 0).clientResult s (transfer d) = (back.length, 0) := by + simpa only [hF, hB, List.length_nil, Nat.zero_add] using hi.2.2.1 + let t := (Memory.spec 0).clientState s (transfer d) + have ht : Block.HoldsList (d 2) back.reverse.reverse t.mem := by + simpa only [List.append_nil] using transfer_contents back [] s hF hB hc hfront hback + have hp := popFront_contents (d := ![back.length, 0, d 2, d 3, d 4]) + back.reverse [] t (by simp) rfl ht (Block.holdsList_nil _ _) + have hs : (Memory.spec 0).clientState s (dequeue d) = t := by + rw [dequeue, ite_eq_left hz, StateTransitionSpec.clientState_bind, hr] + exact clientState_popFront _ _ _ + refine ⟨?_, ?_, ?_⟩ + · rw [dequeue, ite_eq_left hz, StateTransitionSpec.clientResult_bind, hr] + exact hp.1 + · rw [hs] + exact hp.2.1 + · rw [hs] + exact hp.2.2 + | cons a front => + have hn : d 0 ≠ 0 := by rw [hF]; exact Nat.succ_ne_zero _ + have hp := popFront_contents (a :: front) back s hF hB hfront hback + have hs : (Memory.spec 0).clientState s (dequeue d) = s := by + rw [dequeue, ite_eq_right hn, clientState_popFront] + refine ⟨?_, ?_, ?_⟩ + · rw [dequeue, ite_eq_right hn] + exact hp.1 + · rw [hs] + exact hp.2.1 + · rw [hs] + exact hp.2.2 + +end Cslib.DataStructure.Queue.WordRAM.RegisterMetadata diff --git a/Complexity/DataStructure/Queue/WordRAM/RegisterDynamic.lean b/Complexity/DataStructure/Queue/WordRAM/RegisterDynamic.lean new file mode 100644 index 0000000000..e798a9055d --- /dev/null +++ b/Complexity/DataStructure/Queue/WordRAM/RegisterDynamic.lean @@ -0,0 +1,112 @@ +/- +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.RegisterExecution + +/-! +# Adaptive clients of the realized resident queue + +The existing dynamic certificate supplies correctness and the generic resource theorem +telescopes the actual native instruction counts with the terminal rear potential. The operation +program, register file and exchange factor precede widths, layouts, dirty memory and adaptive +clients. Initialization is a literal base/capacity code family, fixed before width and entry data; +its six instructions change no physical memory cell. The client controller and external +tag/payload delivery keep the original calling boundary. + +## Main results + +* `amortized`, `run_client`, `run_initialized`: native adaptive execution and resource bounds. +* `modifies`, `frame`: the original two-array footprint and disjoint heap assertions. + +## References + +The queue and its algorithmic references are in `Cslib.DataStructure.Queue.Spec`. +-/ + +@[expose] public section + +namespace Cslib.DataStructure.Queue.WordRAM + +open Cslib.WordRAM Cslib.WordRAM.Realization +open RegisterMetadata + +/-- Both reserved arrays together contain exactly twice the capacity in cells. -/ +theorem footprint_card (base capacity : ℕ) : (footprint base capacity).card = 2 * capacity := + Block.card_footprint _ + +/-- The expensive native transfer consumes the scaled rear credit. Both branches include +their control, count commits and final halt in this globally constant per-call charge. -/ +theorem amortized {w base capacity : ℕ} (h1 : Fits w 1) : + DataStructure.Amortized (certificate (base := base) (capacity := capacity) h1).impl + DynamicProgram.OperationTrace.instructions (fun _ x => potential x) + (fun _ _ => 4 * Backend.factor family + 6) := + DataStructure.Amortized.ofFn fun hr hv he => by + obtain ⟨front, back, rfl, hr⟩ := hr + obtain ⟨z, n, hz, _, _, _, hp⟩ := execute h1 hr _ hv + obtain ⟨rfl, hn⟩ := he.eq_of_exec hz + rw [hn] + exact hp + +/-- Every admissible adaptive client has a correct native execution with terminal potential +retained. This sums calls of the fixed program, without charging a host controller as RAM. -/ +theorem run_client {w base capacity : ℕ} (h1 : Fits w 1) {α : Type} + (p : (spec w capacity).Client α) {q : List ℕ} {x : Data registers} + (hr : QueueRep w base capacity q x) + (hv : (spec w capacity).ValidHistory q ((spec w capacity).runClient q p).2.2) : + ∃ y traces, DataStructure.Executes ((implementation w capacity).ExecOp w) p x + ((spec w capacity).runClient q p).1 y traces ∧ + QueueRep w base capacity ((spec w capacity).runClient q p).2.1 y ∧ + DataStructure.total DynamicProgram.OperationTrace.instructions traces + potential y ≤ + ((spec w capacity).runClient q p).2.2.length * (4 * Backend.factor family + 6) + + potential x := + (amortized h1).exists_executes_const p hr hv + +/-- Starting from arbitrary bounded dirty data, the actual six initializer instructions +establish zero rear credit. The physical end-fit domain is exactly the original `base+2*C`. +No clearing, installed frontier or zero-memory premise is needed. -/ +theorem run_initialized {w base capacity : ℕ} (h1 : Fits w 1) + (hc : Fits w (base + 2 * capacity)) {α : Type} (p : (spec w capacity).Client α) + {x : Data registers} (hx : x.Bounded w) + (hv : (spec w capacity).ValidHistory [] ((spec w capacity).runClient [] p).2.2) : + ∃ ready y traces, Exec w (initializeCode base capacity) x ready 6 ∧ + DataStructure.Executes ((implementation w capacity).ExecOp w) p ready + ((spec w capacity).runClient [] p).1 y traces ∧ + QueueRep w base capacity ((spec w capacity).runClient [] p).2.1 y ∧ + 6 + DataStructure.total DynamicProgram.OperationTrace.instructions traces + potential y ≤ + 6 + ((spec w capacity).runClient [] p).2.2.length * (4 * Backend.factor family + 6) := by + have hr := initialized_rep hx hc + have hp : potential (initialized base capacity x) = 0 := by + simp only [potential, hr.backLen_eq, List.length_nil, Nat.mul_zero] + obtain ⟨y, traces, he, hy, hb⟩ := run_client (q := []) h1 p ⟨[], [], rfl, hr⟩ hv + exact ⟨initialized base capacity x, y, traces, (initialize_exec hx hc).1, + he, hy, by omega⟩ + +/-- Actual native queue calls modify only the original two reserved array regions. -/ +theorem modifies {w base capacity : ℕ} (h1 : Fits w 1) + {q : List ℕ} {x y : Data registers} {o : Queue.QueueOp ℕ} + {a : (spec w capacity).Result o} {trace : DynamicProgram.OperationTrace} + (hr : QueueRep w base capacity q x) (hv : (spec w capacity).valid q o) + (he : (implementation w capacity).ExecOp w x o a y trace) : + Modifies (footprint base capacity) x.mem y.mem := by + obtain ⟨front, back, rfl, hr⟩ := hr + obtain ⟨z, n, hz, _, _, hf, _⟩ := execute h1 hr o hv + exact (he.eq_of_exec hz).1 ▸ hf + +/-- Disjoint heap assertions survive every call, including the native reversal. This is +the original physical-memory frame, not a claim about another structure's frontier invariant. -/ +theorem frame {w base capacity : ℕ} (h1 : Fits w 1) + {q : List ℕ} {x y : Data registers} {o : Queue.QueueOp ℕ} + {a : (spec w capacity).Result o} {trace : DynamicProgram.OperationTrace} + (hr : QueueRep w base capacity q x) (hv : (spec w capacity).valid q o) + (he : (implementation w capacity).ExecOp w x o a y trace) + {Q : HeapAssertion} (hQ : Q.holds x.mem) + (hd : Disjoint Q.footprint (footprint base capacity)) : Q.holds y.mem := + HeapAssertion.frame hQ hd (modifies h1 hr hv he) + +end Cslib.DataStructure.Queue.WordRAM diff --git a/Complexity/DataStructure/Queue/WordRAM/RegisterExecution.lean b/Complexity/DataStructure/Queue/WordRAM/RegisterExecution.lean new file mode 100644 index 0000000000..f2d4159b46 --- /dev/null +++ b/Complexity/DataStructure/Queue/WordRAM/RegisterExecution.lean @@ -0,0 +1,448 @@ +/- +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.RegisterProgram + +/-! +# Paid initialization and execution of the resident queue + +The proof uses the shared Backend's public packing and live-execution facts. In particular, +the maintained count words are already present in native source registers, rather than being +reinstalled by the external controller. Operation code and packing are the globally fixed family +from `RegisterProgram`; the initializer remains parameterized by literal base and capacity. + +## Main results + +* `initialized_rep`: the six register writes establish the empty two-array representation. +* `initialize_queue`: arbitrary dirty bounded data admits paid constant-time initialization. +* `execute_enqueue`, `execute_dequeue`: the same native dispatcher implements both methods. +* `execute`, `certificate`: scaled native payment and the existing dynamic certificate. + +## References + +The queue and its algorithmic references are in `Cslib.DataStructure.Queue.Spec`. +-/ + +@[expose] public section + +namespace Cslib.DataStructure.Queue.WordRAM + +open Cslib.WordRAM Cslib.WordRAM.Realization +open RegisterMetadata + +private theorem src_injective : Function.Injective src := Backend.src_injective family false + +private theorem result_injective : Function.Injective result := by + intro i j h + have he := Backend.tmp_injective family true h + have hv := congrArg Fin.val he + exact Fin.ext hv + +private theorem src_ne_result (i : Fin 5) (j : Fin 4) : src i ≠ result j := + Backend.src_ne_tmp family false true i _ + +private theorem src_ne_frontier (i : Fin 5) : src i ≠ Backend.frontier family := + Backend.src_ne_frontier family false i + +private theorem result_ne_frontier (i : Fin 4) : result i ≠ Backend.frontier family := + Backend.tmp_ne_frontier family true _ + +private theorem tag_ne_zero : tag ≠ zero := by + intro h + have hv := congrArg Fin.val (result_injective h) + change (0 : ℕ) = 1 at hv + omega + +private theorem prepare_frame (w capacity : ℕ) (o : Queue.QueueOp ℕ) (x : Data registers) + (i : Fin registers) (ht : i ≠ tag) (ha : i ≠ src 4) : + ((implementation w capacity).prepare o x).regs i = x.regs i := by + apply DynamicProgram.prepare_regs_of_not_input + simp [implementation, ht, ha] + +private theorem prepare_src (w capacity : ℕ) (o : Queue.QueueOp ℕ) (x : Data registers) + (i : Fin 5) : + ((implementation w capacity).prepare o x).regs (src i) = + if i = 4 then (match o with | .enqueue v => v | .dequeue => 0) else x.regs (src i) := by + have ht : src i ≠ tag := src_ne_result i 0 + have hs : src i = src 4 ↔ i = 4 := src_injective.eq_iff + cases o <;> simp [DynamicProgram.prepare, implementation, ht, hs] + +private theorem prepare_tag (w capacity : ℕ) (o : Queue.QueueOp ℕ) (x : Data registers) : + ((implementation w capacity).prepare o x).regs tag = + match o with | .enqueue _ => 0 | .dequeue => 1 := by + cases o <;> simp [DynamicProgram.prepare, implementation] + +private theorem prepare_frontier (w capacity : ℕ) (o : Queue.QueueOp ℕ) + (x : Data registers) : + ((implementation w capacity).prepare o x).regs (Backend.frontier family) = + x.regs (Backend.frontier family) := + prepare_frame _ _ _ _ _ (result_ne_frontier 0).symm (src_ne_frontier 4).symm + +private theorem encoded_fits {w capacity : ℕ} (h1 : Fits w 1) (o : Queue.QueueOp ℕ) + (hv : match o with | .enqueue v => Fits w v | .dequeue => True) + (i : Fin registers) : Fits w ((implementation w capacity).encode o i) := by + cases o <;> dsimp only [implementation] <;> split + · exact fits_zero w + · exact hv + · exact h1 + · exact fits_zero w + +private theorem prepare_bounded {w capacity : ℕ} (o : Queue.QueueOp ℕ) + {x : Data registers} (hx : x.Bounded w) (h1 : Fits w 1) + (hv : match o with | .enqueue v => Fits w v | .dequeue => True) : + ((implementation w capacity).prepare o x).Bounded w := + DynamicProgram.prepare_bounded _ hx (fun i _ => encoded_fits h1 o hv i) + +private noncomputable def staged (w capacity : ℕ) (o : Queue.QueueOp ℕ) + (x : Data registers) : Data registers := + ((implementation w capacity).prepare o x).set zero 0 + +private theorem stage_exec (w capacity : ℕ) (o : Queue.QueueOp ℕ) (x : Data registers) : + Exec (B := .standard) w (.op (.const zero 0)) ((implementation w capacity).prepare o x) + (staged w capacity o x) 1 := Exec.const (fits_zero w) + +private theorem staged_bounded {w capacity : ℕ} (o : Queue.QueueOp ℕ) + {x : Data registers} (hx : x.Bounded w) (h1 : Fits w 1) + (hv : match o with | .enqueue v => Fits w v | .dequeue => True) : + (staged w capacity o x).Bounded w := + (stage_exec w capacity o x).bounded (prepare_bounded o hx h1 hv) + +private theorem staged_input {w base capacity : ℕ} {front back : List ℕ} {x : Data registers} + (hr : Rep w base capacity front back x) (o : Queue.QueueOp ℕ) : + (staged w capacity o x).regs ∘ src = + ![front.length, back.length, base, capacity, + match o with | .enqueue v => v | .dequeue => 0] := by + funext i + change Function.update _ zero 0 (src i) = _ + rw [Function.update_of_ne (src_ne_result i 1), prepare_src] + fin_cases i <;> simp [hr.frontLen_eq, hr.backLen_eq, hr.base_eq, hr.capacity_eq] + +private theorem staged_memory (w capacity : ℕ) (o : Queue.QueueOp ℕ) (x : Data registers) : + memoryState (Backend.frontier family) (staged w capacity o x) = + memoryState (Backend.frontier family) x := by + refine Memory.State.ext ?_ ?_ + · change ((implementation w capacity).prepare o x).mem = x.mem + exact DynamicProgram.prepare_mem _ _ _ + · change Function.update _ zero 0 (Backend.frontier family) = _ + rw [Function.update_of_ne (result_ne_frontier 1).symm] + exact prepare_frontier _ _ _ _ + +private theorem staged_tag (w capacity : ℕ) (o : Queue.QueueOp ℕ) (x : Data registers) : + (staged w capacity o x).regs tag = + match o with | .enqueue _ => 0 | .dequeue => 1 := by + change Function.update _ zero 0 tag = _ + rw [Function.update_of_ne tag_ne_zero] + exact prepare_tag _ _ _ _ + +private noncomputable def committed (m : ℕ × ℕ) (x : Data registers) : Data registers := + (x.set (src 0) m.1).set (src 1) m.2 + +private theorem commit_exec {w : ℕ} {x : Data registers} {m : ℕ × ℕ} + (h0 : x.regs (result 0) = m.1) (h1 : x.regs (result 1) = m.2) : + Exec w commit x (committed m x) 2 := by + have h0' : Exec (B := .standard) w (.op (.copy (src 0) (result 0))) x + (x.set (src 0) m.1) 1 := by + simpa only [h0] using (Exec.copy (B := .standard) (w := w) + (x := x) (d := src 0) (s := result 0)) + have hr : (x.set (src 0) m.1).regs (result 1) = m.2 := by + simpa only [Data.set_regs, Function.update_of_ne (src_ne_result 0 1).symm] using h1 + exact h0'.seq (by + simpa only [hr] using (Exec.copy (B := .standard) (w := w) + (x := x.set (src 0) m.1) (d := src 1) (s := result 1))) + +private theorem committed_other (m : ℕ × ℕ) (x : Data registers) (i : Fin registers) + (h0 : i ≠ src 0) (h1 : i ≠ src 1) : (committed m x).regs i = x.regs i := by + simp only [committed, Data.set_regs, Function.update_of_ne h1, Function.update_of_ne h0] + +private theorem committed_counts (m : ℕ × ℕ) (x : Data registers) : + (committed m x).regs (src 0) = m.1 ∧ (committed m x).regs (src 1) = m.2 := by + have hn : src 0 ≠ src 1 := src_injective.ne (by decide) + simp [committed, Data.set, hn] + +private theorem enqueue_result_eq (i : Fin 2) : + Backend.tmp family false (Fin.castAdd ((family false).realizes.scratchSize + 1) i) = + result (Fin.castAdd 2 i) := Backend.tmp_eq_of_val_eq family false true rfl + +private theorem initialized_input (base capacity : ℕ) (x : Data registers) : + (initialized base capacity x).regs ∘ src = ![0, 0, base, capacity, x.regs (src 4)] := by + have hz (i : Fin 5) : src i ≠ zero := src_ne_result i 1 + have hf (i : Fin 5) : src i ≠ Backend.frontier family := src_ne_frontier i + have hs (i j : Fin 5) : src i = src j ↔ i = j := src_injective.eq_iff + funext i + fin_cases i <;> simp [initialized, Data.set, hz, hf, hs] + +private theorem initialized_frontier (base capacity : ℕ) (x : Data registers) : + (initialized base capacity x).regs (Backend.frontier family) = base + 2 * capacity := by + have hz : Backend.frontier family ≠ zero := (result_ne_frontier 1).symm + have hs (i : Fin 5) : Backend.frontier family ≠ src i := (src_ne_frontier i).symm + simp [initialized, Data.set, hz, hs] + +/-- The actual six writes establish the empty representation under precisely the original +end-fit condition, with no assumption on initial registers or dirty memory. -/ +theorem initialized_rep {w base capacity : ℕ} {x : Data registers} (hx : x.Bounded w) + (hc : Fits w (base + 2 * capacity)) : Rep w base capacity [] [] + (initialized base capacity x) := by + have hin := initialized_input base capacity x + exact ⟨(initialize_exec hx hc).2.1, congrFun hin 2, congrFun hin 3, + initialized_frontier base capacity x, congrFun hin 0, congrFun hin 1, + Nat.zero_le _, Block.holdsList_nil _ _, Block.holdsList_nil _ _⟩ + +/-- Paid raw initialization establishes the existing FIFO meaning and preserves all memory. +The original small-width and capacity-zero cases are included; tag delivery later retains its +separate `Fits w 1` requirement. -/ +theorem initialize_queue {w base capacity : ℕ} {x : Data registers} (hx : x.Bounded w) + (hc : Fits w (base + 2 * capacity)) : + ∃ ready, Exec w (initializeCode base capacity) x ready 6 ∧ + QueueRep w base capacity [] ready ∧ ready.mem = x.mem := by + exact ⟨initialized base capacity x, (initialize_exec hx hc).1, + ⟨[], [], rfl, initialized_rep hx hc⟩, rfl⟩ + +/-- The fixed dispatcher executes enqueue from resident metadata, pays the zero test and +two count copies, and includes the final call halt in its bound. The original two arrays are +the complete memory footprint. -/ +theorem execute_enqueue {w base capacity v : ℕ} {front back : List ℕ} {x : Data registers} + (h1 : Fits w 1) (hr : Rep w base capacity front back x) + (hs : front.length + back.length < capacity) (hv : Fits w v) : + ∃ y n, Exec w program ((implementation w capacity).prepare (.enqueue v) x) y n ∧ + n + 1 ≤ 2 * Backend.factor family + 5 ∧ Rep w base capacity front (v :: back) y ∧ + Modifies (footprint base capacity) x.mem y.mem := by + let d : Input := ![front.length, back.length, base, capacity, v] + let z := staged w capacity (.enqueue v) x + have hdom : enqueueDomain w d (memoryState (Backend.frontier family) x) := by + change front.length + back.length < capacity ∧ + base + 2 * capacity ≤ x.regs (Backend.frontier family) ∧ Fits w v + exact ⟨hs, hr.frontier_eq.ge, hv⟩ + have hz : z.Bounded w := staged_bounded (.enqueue v) hr.bounded h1 hv + have hin : z.regs ∘ Backend.src family false = (family false).input d := + staged_input hr (.enqueue v) + have hdomz : enqueueDomain w d (memoryState (Backend.frontier family) z) := by + rw [staged_memory] + exact hdom + obtain ⟨b, n, hb, hn, hm, ho, hbb, hframe⟩ := Backend.exists_exec_live family false d hz hin + hdomz (valid_enqueue hdomz) + rw [staged_memory] at hm + have hn' : n ≤ 2 * Backend.factor family := by + change n ≤ Backend.factor family * (memoryCost (enqueue d) + (memoryState (Backend.frontier family) z) + 0 + 1) at hn + simpa only [memoryCost_enqueue, Nat.add_zero, Nat.mul_comm] using hn + have h0 : b.regs (result 0) = front.length := + (congrArg b.regs (enqueue_result_eq 0)).symm.trans (ho 0) + have hB : b.regs (result 1) = back.length + 1 := + (congrArg b.regs (enqueue_result_eq 1)).symm.trans (ho 1) + let m := (front.length, back.length + 1) + have hcommit : Exec w commit b (committed m b) 2 := commit_exec h0 hB + have hcond : (Cond.eq tag zero).Holds z := by + change z.regs tag = z.regs zero + have hz0 : z.regs zero = 0 := by + simp only [z, staged, Data.set_regs, Function.update_self] + rw [hz0] + exact staged_tag w capacity (.enqueue v) x + have he : Exec w program ((implementation w capacity).prepare (.enqueue v) x) + (committed m b) (1 + (n + 1 + 2)) := + (stage_exec w capacity (.enqueue v) x).seq ((Exec.branch_pos hcond hb).seq hcommit) + have hsrc (i : Fin 5) : b.regs (src i) = d i := by + rw [hframe _ (src_ne_frontier i) (fun a => (Backend.src_ne_tmp family false false i a).symm)] + exact congrFun hin i + have hfrontier : b.regs (Backend.frontier family) = x.regs (Backend.frontier family) := + congrArg Memory.State.frontier hm + have hmemw : (committed m b).mem = + ((Memory.spec w).clientState (memoryState (Backend.frontier family) x) (enqueue d)).mem := + congrArg Memory.State.mem hm + have hmem : (committed m b).mem = + ((Memory.spec 0).clientState (memoryState (Backend.frontier family) x) (enqueue d)).mem := by + simpa only [clientState_width w] using hmemw + have hcontents := enqueue_contents (d := d) front back + (memoryState (Backend.frontier family) x) rfl rfl hs hr.front_contents hr.back_contents + refine ⟨committed m b, 1 + (n + 1 + 2), he, by omega, ?_, ?_⟩ + · refine ⟨hcommit.bounded hbb, ?_, ?_, ?_, (committed_counts m b).1, + (committed_counts m b).2, ?_, ?_, ?_⟩ + · rw [committed_other m b (src 2) (src_injective.ne (by decide)) + (src_injective.ne (by decide)), hsrc] + rfl + · rw [committed_other m b (src 3) (src_injective.ne (by decide)) + (src_injective.ne (by decide)), hsrc] + rfl + · rw [committed_other m b _ (src_ne_frontier 0).symm (src_ne_frontier 1).symm, + hfrontier, hr.frontier_eq] + · simp only [List.length_cons] + omega + · rw [hmem] + exact hcontents.2.1 + · rw [hmem] + exact hcontents.2.2 + · rw [hmemw] + exact enqueue_modifies hdom + +/-- The negative native branch performs the source transfer/pop, commits both returned +counts, and decodes its faithful option result. The bound includes the negative branch's +two instructions and the call halt, without charging any hidden metadata installation. -/ +theorem execute_dequeue {w base capacity : ℕ} {front back : List ℕ} {x : Data registers} + (h1 : Fits w 1) (hr : Rep w base capacity front back x) : + let p := pairStep (front, back) .dequeue + ∃ y n, Exec w program ((implementation w capacity).prepare .dequeue x) y n ∧ + n + 1 ≤ Backend.factor family * + (memoryCost (dequeue ![front.length, back.length, base, capacity, 0]) + (memoryState (Backend.frontier family) x) + 1) + 6 ∧ + Rep w base capacity p.1.1 p.1.2 y ∧ + (implementation w capacity).decode .dequeue y.regs = some p.2 ∧ + Modifies (footprint base capacity) x.mem y.mem := by + let d : Input := ![front.length, back.length, base, capacity, 0] + let p := pairStep (front, back) .dequeue + let z := staged w capacity .dequeue x + have hdom : transferDomain w d (memoryState (Backend.frontier family) x) := by + change front.length + back.length ≤ capacity ∧ + base + 2 * capacity ≤ x.regs (Backend.frontier family) + exact ⟨hr.size_le, hr.frontier_eq.ge⟩ + have hw : Memory.WellFormed w (memoryState (Backend.frontier family) x) := + wellFormed_memoryState hr.bounded + have hi := dequeue_invariant hw hdom + have hcontents := dequeue_contents (d := d) front back + (memoryState (Backend.frontier family) x) hw rfl rfl hr.size_le hr.frontier_eq.ge + hr.front_contents hr.back_contents + have hz : z.Bounded w := staged_bounded .dequeue hr.bounded h1 trivial + have hin : z.regs ∘ Backend.src family true = (family true).input d := by + funext i + change z.regs (Backend.src family true i) = d i + rw [Backend.src_eq_of_val_eq family true false (a := i) (b := i) rfl] + exact congrFun (staged_input hr .dequeue) i + have hdomz : transferDomain w d (memoryState (Backend.frontier family) z) := by + rw [staged_memory] + exact hdom + obtain ⟨b, n, hb, hn, hm, ho, hbb, hframe⟩ := Backend.exists_exec_live family true d hz hin + hdomz (by rw [staged_memory]; exact hi.1) + rw [staged_memory] at hm hn ho + rw [clientResult_width w, hcontents.1] at ho + have hn' : n ≤ Backend.factor family * + (memoryCost (dequeue d) (memoryState (Backend.frontier family) x) + 1) := by + simpa only [Nat.add_zero] using hn + let m := (p.1.1.length, p.1.2.length) + have h0 : b.regs (result 0) = m.1 := ho 0 + have hB : b.regs (result 1) = m.2 := ho 1 + have hcommit : Exec w commit b (committed m b) 2 := commit_exec h0 hB + have hcond : ¬(Cond.eq tag zero).Holds z := by + change ¬z.regs tag = z.regs zero + have ht : z.regs tag = 1 := staged_tag w capacity .dequeue x + have hz0 : z.regs zero = 0 := by + simp only [z, staged, Data.set_regs, Function.update_self] + rw [ht, hz0] + decide + have he : Exec w program ((implementation w capacity).prepare .dequeue x) + (committed m b) (1 + (n + 2 + 2)) := + (stage_exec w capacity .dequeue x).seq ((Exec.branch_neg hcond hb).seq hcommit) + have hsrc (i : Fin 5) : b.regs (src i) = d i := by + rw [hframe _ (src_ne_frontier i) (fun a => (Backend.src_ne_tmp family false true i a).symm)] + exact congrFun (staged_input hr .dequeue) i + have hfrontier : b.regs (Backend.frontier family) = x.regs (Backend.frontier family) := by + have hf := congrArg Memory.State.frontier hm + rw [clientState_width w] at hf + exact hf.trans hi.2.2.2.1 + have hmem : (committed m b).mem = + ((Memory.spec 0).clientState (memoryState (Backend.frontier family) x) (dequeue d)).mem := by + have hmemw : (committed m b).mem = + ((Memory.spec w).clientState (memoryState (Backend.frontier family) x) (dequeue d)).mem := + congrArg Memory.State.mem hm + rw [clientState_width w] at hmemw + exact hmemw + have hclock : 1 + (n + 2 + 2) + 1 ≤ Backend.factor family * + (memoryCost (dequeue d) (memoryState (Backend.frontier family) x) + 1) + 6 := by omega + refine ⟨committed m b, 1 + (n + 2 + 2), he, hclock, ?_, ?_, ?_⟩ + · refine ⟨hcommit.bounded hbb, ?_, ?_, ?_, (committed_counts m b).1, + (committed_counts m b).2, ?_, ?_, ?_⟩ + · rw [committed_other m b (src 2) (src_injective.ne (by decide)) + (src_injective.ne (by decide)), hsrc] + rfl + · rw [committed_other m b (src 3) (src_injective.ne (by decide)) + (src_injective.ne (by decide)), hsrc] + rfl + · rw [committed_other m b _ (src_ne_frontier 0).symm (src_ne_frontier 1).symm, + hfrontier, hr.frontier_eq] + · have hs := pairStep_length_le (w := w) (capacity := capacity) (s := (front, back)) + (by simpa only [contents_length] using hr.size_le) .dequeue trivial + simpa only [contents_length] using hs + · rw [hmem] + exact hcontents.2.1 + · rw [hmem] + exact hcontents.2.2 + · have ht : (committed m b).regs (result 2) = optionWords p.2 0 := by + rw [committed_other m b _ (src_ne_result 0 2).symm (src_ne_result 1 2).symm] + exact ho 2 + have hv : (committed m b).regs (result 3) = optionWords p.2 1 := by + rw [committed_other m b _ (src_ne_result 0 3).symm (src_ne_result 1 3).symm] + exact ho 3 + change some (if (committed m b).regs (result 2) = 0 then none + else some ((committed m b).regs (result 3))) = some p.2 + rw [ht, hv] + cases p.2 <;> rfl + · rw [hmem] + exact hi.2.2.2.2.2 + +/-- One admissible call of the fixed native queue. The terminal rear potential is retained; +the globally chosen exchange factor, control, metadata copies and halt are all charged. +The external controller and tag/payload delivery remain the original dynamic-call boundary. -/ +theorem execute {w base capacity : ℕ} {front back : List ℕ} {x : Data registers} + (h1 : Fits w 1) (hr : Rep w base capacity front back x) (o : Queue.QueueOp ℕ) + (hv : (spec w capacity).valid (contents (front, back)) o) : + let p := pairStep (front, back) o + ∃ y n, Exec w program ((implementation w capacity).prepare o x) y n ∧ + (implementation w capacity).decode o y.regs = some p.2 ∧ + Rep w base capacity p.1.1 p.1.2 y ∧ + Modifies (footprint base capacity) x.mem y.mem ∧ + n + 1 + potential y ≤ 4 * Backend.factor family + 6 + potential x := by + cases o with + | enqueue v => + change (contents (front, back)).length < capacity ∧ Fits w v at hv + rw [contents_length] at hv + obtain ⟨y, n, he, hn, hry, hm⟩ := execute_enqueue h1 hr hv.1 hv.2 + refine ⟨y, n, he, rfl, hry, hm, ?_⟩ + simp only [potential, hry.backLen_eq, hr.backLen_eq, List.length_cons, + Nat.mul_add, Nat.mul_one] + omega + | dequeue => + obtain ⟨y, n, he, hn, hry, ha, hm⟩ := execute_dequeue h1 hr + let d : Input := ![front.length, back.length, base, capacity, 0] + have hw : Memory.WellFormed w (memoryState (Backend.frontier family) x) := + wellFormed_memoryState hr.bounded + have hdom : transferDomain w d (memoryState (Backend.frontier family) x) := + ⟨hr.size_le, hr.frontier_eq.ge⟩ + have hcontents := dequeue_contents (d := d) front back + (memoryState (Backend.frontier family) x) hw rfl rfl hr.size_le hr.frontier_eq.ge + hr.front_contents hr.back_contents + have hpay := dequeue_memory_amortized hw hdom + rw [hcontents.1] at hpay + change memoryCost (dequeue d) (memoryState (Backend.frontier family) x) + + 2 * (pairStep (front, back) .dequeue).1.2.length ≤ 3 + 2 * back.length at hpay + change n + 1 ≤ Backend.factor family * + (memoryCost (dequeue d) (memoryState (Backend.frontier family) x) + 1) + 6 at hn + refine ⟨y, n, he, ha, hry, hm, ?_⟩ + simp only [potential, hry.backLen_eq, hr.backLen_eq] + nlinarith [Nat.mul_le_mul_left (Backend.factor family) hpay] + +/-- The fixed realized dispatcher implements the original bounded FIFO specification via +the existing dynamic certificate. Its representation retains resident count words; no +memory-only Backend implementation is asserted. The bounded resident frontier equals the physical +end, so its fit already follows from `Rep`; raw initialization keeps its explicit end-fit +premise. -/ +noncomputable def certificate {w base capacity : ℕ} (h1 : Fits w 1) : + DynamicCertificate (implementation w capacity) w := + DynamicCertificate.ofFn + (rep := QueueRep w base capacity) + (bounded := fun h => by obtain ⟨_, _, _, hr⟩ := h; exact hr.bounded) + (arguments_fit := fun o hv i _ => by + exact encoded_fits h1 o (by + cases o with + | enqueue v => exact hv.2 + | dequeue => trivial) i) + (execute := fun o hr hv => by + obtain ⟨front, back, rfl, hr⟩ := hr + obtain ⟨y, n, he, ha, hy, _, _⟩ := execute h1 hr o hv + have hs := pairStep_correct (front, back) o + exact ⟨y, n, he, ha.trans (congrArg some (congrArg Prod.snd hs)), + ⟨_, _, (congrArg Prod.fst hs).symm, hy⟩⟩) + +end Cslib.DataStructure.Queue.WordRAM diff --git a/Complexity/DataStructure/Queue/WordRAM/RegisterMetadata.lean b/Complexity/DataStructure/Queue/WordRAM/RegisterMetadata.lean new file mode 100644 index 0000000000..4060cd91a7 --- /dev/null +++ b/Complexity/DataStructure/Queue/WordRAM/RegisterMetadata.lean @@ -0,0 +1,95 @@ +/- +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.DataStructure.Queue.WordRAM.Model +import Complexity.Tactic.Realize + +/-! +# A physical transfer step with explicit queue count words + +The input words are front count, rear count, base, capacity and payload. The source step reads +one rear-array cell, writes the next front cell, and returns updated counts as values. No count +is stored in physical memory. The shared realization judgment certifies every native arithmetic +and staging instruction while retaining literal whole-memory semantics. + +## Main definitions + +* `Input`, `counts`: five input words and two faithfully encoded returned counts. +* `move`, `domain`: one physical step and the original two-array capacity/room domain. + +## Main results + +* `realizes_move`: one uniform emitter before runtime values and register layouts. +* `runClient_move`, `valid_move`, `move_modifies`: exact history, validity and physical frame. + +## References + +The queue and its algorithmic references are in `Cslib.DataStructure.Queue.Spec`. +-/ + +@[expose] public section + +namespace Cslib.DataStructure.Queue.WordRAM.RegisterMetadata + +open Cslib.WordRAM Cslib.WordRAM.Realization + +/-- Five live input words: front count, rear count, base, capacity, reserved payload. -/ +abbrev Input := Fin 5 → ℕ + +/-- One exact source transfer step; the counts are values, not cells. -/ +def move (d : Input) : Memory.Prog (ℕ × ℕ) := do + let v ← Memory.read (d 2 + d 3 + (d 1 - 1)) + Memory.write (d 2 + d 0) v + pure (d 0 + 1, d 1 - 1) + +/-- The old capacity and allocation envelope, without extra count cells or zero memory. -/ +def domain (_w : ℕ) (d : Input) (s : Memory.State) : Prop := + 0 < d 1 ∧ d 0 + d 1 ≤ d 3 ∧ d 2 + 2 * d 3 ≤ s.frontier + +/-- The two returned count words. -/ +def counts : ℕ × ℕ ↪ (Fin 2 → ℕ) := + appendWords (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + (Equiv.funUnique (Fin 1) ℕ).symm.toEmbedding + +/-- One emitter precedes every count, layout, width and initial memory. All native arithmetic +and vector staging are charged by the memory-query budget; the body performs two queries. -/ +theorem realizes_move : Realizes .exact move (.refl Input) counts + domain (fun _ _ => 0) := by + change Realizes .exact move (.refl (Fin 5 → ℕ)) counts _ _ + realize + · exact h.1 + · exact hmem.fits_of_lt_frontier (by rcases h with ⟨hb, hc, hf⟩; omega) + · exact hmem.fits_of_lt_frontier (by rcases h with ⟨hb, hc, hf⟩; omega) + · exact (show Fits w (d 3) from hwords 3).of_le (by rcases h with ⟨hb, hc, hf⟩; omega) + +/-- The exact source history, including the physical read and write. -/ +theorem runClient_move (w : ℕ) (d : Input) (s : Memory.State) : + (Memory.spec w).runClient s (move d) = + ((d 0 + 1, d 1 - 1), + ⟨Function.update s.mem (d 2 + d 0) (s.mem (d 2 + d 3 + (d 1 - 1))), s.frontier⟩, + [.read (d 2 + d 3 + (d 1 - 1)), + .write (d 2 + d 0) (s.mem (d 2 + d 3 + (d 1 - 1)))]) := rfl + +/-- Every access is within the original two arrays, including the written value's fit. -/ +theorem valid_move {w : ℕ} {d : Input} {s : Memory.State} + (hw : Memory.WellFormed w s) (h : domain w d s) : + (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s (move d)) := by + change (d 2 + d 3 + (d 1 - 1) < s.frontier) ∧ + (d 2 + d 0 < s.frontier ∧ Fits w (s.mem (d 2 + d 3 + (d 1 - 1)))) ∧ True + exact ⟨by rcases h with ⟨hb, hc, hf⟩; omega, + ⟨by rcases h with ⟨hb, hc, hf⟩; omega, hw.mem_fits _⟩, trivial⟩ + +/-- The literal physical-memory frame remains the old `2*C` block. -/ +theorem move_modifies {w : ℕ} {d : Input} {s : Memory.State} (h : domain w d s) : + Modifies (Block.mk (d 2) (2 * d 3)).footprint s.mem + ((Memory.spec w).clientState s (move d)).mem := by + exact Modifies.write_of_owns ⟨by omega, by rcases h with ⟨hb, hc, hf⟩; omega⟩ _ + +end Cslib.DataStructure.Queue.WordRAM.RegisterMetadata diff --git a/Complexity/DataStructure/Queue/WordRAM/RegisterMethods.lean b/Complexity/DataStructure/Queue/WordRAM/RegisterMethods.lean new file mode 100644 index 0000000000..ff57b2d130 --- /dev/null +++ b/Complexity/DataStructure/Queue/WordRAM/RegisterMethods.lean @@ -0,0 +1,289 @@ +/- +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.RegisterTransfer +import Complexity.Tactic.Realize + +/-! +# Queue methods with resident register metadata + +Both methods consume the same five live words: the two stack counts, base, capacity and the +delivered payload. Their returned vectors begin with the two updated counts. Enqueue has no +logical answer word; dequeue appends the faithful option encoding, including a distinct tag for +`some 0`. The memory programs write only the original two physical arrays. + +## Main definitions + +* `enqueue`, `popFront`, `dequeue`: memory programs with explicit count inputs and results. +* `enqueueDomain`: spare capacity, physical room and a word-sized delivered value. + +## Main results + +* `realizes_enqueue`, `realizes_popFront`, `realizes_dequeue`: uniform native method bodies. + +## References + +The queue and its algorithmic references are in `Cslib.DataStructure.Queue.Spec`. +-/ + +@[expose] public section + +namespace Cslib.DataStructure.Queue.WordRAM.RegisterMetadata + +open Cslib.WordRAM Cslib.WordRAM.Realization + +/-- Write the rear's next physical cell and return the two new count words. There is no echo +of the delivered payload in the result. -/ +def enqueue (d : Input) : Memory.Prog (ℕ × ℕ) := do + Memory.write (d 2 + d 3 + d 1) (d 4) + pure (d 0, d 1 + 1) + +/-- The original queue enqueue domain, with the physical room unchanged. -/ +def enqueueDomain (w : ℕ) (d : Input) (s : Memory.State) : Prop := + d 0 + d 1 < d 3 ∧ d 2 + 2 * d 3 ≤ s.frontier ∧ Fits w (d 4) + +/-- An empty front returns none without a memory query. A nonempty front reads its top and +returns a decremented count; the unused physical cell is retained. -/ +def popFront (d : Input) : Memory.Prog ((ℕ × ℕ) × Option ℕ) := + if d 0 = 0 then pure ((d 0, d 1), none) else do + let v ← Memory.read (d 2 + (d 0 - 1)) + pure ((d 0 - 1, d 1), some v) + +/-- The front-empty path transfers the rear before performing the original front pop. -/ +def dequeue (d : Input) : Memory.Prog ((ℕ × ℕ) × Option ℕ) := + if d 0 = 0 then do + let m ← transfer d + popFront ![m.1, m.2, d 2, d 3, d 4] + else popFront d + +/-- One uniform enqueue body, with all address arithmetic, result copies and control charged. -/ +theorem realizes_enqueue : Realizes .exact enqueue (.refl Input) counts + enqueueDomain (fun _ _ => 0) := by + change Realizes .exact enqueue (.refl (Fin 5 → ℕ)) counts _ _ + realize + · exact hmem.fits_of_lt_frontier (by have := h.1; have := h.2.1; omega) + · exact (show Fits w (d 3) from hwords 3).of_le (by have := h.1; omega) + +/-- A uniform typed pop body; the option tag is separate from its full-range word payload. -/ +theorem realizes_popFront : Realizes .exact popFront (.refl Input) + (appendWords counts optionWords) transferDomain (fun _ _ => 0) := by + change Realizes .exact popFront (.refl (Fin 5 → ℕ)) _ _ _ + realize + · omega + · exact hmem.fits_of_lt_frontier (by have := h.1; have := h.2; omega) + · exact (show Fits w (d 0) from hwords 0).of_le (by omega) + +/-- Dequeue reuses the shared remaining-count transfer and preserves the exact old room. -/ +theorem realizes_dequeue : Realizes .exact dequeue (.refl Input) + (appendWords counts optionWords) transferDomain (fun _ _ => 0) := by + change Realizes .exact dequeue (.refl (Fin 5 → ℕ)) _ _ _ + realize + all_goals try omega + case hsafe.isTrue.left => + intro hv + refine whileSafe_of_measure _ _ _ w d + (fun m t => Memory.WellFormed w t ∧ m.1 + m.2 ≤ d 3 ∧ d 2 + 2 * d 3 ≤ t.frontier) + (fun m _ => m.2) (fun m _ _ hz hg => by omega) ?_ ⟨hmem, h.1, h.2⟩ le_rfl hv + rintro m t ⟨hwt, hcap, hfrontier⟩ hg hb + refine ⟨?_, ?_, ?_⟩ + · change (1 ≤ m.2 ∧ Fits w (d 2 + d 3 + (m.2 - 1))) ∧ + Fits w (d 2 + m.1) ∧ 1 ≤ m.2 ∧ Fits w (m.1 + 1) + exact ⟨⟨by omega, hwt.fits_of_lt_frontier (by omega)⟩, + hwt.fits_of_lt_frontier (by omega), by omega, + (show Fits w (d 3) from hwords 3).of_le (by omega)⟩ + · have hw' : Memory.WellFormed w + ((Memory.spec 0).clientState t (move ![m.1, m.2, d 2, d 3, d 4])) := by + rw [← clientState_width w, ← (Memory.spec w).run_runClient] + exact hwt.run hb + refine ⟨hw', ?_, ?_⟩ + · change (m.1 + 1) + (m.2 - 1) ≤ d 3 + omega + · exact hfrontier + · change m.2 - 1 < m.2 + omega + case hsafe.isTrue.right.isFalse.left.right => + have hr := (transfer_invariant hmem h).2.2.1 + rw [hr] at hc ⊢ + change d 0 + d 1 ≠ 0 at hc + change Fits w (d 2 + (d 0 + d 1 - 1)) + exact hmem.fits_of_lt_frontier (by have := h.1; have := h.2; omega) + case hsafe.isTrue.right.isFalse.right.right => + have hr := (transfer_invariant hmem h).2.2.1 + rw [hr] at hc + change d 0 + d 1 ≠ 0 at hc + exact (show Fits w (d 3) from hwords 3).of_le (by have := h.1; omega) + case hsafe.isFalse.isFalse.left.right => + exact hmem.fits_of_lt_frontier (by have := h.1; have := h.2; omega) + case hsafe.isFalse.isFalse.right.right => + exact (show Fits w (d 0) from hwords 0).of_le (by omega) + +/-- The original standard word-RAM basis has the same checked method behavior. -/ +theorem realizes_enqueue_standard : Realizes .standard enqueue (.refl Input) counts + enqueueDomain (fun _ _ => 0) := + realizes_enqueue.mapSimulates Basis.Simulates.exactStandard + +/-- Standard-basis dequeue keeps both counts and the faithful option result. -/ +theorem realizes_dequeue_standard : Realizes .standard dequeue (.refl Input) + (appendWords counts optionWords) transferDomain (fun _ _ => 0) := + realizes_dequeue.mapSimulates Basis.Simulates.exactStandard + +/-- Enqueue issues exactly the physical rear write, retaining the frontier. -/ +theorem runClient_enqueue (w : ℕ) (d : Input) (s : Memory.State) : + (Memory.spec w).runClient s (enqueue d) = + ((d 0, d 1 + 1), ⟨Function.update s.mem (d 2 + d 3 + d 1) (d 4), s.frontier⟩, + [.write (d 2 + d 3 + d 1) (d 4)]) := rfl + +/-- The spare-capacity domain admits the one physical write, including the delivered value. -/ +theorem valid_enqueue {w : ℕ} {d : Input} {s : Memory.State} + (h : enqueueDomain w d s) : + (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s (enqueue d)) := by + change (d 2 + d 3 + d 1 < s.frontier ∧ Fits w (d 4)) ∧ True + exact ⟨⟨by have := h.1; have := h.2.1; omega, h.2.2⟩, trivial⟩ + +/-- Enqueue uses one memory query, independently of the capacity and existing contents. -/ +theorem memoryCost_enqueue (d : Input) (s : Memory.State) : + memoryCost (enqueue d) s = 1 := rfl + +/-- The rear write remains inside the original two-array footprint. -/ +theorem enqueue_modifies {w : ℕ} {d : Input} {s : Memory.State} + (h : enqueueDomain w d s) : + Modifies (Block.mk (d 2) (2 * d 3)).footprint s.mem + ((Memory.spec w).clientState s (enqueue d)).mem := by + exact Modifies.write_of_owns ⟨by omega, by have := h.1; omega⟩ _ + +/-- Pop never changes any memory cell or the frontier, including its empty path. -/ +theorem clientState_popFront (w : ℕ) (d : Input) (s : Memory.State) : + (Memory.spec w).clientState s (popFront d) = s := by + by_cases hz : d 0 = 0 + · rw [popFront, ite_eq_left hz] + rfl + · rw [popFront, ite_eq_right hz] + rfl + +/-- Both returned counts are exact even when the unused physical top cell remains dirty. -/ +theorem clientResult_popFront (w : ℕ) (d : Input) (s : Memory.State) : + (Memory.spec w).clientResult s (popFront d) = + if d 0 = 0 then ((d 0, d 1), none) + else ((d 0 - 1, d 1), some (s.mem (d 2 + (d 0 - 1)))) := by + by_cases hz : d 0 = 0 + · rw [popFront, ite_eq_left hz, ite_eq_left hz] + rfl + · rw [popFront, ite_eq_right hz, ite_eq_right hz] + rfl + +/-- A nonempty front performs one read; an empty front performs no memory operation. -/ +theorem memoryCost_popFront (d : Input) (s : Memory.State) : + memoryCost (popFront d) s = if d 0 = 0 then 0 else 1 := by + by_cases hz : d 0 = 0 + · rw [popFront, ite_eq_left hz, ite_eq_left hz] + rfl + · rw [popFront, ite_eq_right hz, ite_eq_right hz] + rfl + +/-- Every nonempty front read lies in the old physical region. -/ +theorem valid_popFront {w : ℕ} {d : Input} {s : Memory.State} + (h : transferDomain w d s) : + (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s (popFront d)) := by + by_cases hz : d 0 = 0 + · rw [popFront, ite_eq_left hz] + trivial + · rw [popFront, ite_eq_right hz] + change (Memory.spec w).ValidHistory s + ((Memory.spec w).clientHistory s (do + let v ← Memory.read (d 2 + (d 0 - 1)) + pure ((d 0 - 1, d 1), some v))) + change (d 2 + (d 0 - 1) < s.frontier) ∧ True + exact ⟨by have := h.1; have := h.2; omega, trivial⟩ + +/-- Dequeue's source summary retains the actual final counts, valid physical history and +frontier. The fallback performs exactly two queries per transferred element and one final +read when the queue was nonempty. -/ +theorem dequeue_invariant {w : ℕ} {d : Input} {s : Memory.State} + (hw : Memory.WellFormed w s) (h : transferDomain w d s) : + let t := (Memory.spec 0).clientState s (dequeue d) + (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s (dequeue d)) ∧ + Memory.WellFormed w t ∧ + ((Memory.spec 0).clientResult s (dequeue d)).1 = + (if d 0 = 0 then (d 1 - 1, 0) else (d 0 - 1, d 1)) ∧ + t.frontier = s.frontier ∧ + memoryCost (dequeue d) s = + (if d 0 = 0 then 2 * d 1 + (if d 1 = 0 then 0 else 1) else 1) ∧ + Modifies (Block.mk (d 2) (2 * d 3)).footprint s.mem t.mem := by + by_cases hz : d 0 = 0 + · obtain ⟨hv, hwt, hr, hf, hc, hm⟩ := transfer_invariant hw h + have hr' : (Memory.spec 0).clientResult s (transfer d) = (d 1, 0) := by + simpa only [hz, Nat.zero_add] using hr + let t := (Memory.spec 0).clientState s (transfer d) + let e : Input := ![d 1, 0, d 2, d 3, d 4] + have he : transferDomain w e t := by + refine ⟨?_, ?_⟩ + · change d 1 + 0 ≤ d 3 + have := h.1 + omega + · change d 2 + 2 * d 3 ≤ t.frontier + rw [hf] + exact h.2 + have hs : (Memory.spec 0).clientState s (dequeue d) = t := by + rw [dequeue, ite_eq_left hz, StateTransitionSpec.clientState_bind, hr'] + exact clientState_popFront 0 e t + refine ⟨?_, ?_, ?_, ?_, ?_, ?_⟩ + · rw [dequeue, ite_eq_left hz] + change (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s + ((transfer d).bind (fun m => popFront ![m.1, m.2, d 2, d 3, d 4]))) + rw [validHistory_bind] + refine ⟨hv, ?_⟩ + rw [clientState_width w, clientResult_width w, hr'] + exact valid_popFront he + · rw [hs] + exact hwt + · rw [dequeue, ite_eq_left hz, StateTransitionSpec.clientResult_bind, hr', + clientResult_popFront, ite_eq_left hz] + change (if d 1 = 0 then ((d 1, 0), none) + else ((d 1 - 1, 0), some (t.mem (d 2 + (d 1 - 1))))).1 = (d 1 - 1, 0) + split <;> simp_all + · rw [hs] + exact hf + · rw [dequeue, ite_eq_left hz] + change memoryCost ((transfer d).bind (fun m => popFront ![m.1, m.2, d 2, d 3, d 4])) s = _ + rw [memoryCost_bind, hr', hc, memoryCost_popFront, ite_eq_left hz] + rfl + · rw [hs] + exact hm + · have hs : (Memory.spec 0).clientState s (dequeue d) = s := by + rw [dequeue, ite_eq_right hz, clientState_popFront] + refine ⟨?_, ?_, ?_, ?_, ?_, ?_⟩ + · rw [dequeue, ite_eq_right hz] + exact valid_popFront h + · rw [hs] + exact hw + · rw [dequeue, ite_eq_right hz, clientResult_popFront, ite_eq_right hz, ite_eq_right hz] + · rw [hs] + · rw [dequeue, ite_eq_right hz, memoryCost_popFront, ite_eq_right hz, ite_eq_right hz] + · rw [hs] + exact fun _ _ => rfl + +/-- At the source-memory boundary, one enqueue pays for its write and the added rear credit. +Native glue and the final halt are accounted for separately by the realized dispatcher. -/ +theorem enqueue_memory_amortized (d : Input) (s : Memory.State) : + memoryCost (enqueue d) s + 2 * ((Memory.spec 0).clientResult s (enqueue d)).2 = + 3 + 2 * d 1 := by + change 1 + 2 * (d 1 + 1) = _ + omega + +/-- The actual transfer queries are paid by the initial rear credit, including a final +nonempty pop. This is a source-memory bound, not a claim that native glue is free. -/ +theorem dequeue_memory_amortized {w : ℕ} {d : Input} {s : Memory.State} + (hw : Memory.WellFormed w s) (h : transferDomain w d s) : + memoryCost (dequeue d) s + 2 * ((Memory.spec 0).clientResult s (dequeue d)).1.2 ≤ + 3 + 2 * d 1 := by + have hi := dequeue_invariant hw h + rw [hi.2.2.1, hi.2.2.2.2.1] + split <;> (try split) <;> simp_all + +end Cslib.DataStructure.Queue.WordRAM.RegisterMetadata diff --git a/Complexity/DataStructure/Queue/WordRAM/RegisterProgram.lean b/Complexity/DataStructure/Queue/WordRAM/RegisterProgram.lean new file mode 100644 index 0000000000..aa3c7a9f71 --- /dev/null +++ b/Complexity/DataStructure/Queue/WordRAM/RegisterProgram.lean @@ -0,0 +1,152 @@ +/- +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.RegisterCorrectness +public import Complexity.Computability.WordRAM.Realization.Backend + +/-! +# One fixed queue dispatcher over realized physical-array methods + +The finite method family chooses all code and register packing before any runtime count, +base, capacity, width, memory or operation. Only the tag and payload are delivered by the +external dynamic calling convention. The count words, base, capacity and frontier remain +resident. A native branch selects a body, and two actual copies commit its returned counts. + +The initializer is a literal-parameterized code family, as in the original queue: base and +capacity are fixed before the arbitrary dirty entry data. It writes registers only, performs +six instructions, and reserves exactly `2*capacity` physical cells. It is not a memory-clearing +initializer or an uncharged use of `Backend.install`. + +## Main definitions + +* `family`, `program`: the finite body family and its fixed native dispatcher. +* `implementation`: the existing dynamic-program calling convention and typed observation. +* `Rep`, `QueueRep`, `footprint`: the original two physical arrays with resident count words. +* `initializeCode`, `initialized`: six register writes and their exact state. + +## References + +The queue and its algorithmic references are in `Cslib.DataStructure.Queue.Spec`. +-/ + +@[expose] public section + +namespace Cslib.DataStructure.Queue.WordRAM + +open Cslib.WordRAM Cslib.WordRAM.Realization +open RegisterMetadata +open Cslib.DataStructure.Queue + +/-- One fixed family contains both method bodies. Runtime arguments select no new code. -/ +noncomputable abbrev family : Bool → Certificate .standard + | false => ⟨realizes_enqueue_standard⟩ + | true => ⟨realizes_dequeue_standard⟩ + +/-- One finite register file for both bodies and the dispatcher. -/ +noncomputable abbrev registers := Backend.registers family + +/-- The complete physical reservation, including unused array cells. -/ +def footprint (base capacity : ℕ) : Finset ℕ := (Block.mk base (2 * capacity)).footprint + +/-- Persistent count, layout and payload bank. Both tags have the same five-word input. -/ +noncomputable def src (i : Fin 5) : Fin registers := Backend.src family false i + +/-- The two counts occupy the first two words of this common result prefix. -/ +noncomputable def result (i : Fin 4) : Fin registers := + Backend.tmp family true (Fin.castAdd ((family true).realizes.scratchSize + 1) i) + +/-- Tag delivery reuses scratch word zero, which the selected body may overwrite. -/ +noncomputable abbrev tag : Fin registers := result 0 + +/-- A paid zero constant for the native tag test, also in the shared scratch bank. -/ +noncomputable abbrev zero : Fin registers := result 1 + +/-- Two paid copies update the resident count bank. -/ +noncomputable def commit : Code .standard registers := + .seq (.op (.copy (src 0) (result 0))) (.op (.copy (src 1) (result 1))) + +/-- The same native program executes every operation. No host-selected body is substituted. -/ +noncomputable def program : Code .standard registers := + .seq (.op (.const zero 0)) + (.seq (.branch (.eq tag zero) (Backend.code family false) (Backend.code family true)) commit) + +/-- Only the tag and payload are external inputs. The result decoder observes Unit for +enqueue and the faithful option tag/value for dequeue; enqueue need not echo its argument. -/ +noncomputable def implementation (w capacity : ℕ) : + DynamicProgram (spec w capacity) registers where + code := program + output := result 3 + input i := i == tag || i == src 4 + encode o i := if i = tag then + match o with | .enqueue _ => 0 | .dequeue => 1 + else match o with | .enqueue v => v | .dequeue => 0 + decode o regs := match o with + | .enqueue _ => some () + | .dequeue => some (if regs (result 2) = 0 then none else some (regs (result 3))) + +/-- The existing two-array representation, with counts and layout in the shared input bank. +Unused cells and the delivered tag/payload impose no extra memory or register premise. -/ +structure Rep (w base capacity : ℕ) (front back : List ℕ) (x : Data registers) : Prop where + bounded : x.Bounded w + base_eq : x.regs (src 2) = base + capacity_eq : x.regs (src 3) = capacity + frontier_eq : x.regs (Backend.frontier family) = base + 2 * capacity + frontLen_eq : x.regs (src 0) = front.length + backLen_eq : x.regs (src 1) = back.length + size_le : front.length + back.length ≤ capacity + front_contents : Block.HoldsList base front.reverse x.mem + back_contents : Block.HoldsList (base + capacity) back.reverse x.mem + +/-- The same logical FIFO projection as the original queue. -/ +def QueueRep (w base capacity : ℕ) (q : List ℕ) (x : Data registers) : Prop := + ∃ front back, q = contents (front, back) ∧ Rep w base capacity front back x + +/-- The actual resident input words, without installing the maintained metadata again. -/ +theorem Rep.input_eq {w base capacity : ℕ} {front back : List ℕ} {x : Data registers} + (hr : Rep w base capacity front back x) : + x.regs ∘ src = ![front.length, back.length, base, capacity, x.regs (src 4)] := by + funext i + fin_cases i + · exact hr.frontLen_eq + · exact hr.backLen_eq + · exact hr.base_eq + · exact hr.capacity_eq + · rfl + +/-- Rear credit is scaled by the single native exchange factor. This definition does not +assert the still separate dispatcher clock or the final per-call amortized inequality. -/ +noncomputable def potential (x : Data registers) : ℕ := + 2 * Backend.factor family * x.regs (src 1) + +/-- Exact initialized data: only the frontier, counts, layout and zero scratch word change. -/ +noncomputable def initialized (base capacity : ℕ) (x : Data registers) : Data registers := + (((((x.set (Backend.frontier family) (base + 2 * capacity)).set (src 0) 0).set + (src 1) 0).set (src 2) base).set (src 3) capacity).set zero 0 + +/-- A literal base/capacity initializer with the original uniform six-instruction cost. -/ +noncomputable def initializeCode (base capacity : ℕ) : Code .standard registers := + .seq (.op (.const (Backend.frontier family) (base + 2 * capacity))) + (.seq (.op (.const (src 0) 0)) (.seq (.op (.const (src 1) 0)) + (.seq (.op (.const (src 2) base)) + (.seq (.op (.const (src 3) capacity)) (.op (.const zero 0)))))) + +/-- Initialization starts from arbitrary bounded dirty data and keeps every memory cell. +The original `Fits w 1` premise is needed by operation tag delivery, not these zero writes. -/ +theorem initialize_exec {w base capacity : ℕ} {x : Data registers} (hx : x.Bounded w) + (hc : Fits w (base + 2 * capacity)) : + Exec w (initializeCode base capacity) x (initialized base capacity x) 6 ∧ + (initialized base capacity x).Bounded w ∧ (initialized base capacity x).mem = x.mem := by + have hb : Fits w base := hc.of_le (by omega) + have hcap : Fits w capacity := hc.of_le (by omega) + have he : Exec w (initializeCode base capacity) x (initialized base capacity x) 6 := + (Exec.const hc).seq ((Exec.const (fits_zero w)).seq ((Exec.const (fits_zero w)).seq + ((Exec.const hb).seq ((Exec.const hcap).seq (Exec.const (fits_zero w)))))) + exact ⟨he, he.bounded hx, rfl⟩ + +end Cslib.DataStructure.Queue.WordRAM diff --git a/Complexity/DataStructure/Queue/WordRAM/RegisterTransfer.lean b/Complexity/DataStructure/Queue/WordRAM/RegisterTransfer.lean new file mode 100644 index 0000000000..5a8decf4cb --- /dev/null +++ b/Complexity/DataStructure/Queue/WordRAM/RegisterTransfer.lean @@ -0,0 +1,221 @@ +/- +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.RegisterMetadata +public import Complexity.Computability.WordRAM.Realization.WhileAbsorb +public import Complexity.Computability.WordRAM.ListArray +import Complexity.Tactic.Realize + +/-! +# Queue transfer with explicit register metadata + +Both stack counts are explicit input and result words. The source uses the existing memory +programs and bounded while, writes only the original two physical arrays, and neither allocates +count cells nor changes unallocated memory. The remaining rear count is the loop rank. + +## Main definitions + +* `transfer`: the remaining-count loop over the shared physical transfer step. +* `transferDomain`: the old combined-capacity and physical frontier envelope. + +## Main results + +* `realizes_transfer`: one native emitter before every count, layout, width and memory. +* `realizes_transfer_standard`: the same semantics on the original standard word-RAM basis. +* `transfer_invariant`: valid histories, exact two-query-per-item cost, count and memory frame. +* `transfer_contents`: the physical arrays have exactly the original reversal semantics. + +## References + +The queue and its algorithmic references are in `Cslib.DataStructure.Queue.Spec`. +-/ + +@[expose] public section + +namespace Cslib.DataStructure.Queue.WordRAM.RegisterMetadata + +open Cslib.WordRAM Cslib.WordRAM.Realization + +/-- Reuse the shared physical step with the current two count words. -/ +def transferBody (d : Input × (ℕ × ℕ)) : Memory.Prog (ℕ × ℕ) := + move ![d.2.1, d.2.2, d.1 2, d.1 3, d.1 4] + +/-- Move the whole rear, using the explicit rear count as a sufficient ghost cutoff. -/ +def transfer (d : Input) : Memory.Prog (ℕ × ℕ) := + boundedWhile (fun _ m => 0 < m.2) transferBody d (d 1) (d 0, d 1) + +/-- Both arrays use the old `2*C` physical room; the two counts occupy registers. -/ +def transferDomain (_w : ℕ) (d : Input) (s : Memory.State) : Prop := + d 0 + d 1 ≤ d 3 ∧ d 2 + 2 * d 3 ≤ s.frontier + +/-- The canonical tactic certifies the actual memory-query loop. Every guard and count-copy +instruction is paid through the existing realization factor. -/ +theorem realizes_transfer : Realizes .exact transfer (.refl Input) counts + transferDomain (fun _ _ => 0) := by + change Realizes .exact transfer (.refl (Fin 5 → ℕ)) counts _ _ + realize + intro hv + refine whileSafe_of_measure _ _ _ w d + (fun m t => Memory.WellFormed w t ∧ m.1 + m.2 ≤ d 3 ∧ d 2 + 2 * d 3 ≤ t.frontier) + (fun m _ => m.2) (fun m _ _ hz hg => by omega) ?_ ⟨hmem, h.1, h.2⟩ le_rfl hv + rintro m t ⟨hwt, hcap, hfrontier⟩ hg hb + refine ⟨?_, ?_, ?_⟩ + · change (1 ≤ m.2 ∧ Fits w (d 2 + d 3 + (m.2 - 1))) ∧ + Fits w (d 2 + m.1) ∧ 1 ≤ m.2 ∧ Fits w (m.1 + 1) + exact ⟨⟨by omega, hwt.fits_of_lt_frontier (by omega)⟩, + hwt.fits_of_lt_frontier (by omega), by omega, + (show Fits w (d 3) from hwords 3).of_le (by omega)⟩ + · have hw' : Memory.WellFormed w + ((Memory.spec 0).clientState t (move ![m.1, m.2, d 2, d 3, d 4])) := by + rw [← clientState_width w, ← (Memory.spec w).run_runClient] + exact hwt.run hb + refine ⟨hw', ?_, ?_⟩ + · change (m.1 + 1) + (m.2 - 1) ≤ d 3 + omega + · exact hfrontier + · change m.2 - 1 < m.2 + omega + +/-- The existing defined-evaluation simulation gives the same literal memory semantics on the +standard word-RAM basis used by the original queue. No overflow/underflow behavior is weakened. -/ +theorem realizes_transfer_standard : Realizes .standard transfer (.refl Input) counts + transferDomain (fun _ _ => 0) := + realizes_transfer.mapSimulates Basis.Simulates.exactStandard + +private theorem transfer_summary (d : Input) (F n : ℕ) {w : ℕ} {s : Memory.State} + (hw : Memory.WellFormed w s) (hc : F + n ≤ d 3) + (hf : d 2 + 2 * d 3 ≤ s.frontier) : + let p := boundedWhile (fun _ m => 0 < m.2) transferBody d n (F, n) + let t := (Memory.spec 0).clientState s p + (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s p) ∧ + Memory.WellFormed w t ∧ (Memory.spec 0).clientResult s p = (F + n, 0) ∧ + t.frontier = s.frontier ∧ memoryCost p s = 2 * n ∧ + Modifies (Block.mk (d 2) (2 * d 3)).footprint s.mem t.mem := by + induction n generalizing F s with + | zero => exact ⟨trivial, hw, rfl, rfl, rfl, fun _ _ => rfl⟩ + | succ n ih => + let b := transferBody (d, (F, n + 1)) + let t₁ := (Memory.spec 0).clientState s b + have hdom : domain w ![F, n + 1, d 2, d 3, d 4] s := + ⟨Nat.zero_lt_succ n, hc, hf⟩ + have hvb : (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s b) := + valid_move hw hdom + have hw₁ : Memory.WellFormed w t₁ := by + change Memory.WellFormed w ((Memory.spec 0).clientState s b) + rw [← clientState_width w, ← (Memory.spec w).run_runClient] + exact hw.run hvb + have hresult : (Memory.spec 0).clientResult s b = (F + 1, n) := rfl + have hfrontier : t₁.frontier = s.frontier := rfl + obtain ⟨hvt, hwt, hrt, hft, hct, hmt⟩ := ih (F + 1) hw₁ (by omega) (by + rw [hfrontier] + exact hf) + let rest := boundedWhile (fun _ m => 0 < m.2) transferBody d n + have hstate : (Memory.spec 0).clientState s + (boundedWhile (fun _ m => 0 < m.2) transferBody d (n + 1) (F, n + 1)) = + (Memory.spec 0).clientState t₁ (rest (F + 1, n)) := by + change (Memory.spec 0).clientState s (b.bind rest) = _ + simp only [StateTransitionSpec.clientState, StateTransitionSpec.runClient_bind] + change (Memory.spec 0).clientState t₁ (rest ((Memory.spec 0).clientResult s b)) = _ + rw [hresult] + refine ⟨?_, hstate ▸ hwt, ?_, hstate ▸ hft.trans hfrontier, ?_, ?_⟩ + · change (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s (b.bind rest)) + apply (validHistory_bind w b rest s).mpr + refine ⟨hvb, ?_⟩ + rw [clientState_width w, clientResult_width w, hresult] + exact hvt + · change (Memory.spec 0).clientResult s (b.bind rest) = _ + simp only [StateTransitionSpec.clientResult, StateTransitionSpec.runClient_bind] + change (Memory.spec 0).clientResult t₁ (rest ((Memory.spec 0).clientResult s b)) = _ + rw [hresult, hrt] + congr 1 + omega + · change memoryCost (b.bind rest) s = _ + rw [memoryCost_bind, hresult, hct] + have hb : memoryCost b s = 2 := rfl + omega + · intro j hj + rw [hstate] + exact (hmt j hj).trans (move_modifies (w := 0) hdom j hj) + +/-- Transfer is valid on arbitrary well-formed physical memory under the original room +envelope. It performs exactly two memory queries per moved element and preserves the entire +memory outside the old `2*C` block. The two returned counts are the actual final counts. -/ +theorem transfer_invariant {w : ℕ} {d : Input} {s : Memory.State} + (hw : Memory.WellFormed w s) (h : transferDomain w d s) : + let t := (Memory.spec 0).clientState s (transfer d) + (Memory.spec w).ValidHistory s ((Memory.spec w).clientHistory s (transfer d)) ∧ + Memory.WellFormed w t ∧ (Memory.spec 0).clientResult s (transfer d) = (d 0 + d 1, 0) ∧ + t.frontier = s.frontier ∧ memoryCost (transfer d) s = 2 * d 1 ∧ + Modifies (Block.mk (d 2) (2 * d 3)).footprint s.mem t.mem := + transfer_summary d (d 0) (d 1) hw h.1 h.2 + +private theorem transfer_contents_aux (d : Input) (back front : List ℕ) (s : Memory.State) + (hc : front.length + back.length ≤ d 3) + (hfront : Block.HoldsList (d 2) front.reverse s.mem) + (hback : Block.HoldsList (d 2 + d 3) back.reverse s.mem) : + Block.HoldsList (d 2) (back.reverse ++ front).reverse + ((Memory.spec 0).clientState s + (boundedWhile (fun _ m => 0 < m.2) transferBody d back.length + (front.length, back.length))).mem := by + induction back generalizing front s with + | nil => + simpa only [List.length_nil, boundedWhile, StateTransitionSpec.clientState, + StateTransitionSpec.runClient_pure', List.reverse_nil, List.nil_append] using hfront + | cons v back ih => + have hread : s.mem (d 2 + d 3 + back.length) = v := by + simpa [List.reverse_cons] using hback.get back.length (by simp) + let t : Memory.State := ⟨Function.update s.mem (d 2 + front.length) v, s.frontier⟩ + have hfront' : Block.HoldsList (d 2) (v :: front).reverse t.mem := by + simpa only [t, List.reverse_cons, List.length_reverse] using hfront.append_one v + have hback' : Block.HoldsList (d 2 + d 3) back.reverse t.mem := by + have hprefix : Block.HoldsList (d 2 + d 3) back.reverse s.mem := by + simpa [List.reverse_cons] using hback.take back.length + apply hprefix.frame + intro a ha _ + exact Function.update_of_ne (by have := hc; simp only [List.length_cons] at this; omega) _ _ + have hc' : (v :: front).length + back.length ≤ d 3 := by + simp only [List.length_cons] at hc ⊢ + omega + have hi := ih (v :: front) t hc' hfront' hback' + let b := transferBody (d, (front.length, (v :: back).length)) + have hrun : (Memory.spec 0).runClient s b = + ((front.length + 1, back.length), t, + [Memory.Op.read (d 2 + d 3 + back.length), Memory.Op.write (d 2 + front.length) v]) := by + change ((front.length + 1, back.length), + (⟨Function.update s.mem (d 2 + front.length) + (s.mem (d 2 + d 3 + back.length)), s.frontier⟩ : Memory.State), + [Memory.Op.read (d 2 + d 3 + back.length), Memory.Op.write (d 2 + front.length) + (s.mem (d 2 + d 3 + back.length))]) = _ + rw [hread] + have hstate : (Memory.spec 0).clientState s + (boundedWhile (fun _ m => 0 < m.2) transferBody d (v :: back).length + (front.length, (v :: back).length)) = + (Memory.spec 0).clientState t + (boundedWhile (fun _ m => 0 < m.2) transferBody d back.length + ((v :: front).length, back.length)) := by + change (Memory.spec 0).clientState s + (b.bind (boundedWhile (fun _ m => 0 < m.2) transferBody d back.length)) = _ + simp only [StateTransitionSpec.clientState, StateTransitionSpec.runClient_bind, hrun, + List.length_cons] + rw [hstate] + simpa only [List.reverse_cons, List.append_assoc, List.singleton_append] using hi + +/-- The physical arrays have exactly the original transfer semantics: the reversed rear is +placed on top of the front. This proof reuses the generic list-array append, prefix and frame +lemmas; no machine step table or count-cell representation is introduced. -/ +theorem transfer_contents {d : Input} (back front : List ℕ) (s : Memory.State) + (hF : d 0 = front.length) (hB : d 1 = back.length) + (hc : front.length + back.length ≤ d 3) + (hfront : Block.HoldsList (d 2) front.reverse s.mem) + (hback : Block.HoldsList (d 2 + d 3) back.reverse s.mem) : + Block.HoldsList (d 2) (back.reverse ++ front).reverse + ((Memory.spec 0).clientState s (transfer d)).mem := by + simpa only [transfer, hF, hB] using transfer_contents_aux d back front s hc hfront hback + +end Cslib.DataStructure.Queue.WordRAM.RegisterMetadata diff --git a/ComplexityTests/QueueMemoryTransfer.lean b/ComplexityTests/QueueMemoryTransfer.lean new file mode 100644 index 0000000000..95c2c7f955 --- /dev/null +++ b/ComplexityTests/QueueMemoryTransfer.lean @@ -0,0 +1,69 @@ +/- +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 the memory queue migration) + +public import Complexity.DataStructure.Queue.Memory.Transfer + +@[expose] public section + +namespace ComplexityTests.QueueMemoryTransfer + +open Cslib Cslib.DataStructure Cslib.WordRAM Cslib.WordRAM.Realization + +def front : Stack.Memory.Layout := ⟨⟨0, 1⟩, ⟨1, 2⟩⟩ +def rear : Stack.Memory.Layout := ⟨⟨3, 1⟩, ⟨4, 2⟩⟩ + +def memory : Memory.State := + ⟨fun a => if a = 3 then 2 else if a = 4 then 3 else if a = 7 then 5 else 0, 6⟩ + +def source : Memory.Prog ℕ := + boundedWhile (fun _ r => 0 < r) Queue.Memory.transferBody (front, rear) 2 2 + +example : (Memory.spec 3).clientResult memory source = 0 := rfl +example : ((Memory.spec 3).clientState memory source).mem 0 = 2 := rfl +example : ((Memory.spec 3).clientState memory source).mem 3 = 0 := rfl +example : ((Memory.spec 3).clientState memory source).mem 7 = 5 := rfl +example : Stack.Memory.abs front ((Memory.spec 3).clientState memory source) = [3, 0] := rfl + +-- The ghost cutoff may exceed the count without changing the linked source or its history. +example : + link (Methods.prod (Stack.Memory.methods front 3 2) (Stack.Memory.methods rear 3 2)) + (Queue.transfer 2) = + (boundedWhile (fun _ r => 0 < r) Queue.Memory.transferBody (front, rear) 9 2).bind + (fun _ => pure ()) := + Queue.Memory.transfer_eq_boundedWhile front rear 3 2 9 2 (by omega) + +-- A positive remaining count on an empty rear performs just the terminating count read. +example : + (Memory.spec 3).clientHistory (⟨fun _ => 0, 6⟩ : Memory.State) + (boundedWhile (fun _ r => 0 < r) Queue.Memory.transferBody (front, rear) 4 4) = + [.read 3] := rfl + +-- The existing invariants are satisfiable at the concrete nonempty transfer above. +example : + let p := link (Methods.prod (Stack.Memory.methods front 3 2) + (Stack.Memory.methods rear 3 2)) (Queue.transfer 2) + let s' := (Memory.spec 3).clientState memory p + (Memory.spec 3).ValidHistory memory ((Memory.spec 3).clientHistory memory p) ∧ + Memory.WellFormed 3 s' ∧ Stack.Memory.Inv front s' ∧ Stack.Memory.Inv rear s' ∧ + s'.mem rear.count.base = 0 ∧ s'.mem front.count.base = memory.mem front.count.base + 2 ∧ + Stack.Memory.abs front s' = (Stack.Memory.abs rear memory).reverse ++ + Stack.Memory.abs front memory := by + apply Queue.Memory.transfer_invariant (w := 3) (C := 2) + · exact ⟨rfl, by change ¬(1 ≤ 0 ∧ 0 < 1 + 2); decide, rfl, by decide⟩ + · exact ⟨rfl, by change ¬(4 ≤ 3 ∧ 3 < 4 + 2); decide, rfl, by decide⟩ + · decide + · refine ⟨by decide, fun a => ?_⟩ + dsimp only [memory] + split <;> (try split) <;> (try split) <;> decide + · exact ⟨by change 0 + 1 ≤ 6; decide, by change 1 + 2 ≤ 6; decide, by decide⟩ + · exact ⟨by change 3 + 1 ≤ 6; decide, by change 4 + 2 ≤ 6; decide, by decide⟩ + · rfl + · decide + +end ComplexityTests.QueueMemoryTransfer diff --git a/ComplexityTests/QueueRegisterCorrectness.lean b/ComplexityTests/QueueRegisterCorrectness.lean new file mode 100644 index 0000000000..cf990d0827 --- /dev/null +++ b/ComplexityTests/QueueRegisterCorrectness.lean @@ -0,0 +1,72 @@ +/- +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.WordRAM.RegisterCorrectness + +/-! Logical clients of the physical-array source proofs. Native prepared-body regressions +are in `QueueRegisterMethods`; raw initialization and persistent dispatch are separate. -/ + +@[expose] public section + +namespace ComplexityTests.QueueRegisterCorrectness + +open Cslib Cslib.WordRAM Cslib.WordRAM.Realization Cslib.DataStructure +open Cslib.DataStructure.Queue.WordRAM.RegisterMetadata + +/-- Two rear entries are stored in physical stack order in otherwise dirty memory. -/ +def rear : Memory.State := ⟨fun a => if a = 3 then 0 else if a = 4 then 7 else 5, 5⟩ + +theorem rear_bounded : Memory.WellFormed 3 rear := by + refine ⟨by decide, fun a => ?_⟩ + dsimp only [rear] + split <;> (try split) <;> decide + +-- Existing pairStep, not a separate FIFO specification, supplies the actual dequeue result. +example : + (Memory.spec 0).clientResult rear (dequeue ![0, 2, 1, 2, 0]) = ((1, 0), some 0) ∧ + Block.HoldsList 1 [7] ((Memory.spec 0).clientState rear + (dequeue ![0, 2, 1, 2, 0])).mem := by + have h := dequeue_contents (d := ![0, 2, 1, 2, 0]) [] [7, 0] rear + rear_bounded rfl rfl (by decide) (by decide) (Block.holdsList_nil _ _) + (by intro i; fin_cases i <;> rfl) + exact ⟨h.1, h.2.1⟩ + +-- A nonempty front and a live rear are both retained correctly after the front pop. +example : + (Memory.spec 0).clientResult rear (dequeue ![1, 1, 1, 2, 0]) = ((0, 1), some 5) ∧ + Block.HoldsList 3 [0] ((Memory.spec 0).clientState rear + (dequeue ![1, 1, 1, 2, 0])).mem := by + have h := dequeue_contents (d := ![1, 1, 1, 2, 0]) [5] [0] rear + rear_bounded rfl rfl (by decide) (by decide) + (by intro i; fin_cases i; rfl) (by intro i; fin_cases i; rfl) + exact ⟨h.1, h.2.2⟩ + +-- Enqueue preserves the existing front and extends the rear in physical stack order. +example : Block.HoldsList 3 [0, 7] + ((Memory.spec 0).clientState ⟨fun _ => 0, 5⟩ (enqueue ![0, 1, 1, 2, 7])).mem := by + have h := enqueue_contents (d := ![0, 1, 1, 2, 7]) [] [0] ⟨fun _ => 0, 5⟩ + rfl rfl (by decide) (Block.holdsList_nil _ _) (by intro i; fin_cases i; rfl) + exact h.2.2 + +/-- A source client threads the returned count values, with no hidden memory count cells. -/ +def cycle (v : ℕ) : Memory.Prog (Option ℕ) := do + let m ← enqueue ![0, 0, 1, 1, v] + let r ← dequeue ![m.1, m.2, 1, 1, v] + pure r.2 + +example : (Memory.spec 2).clientResult ⟨fun _ => 1, 3⟩ (cycle 3) = some 3 := rfl +example : memoryCost (cycle 3) ⟨fun _ => 1, 3⟩ = 4 := rfl +example : ((Memory.spec 2).clientState ⟨fun _ => 1, 3⟩ (cycle 3)).mem 4 = 1 := rfl + +-- Full and overflowing enqueues remain rejected; zero-capacity dequeue remains admitted. +example : ¬ enqueueDomain 2 ![0, 1, 1, 1, 3] ⟨fun _ => 3, 3⟩ := by unfold enqueueDomain; decide +example : ¬ enqueueDomain 2 ![0, 0, 1, 1, 4] ⟨fun _ => 3, 3⟩ := by unfold enqueueDomain; decide +example : transferDomain 1 ![0, 0, 0, 0, 1] ⟨fun _ => 1, 0⟩ := by unfold transferDomain; decide + +end ComplexityTests.QueueRegisterCorrectness diff --git a/ComplexityTests/QueueRegisterDynamic.lean b/ComplexityTests/QueueRegisterDynamic.lean new file mode 100644 index 0000000000..8896b2b3dd --- /dev/null +++ b/ComplexityTests/QueueRegisterDynamic.lean @@ -0,0 +1,83 @@ +/- +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.WordRAM.RegisterDynamic + +/-! +The original adaptive FIFO client is consumed by the existing dynamic execution/resource +interfaces. Raw dirty initialization, its six actual instructions, final potential and the +free external-controller boundary are preserved. The native exchange factor is uniform; +the old hand-written program's literal14/28 instruction claims are not reused. +-/ + +@[expose] public section + +namespace ComplexityTests.QueueRegisterDynamic + +open Cslib Cslib.WordRAM Cslib.WordRAM.Realization Cslib.DataStructure +open Cslib.DataStructure.Queue.WordRAM + +/-- Enqueue two values, dequeue the first and reinsert that observed value before the final +dequeue. The client never inspects either physical stack or its count words. -/ +def cycle {w capacity : ℕ} (a b : ℕ) : + (spec w capacity).Client (Option ℕ × Option ℕ) := do + let _ ← (spec w capacity).call (.enqueue a) + let _ ← (spec w capacity).call (.enqueue b) + let first ← (spec w capacity).call .dequeue + let _ ← (spec w capacity).call (.enqueue (first.getD 0)) + let second ← (spec w capacity).call .dequeue + pure (first, second) + +theorem cycle_valid {w capacity a b : ℕ} (hc : 2 ≤ capacity) + (ha : Fits w a) (hb : Fits w b) : + (spec w capacity).ValidHistory [] ((spec w capacity).runClient [] (cycle a b)).2.2 := by + exact ⟨⟨by simpa using (show 0 < capacity by omega), ha⟩, + ⟨⟨by simpa using (show 1 < capacity by omega), hb⟩, + trivial, ⟨by simpa using (show 1 < capacity by omega), ha⟩, trivial, trivial⟩⟩ + +example {w capacity a b : ℕ} : (spec w capacity).runClient [] (cycle a b) = + ((some a, some b), [a], [.enqueue a, .enqueue b, .dequeue, .enqueue a, .dequeue]) := rfl + +/-- All registers and all physical cells initially contain dirty maximum words. -/ +def stale : Data registers := ⟨fun _ => 15, fun _ => 15⟩ + +theorem stale_bounded : stale.Bounded 4 := + ⟨fun _ => (by decide : Fits 4 15), fun _ => (by decide : Fits 4 15)⟩ + +/-- The five adaptive native calls retain the final queue and terminal rear credit. -/ +theorem adaptive_from_dirty : ∃ ready y traces, + Exec 4 (initializeCode 3 2) stale ready 6 ∧ + DataStructure.Executes ((implementation 4 2).ExecOp 4) + (cycle 0 15) ready (some 0, some 15) y traces ∧ + QueueRep 4 3 2 [0] y ∧ + 6 + DataStructure.total DynamicProgram.OperationTrace.instructions traces + potential y ≤ + 6 + 5 * (4 * Backend.factor family + 6) := by + exact run_initialized (w := 4) (base := 3) (capacity := 2) + (by decide) (by decide) (cycle 0 15) stale_bounded + (cycle_valid (by decide) (by decide) (by decide)) + +-- No unused physical cell is cleared by the raw initializer. +example : (initialized 3 2 stale).mem = stale.mem := rfl + +-- The original zero-capacity domain and dirty memory need no extra metadata cells. +example {x : Data registers} (hx : x.Bounded 1) : ∃ ready y traces, + Exec 1 (initializeCode 0 0) x ready 6 ∧ + DataStructure.Executes ((implementation 1 0).ExecOp 1) + ((spec 1 0).call .dequeue) ready none y traces ∧ + QueueRep 1 0 0 [] y ∧ + 6 + DataStructure.total DynamicProgram.OperationTrace.instructions traces + potential y ≤ + 6 + (4 * Backend.factor family + 6) := by + have hrun : (spec 1 0).runClient [] ((spec 1 0).call .dequeue) = + (none, [], [Queue.QueueOp.dequeue]) := rfl + have h := run_initialized (w := 1) (base := 0) (capacity := 0) + (by decide) (by decide) ((spec 1 0).call .dequeue) hx ⟨trivial, trivial⟩ + rw [hrun] at h + simpa only [List.length_singleton, Nat.one_mul] using h + +end ComplexityTests.QueueRegisterDynamic diff --git a/ComplexityTests/QueueRegisterExecution.lean b/ComplexityTests/QueueRegisterExecution.lean new file mode 100644 index 0000000000..88b7527614 --- /dev/null +++ b/ComplexityTests/QueueRegisterExecution.lean @@ -0,0 +1,106 @@ +/- +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.WordRAM.RegisterExecution + +/-! +Actual fixed-dispatcher calls carry their count registers from one call to the next. External +prepare delivers only tag/payload; no Backend.install or replacement metadata is used. The +six-instruction initializer starts from dirty registers and preserves every memory cell. +-/ + +@[expose] public section + +namespace ComplexityTests.QueueRegisterExecution + +open Cslib.WordRAM Cslib.WordRAM.Realization +open Cslib.DataStructure.Queue.WordRAM + +/-- Dirty entry registers, zero-valued physical and inaccessible memory. -/ +def rawTwo : Data registers := ⟨fun _ => 3, fun _ => 0⟩ + +theorem rawTwo_bounded : rawTwo.Bounded 2 := + ⟨fun _ => (by decide : Fits 2 3), fun _ => fits_zero 2⟩ + +/-- At the old small-width boundary, one fixed program enqueues and dequeues every fitting +value, with paid initialization, carried metadata, typed observation and an intact sentinel. -/ +theorem roundTrip_native (v : ℕ) (hv : Fits 2 v) : + ∃ ready y z n m, Exec 2 (initializeCode 1 1) rawTwo ready 6 ∧ + Exec 2 program ((implementation 2 1).prepare (.enqueue v) ready) y n ∧ + Exec 2 program ((implementation 2 1).prepare .dequeue y) z m ∧ + (implementation 2 1).decode .dequeue z.regs = some (some v) ∧ + QueueRep 2 1 1 [] z ∧ 6 + (n + 1) + (m + 1) ≤ 6 + (6 * Backend.factor family + 11) ∧ + z.mem 4 = 0 := by + let ready := initialized 1 1 rawTwo + have hr : Rep 2 1 1 [] [] ready := initialized_rep rawTwo_bounded (by decide) + obtain ⟨y, n, he, hn, hry, hmy⟩ := execute_enqueue (by decide) hr (by decide) hv + obtain ⟨z, m, hd, hm, hrz, ha, hmz⟩ := execute_dequeue (by decide) hry + have hm' : m + 1 ≤ Backend.factor family * 4 + 6 := hm + have houtside : 4 ∉ (Block.mk 1 (2 * 1)).footprint := by decide + have hsentinel : z.mem 4 = 0 := (hmz 4 houtside).trans ((hmy 4 houtside).trans rfl) + refine ⟨ready, y, z, n, m, (initialize_exec rawTwo_bounded (by decide)).1, + he, hd, ha, ⟨[], [], rfl, hrz⟩, ?_, hsentinel⟩ + omega + +-- Some-zero and the maximum word use the same program and persistent register file. +example := roundTrip_native 0 (fits_zero 2) +example := roundTrip_native 3 (by decide) + +/-- A larger old two-array region, with arbitrary dirty unused cells. -/ +def rawThree : Data registers := ⟨fun _ => 7, fun _ => 5⟩ + +theorem rawThree_bounded : rawThree.Bounded 3 := + ⟨fun _ => (by decide : Fits 3 7), fun _ => (by decide : Fits 3 5)⟩ + +-- Five actual calls carry metadata through a two-iteration reversal, an immediate pop and +-- the empty case. The values zero and maximum7 remain distinct from the empty observation. +example : ∃ ready a b c d e n0 n1 n2 n3 n4, + Exec 3 (initializeCode 1 2) rawThree ready 6 ∧ + Exec 3 program ((implementation 3 2).prepare (.enqueue 0) ready) a n0 ∧ + Exec 3 program ((implementation 3 2).prepare (.enqueue 7) a) b n1 ∧ + Exec 3 program ((implementation 3 2).prepare .dequeue b) c n2 ∧ + Exec 3 program ((implementation 3 2).prepare .dequeue c) d n3 ∧ + Exec 3 program ((implementation 3 2).prepare .dequeue d) e n4 ∧ + (implementation 3 2).decode .dequeue c.regs = some (some 0) ∧ + (implementation 3 2).decode .dequeue d.regs = some (some 7) ∧ + (implementation 3 2).decode .dequeue e.regs = some none ∧ QueueRep 3 1 2 [] e := by + let ready := initialized 1 2 rawThree + have hr : Rep 3 1 2 [] [] ready := initialized_rep rawThree_bounded (by decide) + obtain ⟨a, n0, h0, _, hra, _⟩ := execute_enqueue (v := 0) (by decide) hr (by decide) (by decide) + obtain ⟨b, n1, h1, _, hrb, _⟩ := execute_enqueue (v := 7) (by decide) hra (by decide) (by decide) + obtain ⟨c, n2, h2, _, hrc, hc, _⟩ := execute_dequeue (by decide) hrb + obtain ⟨d, n3, h3, _, hrd, hd, _⟩ := execute_dequeue (by decide) hrc + obtain ⟨e, n4, h4, _, hre, he, _⟩ := execute_dequeue (by decide) hrd + exact ⟨ready, a, b, c, d, e, n0, n1, n2, n3, n4, + (initialize_exec rawThree_bounded (by decide)).1, + h0, h1, h2, h3, h4, hc, hd, he, ⟨[], [], rfl, hre⟩⟩ + +/-- Capacity-zero entry is dirty in every register and every memory cell. -/ +def rawOne : Data registers := ⟨fun _ => 1, fun _ => 1⟩ + +theorem rawOne_bounded : rawOne.Bounded 1 := + ⟨fun _ => (by decide : Fits 1 1), fun _ => (by decide : Fits 1 1)⟩ + +example : ∃ ready y n, Exec 1 (initializeCode 0 0) rawOne ready 6 ∧ + Exec 1 program ((implementation 1 0).prepare .dequeue ready) y n ∧ + n + 1 ≤ Backend.factor family + 6 ∧ + (implementation 1 0).decode .dequeue y.regs = some none ∧ + QueueRep 1 0 0 [] y ∧ y.mem = rawOne.mem := by + let ready := initialized 0 0 rawOne + have hr : Rep 1 0 0 [] [] ready := initialized_rep rawOne_bounded (by decide) + obtain ⟨y, n, he, hn, hry, ha, hm⟩ := execute_dequeue (by decide) hr + have hn' : n + 1 ≤ Backend.factor family + 6 := by + change n + 1 ≤ Backend.factor family * 1 + 6 at hn + simpa only [Nat.mul_one] using hn + refine ⟨ready, y, n, (initialize_exec rawOne_bounded (by decide)).1, + he, hn', ha, ⟨[], [], rfl, hry⟩, ?_⟩ + funext a + exact hm a (by simp [footprint, Block.footprint]) + +end ComplexityTests.QueueRegisterExecution diff --git a/ComplexityTests/QueueRegisterMethods.lean b/ComplexityTests/QueueRegisterMethods.lean new file mode 100644 index 0000000000..6101f17b43 --- /dev/null +++ b/ComplexityTests/QueueRegisterMethods.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: local-only (test driver) + +public import Complexity.DataStructure.Queue.WordRAM.RegisterProgram + +/-! +The production finite family is fixed before every runtime count, base, capacity, width and memory. +These are prepared-body executions. A later fixed native dispatcher must pay for tag selection, +metadata commit and the final halt; `Backend.install` is explicit here and is not a raw loader. +-/ + +@[expose] public section + +namespace ComplexityTests.QueueRegisterMethods + +open Cslib Cslib.WordRAM Cslib.WordRAM.Realization Cslib.DataStructure +open Cslib.DataStructure.Queue.WordRAM.RegisterMetadata +open Cslib.DataStructure.Queue.WordRAM + +noncomputable def result (b : Bool) (i : Fin (family b).l) : + Fin (Backend.registers family) := + Backend.tmp family b (Fin.castAdd ((family b).realizes.scratchSize + 1) i) + +/-- Enqueue's whole source memory and its two count words are realized by the fixed body. -/ +theorem native_enqueue {w : ℕ} {d : Input} {s : Memory.State} + (hw : Memory.WellFormed w s) (hwords : ∀ i, Fits w (d i)) + (h : enqueueDomain w d s) : + ∃ y n, Exec w (Backend.code family false) + (Backend.install family false d (Backend.data family s)) y n ∧ + n ≤ Backend.factor family * 2 ∧ + y.mem = Function.update s.mem (d 2 + d 3 + d 1) (d 4) ∧ + ∀ i, y.regs (result false i) = counts (d 0, d 1 + 1) i := by + obtain ⟨y, n, he, hn, hm, hout, _⟩ := Backend.exists_exec family false d + (Backend.bounded_data family hw) hwords + (by simpa only [Backend.memoryState_data] using h) + (by simpa only [Backend.memoryState_data] using valid_enqueue h) + rw [Backend.memoryState_data, memoryCost_enqueue] at hn + have hmem := congrArg Memory.State.mem hm + rw [Backend.memoryState_data, clientState_width w] at hmem + rw [Backend.memoryState_data, clientResult_width w] at hout + exact ⟨y, n, he, hn, hmem, hout⟩ + +/-- The same dequeue body realizes empty, immediate and transferred option answers. -/ +theorem native_dequeue {w : ℕ} {d : Input} {s : Memory.State} + (hw : Memory.WellFormed w s) (hwords : ∀ i, Fits w (d i)) + (h : transferDomain w d s) : + ∃ y n, Exec w (Backend.code family true) + (Backend.install family true d (Backend.data family s)) y n ∧ + n ≤ Backend.factor family * + ((if d 0 = 0 then 2 * d 1 + (if d 1 = 0 then 0 else 1) else 1) + 1) ∧ + y.mem = ((Memory.spec 0).clientState s (dequeue d)).mem ∧ + ∀ i, y.regs (result true i) = + appendWords counts optionWords ((Memory.spec 0).clientResult s (dequeue d)) i := by + have hi := dequeue_invariant hw h + obtain ⟨y, n, he, hn, hm, hout, _⟩ := Backend.exists_exec family true d + (Backend.bounded_data family hw) hwords + (by simpa only [Backend.memoryState_data] using h) + (by simpa only [Backend.memoryState_data] using hi.1) + rw [Backend.memoryState_data, hi.2.2.2.2.1] at hn + have hmem := congrArg Memory.State.mem hm + rw [Backend.memoryState_data, clientState_width w] at hmem + rw [Backend.memoryState_data, clientResult_width w] at hout + exact ⟨y, n, he, hn, hmem, hout⟩ + +-- The old decisive small-width domain admits a maximum-word enqueue with only two cells. +example : ∃ y n, Exec 2 (Backend.code family false) + (Backend.install family false ![0, 0, 1, 1, 3] + (Backend.data family ⟨fun _ => 1, 3⟩)) y n ∧ + n ≤ Backend.factor family * 2 ∧ y.mem 2 = 3 ∧ y.mem 4 = 1 ∧ + y.regs (result false 0) = 0 ∧ y.regs (result false 1) = 1 := by + obtain ⟨y, n, he, hn, hm, ho⟩ := native_enqueue (w := 2) + (d := ![0, 0, 1, 1, 3]) (s := ⟨fun _ => 1, 3⟩) + ⟨by decide, fun _ => (by decide : Fits 2 1)⟩ (fun i => by fin_cases i <;> decide) + (by change 0 + 0 < 1 ∧ 1 + 2 * 1 ≤ 3 ∧ Fits 2 3; decide) + exact ⟨y, n, he, hn, hm ▸ rfl, hm ▸ rfl, ho 0, ho 1⟩ + +/-- A dirty two-element rear contains zero and the maximum three-bit word. -/ +def dirty : Memory.State := ⟨fun a => if a = 3 then 0 else if a = 4 then 7 else 5, 5⟩ + +theorem dirty_bounded : Memory.WellFormed 3 dirty := by + refine ⟨by decide, fun a => ?_⟩ + dsimp only [dirty] + split <;> (try split) <;> decide + +-- The actual fallback returns the oldest value zero, with a separate present tag. +example : ∃ y n, Exec 3 (Backend.code family true) + (Backend.install family true ![0, 2, 1, 2, 7] + (Backend.data family dirty)) y n ∧ + n ≤ Backend.factor family * 6 ∧ y.mem 6 = 5 ∧ + y.regs (result true 0) = 1 ∧ y.regs (result true 1) = 0 ∧ + y.regs (result true 2) = 1 ∧ y.regs (result true 3) = 0 := by + obtain ⟨y, n, he, hn, hm, ho⟩ := native_dequeue (d := ![0, 2, 1, 2, 7]) + dirty_bounded (fun i => by fin_cases i <;> decide) + (by change 0 + 2 ≤ 2 ∧ 1 + 2 * 2 ≤ 5; decide) + exact ⟨y, n, he, hn, hm ▸ rfl, ho 0, ho 1, ho 2, ho 3⟩ + +-- A nonempty front returns a maximum word without touching any memory cell. +example : ∃ y n, Exec 2 (Backend.code family true) + (Backend.install family true ![1, 0, 1, 1, 0] + (Backend.data family ⟨fun _ => 3, 3⟩)) y n ∧ + n ≤ Backend.factor family * 2 ∧ y.mem = (fun _ => 3) ∧ + y.regs (result true 0) = 0 ∧ y.regs (result true 1) = 0 ∧ + y.regs (result true 2) = 1 ∧ y.regs (result true 3) = 3 := by + obtain ⟨y, n, he, hn, hm, ho⟩ := native_dequeue (w := 2) + (d := ![1, 0, 1, 1, 0]) (s := ⟨fun _ => 3, 3⟩) + ⟨by decide, fun _ => (by decide : Fits 2 3)⟩ (fun i => by fin_cases i <;> decide) + (by change 1 + 0 ≤ 1 ∧ 1 + 2 * 1 ≤ 3; decide) + exact ⟨y, n, he, hn, hm, ho 0, ho 1, ho 2, ho 3⟩ + +-- The same dequeue body handles capacity zero on arbitrary dirty bounded memory. +example : ∃ y n, Exec 1 (Backend.code family true) + (Backend.install family true ![0, 0, 0, 0, 1] + (Backend.data family ⟨fun _ => 1, 0⟩)) y n ∧ + n ≤ Backend.factor family ∧ y.mem = (fun _ => 1) ∧ + y.regs (result true 0) = 0 ∧ y.regs (result true 1) = 0 ∧ + y.regs (result true 2) = 0 ∧ y.regs (result true 3) = 0 := by + obtain ⟨y, n, he, hn, hm, ho⟩ := native_dequeue (w := 1) + (d := ![0, 0, 0, 0, 1]) (s := ⟨fun _ => 1, 0⟩) + ⟨by decide, fun _ => (by decide : Fits 1 1)⟩ (fun i => by fin_cases i <;> decide) + (by change 0 + 0 ≤ 0 ∧ 0 + 2 * 0 ≤ 0; decide) + exact ⟨y, n, he, by simpa using hn, hm, ho 0, ho 1, ho 2, ho 3⟩ + +end ComplexityTests.QueueRegisterMethods diff --git a/ComplexityTests/QueueRegisterProgram.lean b/ComplexityTests/QueueRegisterProgram.lean new file mode 100644 index 0000000000..adc30b493c --- /dev/null +++ b/ComplexityTests/QueueRegisterProgram.lean @@ -0,0 +1,44 @@ +/- +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.WordRAM.RegisterProgram + +/-! Paid raw setup in the same globally chosen register file as both method bodies. These +checks establish six actual writes and literal dirty-memory preservation; the full initialized +representation and persistent dispatcher certificate remain separate proof obligations. -/ + +@[expose] public section + +namespace ComplexityTests.QueueRegisterProgram + +open Cslib.WordRAM +open Cslib.DataStructure.Queue.WordRAM + +/-- Every entry register and cell starts dirty, including the frontier and count words. -/ +def rawTwo : Data registers := ⟨fun _ => 3, fun _ => 3⟩ + +theorem rawTwo_bounded : rawTwo.Bounded 2 := + ⟨fun _ => (by decide : Fits 2 3), fun _ => (by decide : Fits 2 3)⟩ + +-- The decisive old width2/base1/C1 domain, with no two extra count cells. +example : ∃ y, Exec 2 (initializeCode 1 1) rawTwo y 6 ∧ y.Bounded 2 ∧ + y.mem = rawTwo.mem := by + exact ⟨initialized 1 1 rawTwo, initialize_exec rawTwo_bounded (by decide)⟩ + +/-- Capacity zero still starts from arbitrary dirty bounded memory. -/ +def rawOne : Data registers := ⟨fun _ => 1, fun _ => 1⟩ + +theorem rawOne_bounded : rawOne.Bounded 1 := + ⟨fun _ => (by decide : Fits 1 1), fun _ => (by decide : Fits 1 1)⟩ + +example : ∃ y, Exec 1 (initializeCode 0 0) rawOne y 6 ∧ y.Bounded 1 ∧ + y.mem = rawOne.mem := by + exact ⟨initialized 0 0 rawOne, initialize_exec rawOne_bounded (by decide)⟩ + +end ComplexityTests.QueueRegisterProgram diff --git a/ComplexityTests/QueueRegisterTransfer.lean b/ComplexityTests/QueueRegisterTransfer.lean new file mode 100644 index 0000000000..cf2a3c86a2 --- /dev/null +++ b/ComplexityTests/QueueRegisterTransfer.lean @@ -0,0 +1,112 @@ +/- +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.WordRAM.RegisterTransfer +public import Complexity.Computability.WordRAM.Realization.Backend + +/-! +One standard-basis body serves all prepared transfer inputs below. Argument installation is +explicit in the entry state; these are body executions, not an uncharged raw initializer or a +stateful `Backend.implementation`. The separate metadata feasibility test pays its initializer. +-/ + +@[expose] public section + +namespace ComplexityTests.QueueRegisterTransfer + +open Cslib Cslib.WordRAM Cslib.WordRAM.Realization Cslib.DataStructure + +open Cslib.DataStructure.Queue.WordRAM.RegisterMetadata + +/-- The same globally fixed certificate family is used by every width/layout/value case. -/ +noncomputable abbrev certificates (_ : Unit) : Certificate .standard := ⟨realizes_transfer_standard⟩ + +noncomputable def result (i : Fin 2) : Fin (Backend.registers certificates) := + Backend.tmp certificates () (Fin.castAdd ((certificates ()).realizes.scratchSize + 1) i) + +theorem native_body {w : ℕ} {d : Input} {s : Memory.State} + (hw : Memory.WellFormed w s) (hwords : ∀ i, Fits w (d i)) + (h : transferDomain w d s) : + ∃ y n, Exec w (Backend.code certificates ()) + (Backend.install certificates () d (Backend.data certificates s)) y n ∧ + n ≤ Backend.factor certificates * (2 * d 1 + 1) ∧ + y.mem = ((Memory.spec w).clientState s (transfer d)).mem ∧ + y.regs (result 0) = d 0 + d 1 ∧ y.regs (result 1) = 0 := by + have hi := transfer_invariant hw h + obtain ⟨y, n, he, hn, hm, hout, _⟩ := Backend.exists_exec certificates () d + (Backend.bounded_data certificates hw) hwords + (by simpa only [Backend.memoryState_data] using h) + (by simpa only [Backend.memoryState_data] using hi.1) + have hcost := hi.2.2.2.2.1 + change memoryCost (transfer d) s = 2 * d 1 at hcost + have hresult : (Memory.spec w).clientResult s (transfer d) = (d 0 + d 1, 0) := + (clientResult_width w _ _).trans hi.2.2.1 + have h0 := hout (0 : Fin 2) + have h1 := hout (1 : Fin 2) + rw [Backend.memoryState_data, hresult] at h0 h1 + rw [Backend.memoryState_data, hcost] at hn + have hmem := congrArg Memory.State.mem hm + rw [Backend.memoryState_data] at hmem + exact ⟨y, n, he, hn, hmem, h0, h1⟩ + +/-- Two iterations move a zero and the largest three-bit word through dirty physical arrays. -/ +def two : Input := ![0, 2, 1, 2, 7] + +def dirty : Memory.State := ⟨fun a => if a = 3 then 7 else if a = 4 then 0 else 5, 5⟩ + +theorem dirty_bounded : Memory.WellFormed 3 dirty := by + refine ⟨by decide, fun a => ?_⟩ + dsimp only [dirty] + split <;> (try split) <;> decide + +example : (Memory.spec 3).clientResult dirty (transfer two) = (2, 0) := rfl +example : memoryCost (transfer two) dirty = 4 := rfl +example : let t := (Memory.spec 3).clientState dirty (transfer two) + t.mem 1 = 0 ∧ t.mem 2 = 7 ∧ t.mem 3 = 7 ∧ t.mem 4 = 0 ∧ t.mem 6 = 5 := + ⟨rfl, rfl, rfl, rfl, rfl⟩ + +example : Block.HoldsList 1 [0, 7] + ((Memory.spec 3).clientState dirty (transfer two)).mem := by + rw [clientState_width 3] + exact transfer_contents (d := two) [0, 7] [] dirty rfl rfl (by decide) + (fun i => Fin.elim0 i) (by intro i; fin_cases i <;> rfl) + +example : ∃ y n, Exec 3 (Backend.code certificates ()) + (Backend.install certificates () two (Backend.data certificates dirty)) y n ∧ + n ≤ Backend.factor certificates * 5 ∧ y.mem 1 = 0 ∧ y.mem 2 = 7 ∧ + y.mem 6 = 5 ∧ y.regs (result 0) = 2 ∧ y.regs (result 1) = 0 := by + obtain ⟨y, n, he, hn, hm, h0, h1⟩ := native_body (d := two) dirty_bounded + (fun i => by fin_cases i <;> decide) (by change 0 + 2 ≤ 2 ∧ 1 + 2 * 2 ≤ 5; decide) + refine ⟨y, n, he, hn, ?_, ?_, ?_, h0, h1⟩ <;> rw [hm] <;> rfl + +-- The original small-width domain: base1/C1 fits without two extra count cells. +example : ∃ y n, Exec 2 (Backend.code certificates ()) + (Backend.install certificates () ![0, 1, 1, 1, 3] + (Backend.data certificates ⟨fun _ => 3, 3⟩)) y n ∧ + n ≤ Backend.factor certificates * 3 ∧ y.regs (result 0) = 1 ∧ + y.regs (result 1) = 0 := by + obtain ⟨y, n, he, hn, _, h0, h1⟩ := native_body (w := 2) + (d := ![0, 1, 1, 1, 3]) (s := ⟨fun _ => 3, 3⟩) + ⟨by decide, fun _ => (by decide : Fits 2 3)⟩ (fun i => by fin_cases i <;> decide) + (by change 0 + 1 ≤ 1 ∧ 1 + 2 * 1 ≤ 3; decide) + exact ⟨y, n, he, hn, h0, h1⟩ + +-- The same body exits with zero capacity, keeping every dirty memory cell. +example : ∃ y n, Exec 1 (Backend.code certificates ()) + (Backend.install certificates () ![0, 0, 0, 0, 1] + (Backend.data certificates ⟨fun _ => 1, 0⟩)) y n ∧ + n ≤ Backend.factor certificates ∧ y.mem = (fun _ => 1) ∧ + y.regs (result 0) = 0 ∧ y.regs (result 1) = 0 := by + obtain ⟨y, n, he, hn, hm, h0, h1⟩ := native_body (w := 1) + (d := ![0, 0, 0, 0, 1]) (s := ⟨fun _ => 1, 0⟩) + ⟨by decide, fun _ => (by decide : Fits 1 1)⟩ (fun i => by fin_cases i <;> decide) + (by change 0 + 0 ≤ 0 ∧ 0 + 2 * 0 ≤ 0; decide) + exact ⟨y, n, he, by simpa using hn, hm, h0, h1⟩ + +end ComplexityTests.QueueRegisterTransfer diff --git a/ComplexityTests/WordRAMQueue.lean b/ComplexityTests/WordRAMQueue.lean index 0662003f72..28e1760459 100644 --- a/ComplexityTests/WordRAMQueue.lean +++ b/ComplexityTests/WordRAMQueue.lean @@ -7,7 +7,7 @@ Authors: Samuel Schlesinger module -- upstream: local-only (test driver) -public import Complexity.DataStructure.Queue.WordRAM.Dynamic +public import Complexity.DataStructure.Queue.WordRAM.RegisterDynamic /-! # A worked adaptive FIFO example @@ -20,7 +20,7 @@ a nonempty-front dequeue, and an enqueue while both stack regions contain live v @[expose] public section -open Cslib Cslib.WordRAM +open Cslib Cslib.WordRAM Cslib.WordRAM.Realization open Cslib.DataStructure Cslib.DataStructure.Queue namespace WordRAMQueueTest @@ -47,15 +47,16 @@ theorem cycle_valid {w capacity a b : ℕ} (hc : 2 ≤ capacity) (ha : Fits w a) /-- The backend-independent FIFO trace becomes actual queue-operation executions. -/ example {w base capacity a b : ℕ} (h1 : Fits w 1) - (hc : Fits w (base + 2 * capacity)) (hsize : 2 ≤ capacity) - (ha : Fits w a) (hb : Fits w b) {x : Data 11} + (hsize : 2 ≤ capacity) + (ha : Fits w a) (hb : Fits w b) {x : Data Queue.WordRAM.registers} (hr : Queue.WordRAM.QueueRep w base capacity [] x) : ∃ y traces, DataStructure.Executes ((Queue.WordRAM.implementation w capacity).ExecOp w) (cycle a b) x (some a, some b) y traces ∧ Queue.WordRAM.QueueRep w base capacity [a] y ∧ DataStructure.total DynamicProgram.OperationTrace.instructions traces + - Queue.WordRAM.potential y ≤ 70 + Queue.WordRAM.potential x := by + Queue.WordRAM.potential y ≤ + 5 * (4 * Backend.factor Queue.WordRAM.family + 6) + Queue.WordRAM.potential x := by have hv : (Queue.WordRAM.spec w capacity).ValidHistory [] ((Queue.WordRAM.spec w capacity).runClient [] (cycle a b)).2.2 := ⟨⟨by simpa using (show 0 < capacity by omega), ha⟩, @@ -66,17 +67,19 @@ example {w base capacity a b : ℕ} (h1 : Fits w 1) Queue.WordRAM.spec, queue, List.nil_append, List.cons_append, List.tail_cons, List.head?_cons, Option.getD_some, List.length_cons, List.length_nil] using - Queue.WordRAM.run_client h1 hc (cycle a b) hr hv + Queue.WordRAM.run_client h1 (cycle a b) hr hv -- Nonzero unused memory is legitimate: only live prefixes carry queue contents. -private def stale : Data 11 := ⟨fun _ => 15, fun _ => 15⟩ +private def stale : Data Queue.WordRAM.registers := ⟨fun _ => 15, fun _ => 15⟩ private theorem stale_bounded : stale.Bounded 4 := ⟨fun _ => (by decide : Fits 4 15), fun _ => (by decide : Fits 4 15)⟩ example : ∃ ready, Exec 4 (Queue.WordRAM.initializeCode 3 2) stale ready 6 ∧ Queue.WordRAM.Rep 4 3 2 [] [] ready ∧ ready.mem = stale.mem := - Queue.WordRAM.initialize_exec stale_bounded (by decide) (by decide) + ⟨Queue.WordRAM.initialized 3 2 stale, + (Queue.WordRAM.initialize_exec stale_bounded (by decide)).1, + Queue.WordRAM.initialized_rep stale_bounded (by decide), rfl⟩ -- All five adaptive calls work from an initialized representation, including a zero word. example : ∃ ready y traces, Exec 4 (Queue.WordRAM.initializeCode 3 2) stale ready 6 ∧ @@ -84,8 +87,10 @@ example : ∃ ready y traces, Exec 4 (Queue.WordRAM.initializeCode 3 2) stale re (cycle 0 15) ready (some 0, some 15) y traces ∧ Queue.WordRAM.QueueRep 4 3 2 [0] y ∧ 6 + DataStructure.total DynamicProgram.OperationTrace.instructions traces + - Queue.WordRAM.potential y ≤ 76 := by - exact Queue.WordRAM.run_initialized (by decide) (by decide) (cycle 0 15) + Queue.WordRAM.potential y ≤ + 6 + 5 * (4 * Backend.factor Queue.WordRAM.family + 6) := by + exact Queue.WordRAM.run_initialized (w := 4) (base := 3) (capacity := 2) + (by decide) (by decide) (cycle 0 15) stale_bounded (cycle_valid (by decide) (by decide) (by decide)) -- Capacity zero allows an empty dequeue, but no enqueue (even of zero), and capacity/width bounds @@ -97,14 +102,18 @@ example : ¬(Queue.WordRAM.spec 4 2).valid [] (.enqueue 16) := ⟨by decide, trivial, by decide, by decide⟩ --- A refill of two rear words costs 28 including halt: a false 27 bound is rejected. -example {x y : Data 11} {a : Option ℕ} {trace : DynamicProgram.OperationTrace} +-- The realized program has one uniform factor rather than the removed hand-code's exact28 +-- refill clock. Its actual trace still pays control, commit and halt with terminal credit. +example {x y : Data Queue.WordRAM.registers} {a : Option ℕ} + {trace : DynamicProgram.OperationTrace} (hr : Queue.WordRAM.Rep 4 3 2 [] [15, 0] x) (he : (Queue.WordRAM.implementation 4 2).ExecOp 4 x .dequeue a y trace) : - trace.instructions = 28 ∧ ¬trace.instructions ≤ 27 := by - have h := Queue.WordRAM.instructions_eq (by decide) hr trivial he - change trace.instructions = 28 at h - omega + trace.instructions + Queue.WordRAM.potential y ≤ + 4 * Backend.factor Queue.WordRAM.family + 6 + Queue.WordRAM.potential x := by + obtain ⟨z, n, hz, _, _, _, hp⟩ := Queue.WordRAM.execute (by decide) hr .dequeue trivial + obtain ⟨rfl, hn⟩ := he.eq_of_exec hz + rw [hn] + exact hp -- Empty and zero-valued observations are semantically distinct, and the footprint size is `2 * c`. example : diff --git a/ROADMAP.md b/ROADMAP.md index 3893700ec0..5074aa693c 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -653,8 +653,12 @@ reusing graph and matroid foundations and the shared data-structure interfaces. remains only as the stepped component of Chan–He traversal (`Traversal.WordStack`). Backends of framed refinements export their frame (`modifies`, `frame`), run products over one memory, and allocate from arbitrary memory through `Memory.Allocator` (`Backend.Allocation`); the stack - and a pair of stacks check all three. Remaining: the queue and binary-heap migrations onto them, - and the full C2/M1b objective; arbitrary host continuations do not acquire runtime bounds. + and a pair of stacks check all three. Queue operation calls now use one fixed native + dispatcher over physical-array memory programs, with resident count words and paid commits. + Its dynamic certificate preserves the original two-array space, dirty six-instruction + initialization and adaptive-call bounds. Remaining are the binary-heap migration, the full + framed queue/faithful-allocation client and generic stateful composition, and the full C2/M1b + objective; 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 02705160b6..c74d4a36b1 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -570,10 +570,15 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he a balanced tree or machine-time certificate. `WordRAM/Dynamic` separately connects fixed bounded-word operation programs and faithful memory transcripts to those contracts. - The direct shared-heap queue uses the existing FIFO operation signature and native operation - traces, with a hidden two-stack representation, a concrete-register potential, framed storage, - and separately charged initialization. Its `2 * capacity` reservation is explicit; external - client computation and argument delivery are not charged by the operation-history bound. + The direct shared-heap queue realizes physical-array memory programs through one fixed + finite Backend family and the existing dynamic certificate. Count words remain in registers; + one native dispatcher, charged metadata copies and a scaled rear potential give uniform + amortized instruction bounds, including halt. Initialization uses six register instructions + from arbitrary bounded dirty data, preserving every memory cell. The original `2 * capacity` + reservation and end-fit domain are retained. The shared register count and exchange factor are + fixed before widths, layouts, values and clients; the removed hand-code's literal instruction + constants are not asserted. External client computation and tag/payload delivery remain outside + the operation-history bound; no generic stateful memory-only Backend instance is claimed. Coordinate placement and overlap with Srinivas's #685 and the merged #401 (`Algorithms/Lean/Query`) before upstream submission; #685 is not copied here. This is a prototype of composition and refinement, not a replacement for their query models or a general cell-probe lower-bound semantics.