Skip to content

Commit e047989

Browse files
committed
feat: echo-approx-compose-lipschitz — Rung D (Lipschitz composition)
EchoApprox's composition handled only a non-expansive outer leg (L=1). Rung D generalises to a Lipschitz leg with constant `L` via a new layered `LipschitzScale` record (multiplication on `Tolerance` + right-monotonicity `a ≤ c ⇒ s * a ≤ s * c`), mirroring the `BalancedTolerance` / `Separated` opt-in-record pattern — the base `Tolerance` stays minimal so lop-sided carriers remain instances. A Lipschitz outer leg scales the inner tolerance by `L`: IsLipschitz L g = ∀ b₁ b₂ → dist (g b₁) (g b₂) ≤ L * dist b₁ b₂ echo-approx-compose-lipschitz : IsLipschitz L g → EchoR ε₁ f b → dist (g b) y ≤ ε₂ → EchoR (L * ε₁ + ε₂) (g ∘ f) y Same triangle + accumulate skeleton as `echo-approx-compose`, with the non-expansive step `dist (g·)(g·) ≤ dist · ·` replaced by the Lipschitz step `≤ L * dist · ·` and `*-monoʳ` carrying the inner bound through the constant. Recovers `echo-approx-compose` at the corner `L * ε ≡ ε`. `--safe --without-K`, zero postulates. Trivial-⊤ `LipschitzScale` instance + 2 Smoke pins in `EchoApproxInstance` (the parameterised-module pinning pattern); the header "Rung D deferred" note flipped to LANDED. EchoApprox + EchoApproxInstance + Smoke.agda + All.agda exit 0; kernel-guard PASS. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018CaSgNjNURC7ocsyjYh9We
1 parent a8ef31d commit e047989

3 files changed

Lines changed: 79 additions & 3 deletions

File tree

proofs/agda/EchoApprox.agda

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,11 @@
7979
-- a layered record on top of `Tolerance`, NOT a mutation of the base
8080
-- interface — mirrors `Separated`/`PseudoMetric`). §7 obligations 7
8181
-- (separated zero-collapse) and 8 (axis-1 shadow agreement) are
82-
-- landed below. Rung D (Lipschitz `L_g ≠ 1`) is deferred — it requires
83-
-- multiplication on `Tolerance`, another interface call.
82+
-- landed below. Rung D (Lipschitz `L_g ≠ 1`) is now LANDED via the
83+
-- layered `LipschitzScale` record (multiplication on `Tolerance`,
84+
-- mirroring `BalancedTolerance` — the interface call resolved in favour
85+
-- of the same opt-in-record pattern); see `echo-approx-compose-lipschitz`
86+
-- in the `Lipschitz` sub-module at the bottom of `module Approx`.
8487
--
8588
-- The full LHS-element round-trip equality `sound (retract-to e) ≡ e`
8689
-- (with the budget transported via `+-identityˡ`) is NOT discharged
@@ -160,6 +163,28 @@ record BalancedTolerance {ℓ} (T : Tolerance ℓ) : Set ℓ where
160163
+-identityˡ : ε zero + ε ≡ ε
161164
+-identityʳ : ε ε + zero ≡ ε
162165

