Reusable procedures and memory preservation
Lax350013.ProcedureContracts · concepts/Lax350013/ProcedureContracts.lean · lax-350013
No public endorsements yet.
Loading review…
Sign in with ORCIDNatural Language Statement
Definition
A solver remains correct when other procedures are appended to its program. Its contract specifies inputs, outputs, preserved memory, time, and polynomial bounds on words, scratch space and call depth. These guarantees make the algorithmic reductions compositional.
Concept map
Lean source view on GitHub
| 1 | /- |
| 2 | Copyright (c) 2026 Anthropic, PBC. All rights reserved. |
| 3 | Released under Apache 2.0 license as described in the file LICENSE. |
| 4 | SPDX-License-Identifier: Apache-2.0 |
| 5 | -/ |
| 6 | /- |
| 7 | Modified for the independent Lax packaging by Édouard Bonnet, 2026. |
| 8 | Derived from 3sum-apsp/ThreeSumApsp/Lang and Util/List.lean at upstream commit e1a4e6508154ea59f030480661590a9fe3018011. |
| 9 | Changes: Lax module/namespace layout, separated concepts and proofs, archive |
| 10 | annotations, and compatibility with the archive Lean/mathlib environment. |
| 11 | See NOTICE and README.md in the submission root for provenance and scope. |
| 12 | -/ |
| 13 | |
| 14 | import Mathlib.Algebra.MvPolynomial.Basic |
| 15 | import Mathlib.Analysis.SpecialFunctions.Log.Base |
| 16 | import Mathlib.Analysis.SpecialFunctions.Log.Basic |
| 17 | import Mathlib.Analysis.SpecialFunctions.Pow.Real |
| 18 | import Mathlib.Combinatorics.SimpleGraph.Basic |
| 19 | import Mathlib.Data.Finset.Sort |
| 20 | import Mathlib.LinearAlgebra.Matrix.Notation |
| 21 | import Mathlib.MeasureTheory.Integral.Bochner.Basic |
| 22 | import Mathlib.NumberTheory.PrimeCounting |
| 23 | import Mathlib.Probability.Independence.Basic |
| 24 | import Mathlib.Tactic.DeriveFintype |
| 25 | import Lax350013.StructuredPrograms |
| 26 | |
| 27 | /-! |
| 28 | --- |
| 29 | title: Reusable procedures and memory preservation |
| 30 | type: definition |
| 31 | --- |
| 32 | A solver remains correct when other procedures are appended to its program. Its contract specifies inputs, outputs, preserved memory, time, and polynomial bounds on words, scratch space and call depth. These guarantees make the algorithmic reductions compositional. |
| 33 | -/ |
| 34 | |
| 35 | namespace Lax350013.ProcedureContracts |
| 36 | |
| 37 | open Finset |
| 38 | open Lax350013.StructuredPrograms |
| 39 | |
| 40 | /-- The statement s, started in σ, ends within T steps in a state that satisfies Q. -/ |
| 41 | def Ends (lim : Limits) (P : Program) (d : ℕ) (s : Stmt) (σ : State) (T : ℕ) (Q : State → Prop) : |
| 42 | Prop := |
| 43 | ∃ σ' c, Exec lim P d s σ σ' c ∧ c ≤ T ∧ Q σ' |
| 44 | |
| 45 | /-- The bound 2^s · ((p₁ + 1) (p₂ + 1) ⋯)^k in the parameters of an instance. -/ |
| 46 | def polyBound (s k : ℕ) (params : List ℕ) : ℕ := 2 ^ s * ((params.map (· + 1)).prod) ^ k |
| 47 | |
| 48 | /-- The cell `b` is among the `n` cells from address `a`. -/ |
| 49 | abbrev Inside (a n b : ℕ) : Prop := a ≤ b ∧ b < a + n |
| 50 | |
| 51 | /-- The cell `b` is not among the `n` cells from address `a`. -/ |
| 52 | abbrev Outside (a n b : ℕ) : Prop := b < a ∨ a + n ≤ b |
| 53 | |
| 54 | /-- Two regions of the memory, of `n` cells from `a` and of `n'` cells from `a'`, do not meet. -/ |
| 55 | abbrev Apart (a n a' n' : ℕ) : Prop := a + n ≤ a' ∨ a' + n' ≤ a |
| 56 | |
| 57 | /-- The memory `μ'` agrees with `μ` on every cell that satisfies `K`. -/ |
| 58 | def SameOn (K : ℕ → Prop) (μ μ' : ℕ → ℤ) : Prop := ∀ b, K b → μ' b = μ b |
| 59 | |
| 60 | /-- No cell below the free pointer has changed. -/ |
| 61 | abbrev Kept (μ μ' : ℕ → ℤ) (fr : ℕ) : Prop := SameOn (· < fr) μ μ' |
| 62 | |
| 63 | /-- No cell below the free pointer has changed, except the `len` cells from `out`. -/ |
| 64 | abbrev KeptBut (μ μ' : ℕ → ℤ) (fr out len : ℕ) : Prop := |
| 65 | SameOn (fun x => x < fr ∧ Outside out len x) μ μ' |
| 66 | |
| 67 | /-- The cells a, a + 1, … of the memory μ hold the list l. -/ |
| 68 | def Seg (μ : ℕ → ℤ) (a : ℕ) (l : List ℤ) : Prop := ∀ i (h : i < l.length), μ (a + i) = l[i] |
| 69 | |
| 70 | /-- The cells a, a + 1, … hold a list of natural numbers. -/ |
| 71 | abbrev SegN (μ : ℕ → ℤ) (a : ℕ) (l : List ℕ) : Prop := Seg μ a (l.map fun x : ℕ => (x : ℤ)) |
| 72 | |
| 73 | /-- All members of the list `l` have absolute value at most `U`. -/ |
| 74 | def AbsLe (l : List ℤ) (U : ℤ) : Prop := ∀ x ∈ l, |x| ≤ U |
| 75 | |
| 76 | /-- What a run needs: the largest absolute value it forms, the number of cells it uses from the free |
| 77 | pointer on, and the number of levels of calls below the procedure. -/ |
| 78 | structure Need : Type where |
| 79 | word : ℕ |
| 80 | cells : ℕ |
| 81 | depth : ℕ |
| 82 | |
| 83 | /-- The limits allow for the need of a procedure that is called at depth `d` with the free pointer |
| 84 | `fr`. An address always fits in a word. -/ |
| 85 | structure Need.Ok (r : Need) (lim : Limits) (fr d : ℕ) : Prop where |
| 86 | word : (r.word : ℤ) ≤ lim.word |
| 87 | cells : fr + r.cells ≤ lim.space |
| 88 | space : (lim.space : ℤ) ≤ lim.word |
| 89 | depth : d + r.depth ≤ lim.depth |
| 90 | |
| 91 | /-- A need that depends on a size and a bound is polynomially bounded in the two: by |
| 92 | `2^s ((n + 1) (U + 1))^k`. -/ |
| 93 | def PolyNeed (need : ℕ → ℕ → Need) : Prop := |
| 94 | ∃ s k : ℕ, ∀ n U : ℕ, (need n U).word ≤ polyBound s k [n, U] ∧ (need n U).cells ≤ |
| 95 | polyBound s k [n, U] ∧ |
| 96 | (need n U).depth ≤ polyBound s k [n, U] |
| 97 | |
| 98 | /-- A problem with a calling convention. -/ |
| 99 | structure Task : Type 1 where |
| 100 | /-- The instances, as they lie in the memory: sizes, bound, addresses, contents. -/ |
| 101 | Inst : Type |
| 102 | /-- The size of an instance. -/ |
| 103 | size : Inst → ℕ |
| 104 | /-- The bound on the absolute values of its numbers that is handed to the solver. -/ |
| 105 | bound : Inst → ℕ |
| 106 | /-- The arguments of the call, without the free pointer, which comes last. -/ |
| 107 | args : Inst → List ℤ |
| 108 | /-- The instance is valid, and it lies in the memory below the free pointer. -/ |
| 109 | Pre : Inst → (ℕ → ℤ) → ℕ → Prop |
| 110 | /-- The result and the final memory are right. (That the cells below the free pointer are |
| 111 | otherwise unchanged is part of this.) -/ |
| 112 | Post : Inst → (ℕ → ℤ) → ℕ → ℤ → (ℕ → ℤ) → Prop |
| 113 | |
| 114 | /-- **Procedure `p` of the program `P` solves the task** within `T (size) (bound)` steps, whenever |
| 115 | the limits allow for `need (size) (bound)`; and so it does in every program that begins with `P`. -/ |
| 116 | def Solves (task : Task) (P : Program) (p : ℕ) (T : ℕ → ℕ → ℕ) (need : ℕ → ℕ → Need) : Prop := |
| 117 | ∃ body, P[p]? = some body ∧ |
| 118 | ∀ (R : Program) (lim : Limits) (d : ℕ) (x : task.Inst) (μ : ℕ → ℤ) (fr : ℕ), task.Pre x μ fr → |
| 119 | (need (task.size x) (task.bound x)).Ok lim fr d → |
| 120 | Ends lim (P ++ R) d body ⟨frame (task.args x ++ [(fr : ℤ)]), μ⟩ |
| 121 | (T (task.size x) (task.bound x)) |
| 122 | fun σ' => task.Post x μ fr (σ'.loc 0) σ'.mem |
| 123 | |
| 124 | /-- "The task is solved in time `T`", for a real-valued `T` whose second argument is an upper bound |
| 125 | on the numbers: some solver with a polynomially bounded need takes at most `T n u` steps on every |
| 126 | instance of size `n ≥ 1` with a bound `1 ≤ U ≤ u`. -/ |
| 127 | def SolvedIn (task : Task) (T : ℕ → ℝ → ℝ) : Prop := |
| 128 | ∃ (P : Program) (p : ℕ) (Tn : ℕ → ℕ → ℕ) (need : ℕ → ℕ → Need), PolyNeed need ∧ |
| 129 | Solves task P p Tn need ∧ |
| 130 | ∀ (n U : ℕ) (u : ℝ), 1 ≤ n → 1 ≤ U → (U : ℝ) ≤ u → (Tn n U : ℝ) ≤ T n u |
| 131 | |
| 132 | /-- A problem with a calling convention and a list of parameters. -/ |
| 133 | structure TaskN : Type 1 where |
| 134 | /-- The instances, as they lie in the memory. -/ |
| 135 | Inst : Type |
| 136 | /-- The parameters on which time and need depend. -/ |
| 137 | pars : Inst → List ℕ |
| 138 | /-- The arguments of the call, without the free pointer, which comes last. -/ |
| 139 | args : Inst → List ℤ |
| 140 | /-- The instance is valid, and it lies in the memory below the free pointer. -/ |
| 141 | Pre : Inst → (ℕ → ℤ) → ℕ → Prop |
| 142 | /-- The result and the final memory are right. -/ |
| 143 | Post : Inst → (ℕ → ℤ) → ℕ → ℤ → (ℕ → ℤ) → Prop |
| 144 | |
| 145 | /-- Procedure `p` of the program `P` solves the task within `T pars` steps, whenever the limits |
| 146 | allow for `need pars`; and so it does in every program that begins with `P`. -/ |
| 147 | def SolvesN (task : TaskN) (P : Program) (p : ℕ) (T : List ℕ → ℕ) (need : List ℕ → Need) : Prop := |
| 148 | ∃ body, P[p]? = some body ∧ |
| 149 | ∀ (R : Program) (lim : Limits) (d : ℕ) (x : task.Inst) (μ : ℕ → ℤ) (fr : ℕ), task.Pre x μ fr → |
| 150 | (need (task.pars x)).Ok lim fr d → |
| 151 | Ends lim (P ++ R) d body ⟨frame (task.args x ++ [(fr : ℤ)]), μ⟩ (T (task.pars x)) |
| 152 | fun σ' => task.Post x μ fr (σ'.loc 0) σ'.mem |
| 153 | |
| 154 | /-- A need that is polynomially bounded in the parameters. -/ |
| 155 | def PolyNeedN (need : List ℕ → Need) : Prop := |
| 156 | ∃ s k : ℕ, ∀ ps : List ℕ, (need ps).word ≤ polyBound s k ps ∧ (need ps).cells ≤ polyBound s k ps ∧ |
| 157 | (need ps).depth ≤ polyBound s k ps |
| 158 | |
| 159 | end Lax350013.ProcedureContracts |
| 160 |
Builds on
Used by
From Mathlib
Mathlib.Algebra.MvPolynomial.BasicMathlib.Analysis.SpecialFunctions.Log.BaseMathlib.Analysis.SpecialFunctions.Log.BasicMathlib.Analysis.SpecialFunctions.Pow.RealMathlib.Combinatorics.SimpleGraph.BasicMathlib.Data.Finset.SortMathlib.LinearAlgebra.Matrix.NotationMathlib.MeasureTheory.Integral.Bochner.BasicMathlib.NumberTheory.PrimeCountingMathlib.Probability.Independence.BasicMathlib.Tactic.DeriveFintype
Discussion
Ask a question or add context. Endorsements and structured flags are kept in the review panel above.
0 comments