166+
----------------------------------------------------------------------
167+
-- Lipschitz scale: a `Tolerance` with a multiplication monotone in its
168+
-- right argument — the structure under which a Lipschitz (rather than
169+
-- merely non-expansive) outer leg accumulates tolerance *multiplicatively*
170+
-- by its constant.
171+
--
172+
-- Layered on `Tolerance` exactly as `BalancedTolerance` / `Separated`
173+
-- are: an extra structure callers opt into, NOT a field of the base
174+
-- record (which stays minimal so lop-sided carriers remain instances).
175+
-- The minimal content for the Lipschitz composition headline is `_*_`
176+
-- and right-monotonicity `a ≤ c ⇒ s * a ≤ s * c`; associativity,
177+
-- identity, and distributivity are not needed here and are left to
178+
-- richer carriers.
179+
----------------------------------------------------------------------
180+
181+
record LipschitzScale {ℓ} (T : Tolerance ℓ) : Setwhere
182+
open Tolerance T
183+
infixl 7 _*_
184+
field
185+
_*_ : Tol Tol Tol
186+
*-monoʳ : {s a c} a ≤ c (s * a) ≤ (s * c)
187+
163188
----------------------------------------------------------------------
164189
-- Approximate echo
165190
----------------------------------------------------------------------
@@ -542,3 +567,35 @@ module Approx
542567
(echo-strict→approx e))
543568
≡ proj₁ e
544569
echo-strict→approx-collapse-shadow-A sep (x , _) = refl
570+
571+
----------------------------------------------------------------------
572+
-- Lipschitz composition (Rung D)
573+
----------------------------------------------------------------------
574+
575+
-- Parameterised by a chosen `LipschitzScale S` (the opt-in multiplication
576+
-- on the tolerance). The non-expansive `echo-approx-compose` is the
577+
-- corner where `L * ε ≡ ε`.
578+
module Lipschitz (S : LipschitzScale T) where
579+
open LipschitzScale S
580+
581+
-- `g` is `L`-Lipschitz: it stretches distances by at most a factor `L`.
582+
IsLipschitz : Tol (B B) Set (b ⊔ ℓ)
583+
IsLipschitz L g = b₁ b₂ dist (g b₁) (g b₂) ≤ (L * dist b₁ b₂)
584+
585+
-- Lipschitz composition. A Lipschitz outer leg with constant `L`
586+
-- scales the inner tolerance by `L`: an `EchoR ε₁` inner echo
587+
-- composes to an `EchoR (L * ε₁ + ε₂)` echo. Same triangle +
588+
-- accumulate skeleton as `echo-approx-compose`, with the
589+
-- non-expansive step `dist (g·)(g·) ≤ dist · ·` replaced by the
590+
-- Lipschitz step `≤ L * dist · ·`, and `*-monoʳ` carrying the inner
591+
-- bound `dist (f x) b ≤ ε₁` through the constant to `L * ε₁`.
592+
echo-approx-compose-lipschitz :
593+
{L : Tol} {g : B B} {f : A B} {ε₁ ε₂ : Tol} {b y : B}
594+
IsLipschitz L g
595+
EchoR ε₁ f b
596+
dist (g b) y ≤ ε₂
597+
EchoR ((L * ε₁) + ε₂) (g ∘ f) y
598+
echo-approx-compose-lipschitz {L} {g} {f} {ε₁} {ε₂} {b} {y}
599+
lip (x , dfx≤ε₁) dgby≤ε₂ =
600+
x , ≤-trans (dist-tri (g (f x)) (g b) y)
601+
(+-mono-≤ (≤-trans (lip (f x) b) (*-monoʳ dfx≤ε₁)) dgby≤ε₂)

proofs/agda/EchoApproxInstance.agda

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ open import Level using (Level)
4545
open import Data.Unit.Base using (⊤; tt)
4646
open import Relation.Binary.PropositionalEquality using (refl)
4747

48-
open import EchoApprox using (Tolerance; PseudoMetric; BalancedTolerance; module Approx)
48+
open import EchoApprox
49+
using (Tolerance; PseudoMetric; BalancedTolerance; LipschitzScale; module Approx)
4950

5051
----------------------------------------------------------------------
5152
-- The trivial tolerance carrier
@@ -89,6 +90,19 @@ trivialBalancedTolerance = record
8990
; +-identityʳ = λ _ refl
9091
}
9192

93+
----------------------------------------------------------------------
94+
-- The trivial LipschitzScale instance on `trivialTolerance`
95+
----------------------------------------------------------------------
96+
97+
-- On `Tol := ⊤`, multiplication is constantly `tt` and right-monotonicity
98+
-- discharges to `tt`. Pinned so `Smoke.agda` can enumerate the Rung-D
99+
-- Lipschitz lemmas at a typeable instance, same spirit as the pins below.
100+
trivialLipschitzScale : LipschitzScale trivialTolerance
101+
trivialLipschitzScale = record
102+
{ _*_ = λ _ _ tt
103+
; *-monoʳ = λ _ tt
104+
}
105+
92106
----------------------------------------------------------------------
93107
-- Per-lemma proof-of-life pins for `Approx` at the trivial instance.
94108
--
@@ -102,6 +116,7 @@ trivialBalancedTolerance = record
102116
private
103117
open module ApproxT⊤ =
104118
Approx {A = ⊤} {B = ⊤} {T = trivialTolerance} trivialPseudoMetric
119+
open module ApproxLip⊤ = ApproxT⊤.Lipschitz trivialLipschitzScale
105120

106121
approx-EchoR = EchoR
107122
approx-intro = echo-approx-intro
@@ -123,3 +138,5 @@ approx-shadow-iso-to = echo-shadow-iso-to
123138
approx-shadow-iso-from = echo-shadow-iso-from
124139
approx-strict→approx-shadow-A = echo-strict→approx-shadow-A
125140
approx-strict→approx-collapse-shadow-A = echo-strict→approx-collapse-shadow-A
141+
approx-IsLipschitz = IsLipschitz
142+
approx-compose-lipschitz = echo-approx-compose-lipschitz

proofs/agda/Smoke.agda

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,8 @@ open import EchoApproxInstance using
417417
; approx-shadow-iso-from
418418
; approx-strict→approx-shadow-A
419419
; approx-strict→approx-collapse-shadow-A
420+
; approx-IsLipschitz
421+
; approx-compose-lipschitz
420422
)
421423

422424
-- Axis 8 third quantitative artifact (taxonomy.md §8, refinement 1):

0 commit comments

Comments
 (0)