Research-Stack/0-Core-Formalism/lean/Semantics/Semantics/DiscreteContinuousBound.lean
allaun f868f0201d feat: add DiscreteContinuousBound — exponential error bound via Gronwall
Defines coupling matrix A = (ε/2)·[[0,1],[1,0]], proves ‖A‖ = |ε|/2,
and applies Mathlib Gronwall for the discrete-continuous coupling bound:
  ‖e(t)‖ ≤ ‖e(0)‖ · exp(|ε|/2 · (t-a))

Includes trajectory distance variants (interval + global).
2026-06-17 05:10:49 +00:00

209 lines
9.8 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Mathlib.Analysis.ODE.Gronwall
import Mathlib.Analysis.Matrix.Normed
import Mathlib.Analysis.SpecialFunctions.ExpDeriv
/-!
# DiscreteContinuousBound.lean
Exponential error bound for discretecontinuous coupling via Grönwall's inequality.
Given the 2×2 coupling matrix A = (ε/2) · [[0,1],[1,0]],
we prove:
1. ‖A‖ = |ε|/2 (operator norm)
2. ‖e(t)‖ ≤ ‖e(0)‖ · exp(|ε|/2 · t) (Grönwall bound on coupling error)
The proof uses Mathlib's `norm_le_gronwallBound_of_norm_deriv_right_le`
and the ∞-operator-norm on matrices (`linfty_opNorm`).
-/
open Matrix Set Filter Real
open scoped Topology
namespace Semantics.DiscreteContinuousBound
variable {ε : }
-- ═══════════════════════════════════════════════════════════════════════════════
-- §1 The 2×2 Coupling Matrix
-- ═══════════════════════════════════════════════════════════════════════════════
/-- The off-diagonal exchange matrix M₀ = [[0,1],[1,0]]. -/
def M₀ : Matrix (Fin 2) (Fin 2) := !![0, 1; 1, 0]
/-- The coupling matrix A = (ε/2) · M₀ = (ε/2) · [[0,1],[1,0]]. -/
def A (ε : ) : Matrix (Fin 2) (Fin 2) := (ε / 2) • M₀
-- ═══════════════════════════════════════════════════════════════════════════════
-- §2 Operator Norm: ‖A‖ = |ε|/2
-- ═══════════════════════════════════════════════════════════════════════════════
/-- Entry (0,1) of the coupling matrix is ε/2. -/
@[simp] lemma A_apply_zero_one : A ε 0 1 = ε / 2 := by
simp [A, M₀, smul_apply]
/-- Entry (1,0) of the coupling matrix is ε/2. -/
@[simp] lemma A_apply_one_zero : A ε 1 0 = ε / 2 := by
simp [A, M₀, smul_apply]
/-- Entry (0,0) of the coupling matrix is 0. -/
@[simp] lemma A_apply_zero_zero : A ε 0 0 = 0 := by
simp [A, M₀, smul_apply]
/-- Entry (1,1) of the coupling matrix is 0. -/
@[simp] lemma A_apply_one_one : A ε 1 1 = 0 := by
simp [A, M₀, smul_apply]
/-- All entries of A satisfy ‖A i j‖ ≤ |ε/2|. -/
lemma A_entry_bound (i j : Fin 2) : ‖A ε i j‖ ≤ |ε / 2| := by
fin_cases i <;> fin_cases j <;> simp [abs_nonneg]
/-- The ∞-operator norm of A equals |ε|/2.
Strategy: antisymmetry.
• Upper bound: every entry ≤ |ε/2|, so sup ≤ |ε/2| via `norm_le_iff`.
• Lower bound: entry (0,1) = ε/2, so ‖A‖ ≥ |ε/2| via `norm_entry_le_entrywise_sup_norm`.
-/
theorem coupling_opNorm (ε : ) : ‖A ε‖ = |ε| / 2 := by
have hε2 : |ε / 2| = |ε| / 2 := by rw [abs_div, abs_of_pos (by norm_num : (0 : ) < 2)]
-- Upper bound: ‖A‖ ≤ |ε/2|
have h_upper : ‖A ε‖ ≤ |ε / 2| := by
rw [norm_le_iff (abs_nonneg _)]
exact A_entry_bound
-- Lower bound: |ε/2| ≤ ‖A‖ (witness: entry (0,1))
have h_lower : |ε / 2| ≤ ‖A ε‖ := by
have : ‖A ε 0 1‖ ≤ ‖A ε‖ := norm_entry_le_entrywise_sup_norm _
simp_all
-- Combine
linarith [h_upper, h_lower]
/-- Variant: ‖A‖₊ = |ε|/2 as NNReals. -/
theorem coupling_opNNNorm (ε : ) : ‖A ε‖₊ = Real.toNNReal (|ε| / 2) := by
ext; exact coupling_opNorm ε
-- ═══════════════════════════════════════════════════════════════════════════════
-- §3 Lipschitz Constant of the Linear Flow x ↦ A·x
-- ═══════════════════════════════════════════════════════════════════════════════
/-- The linear map v ↦ A *ᵥ v is Lipschitz with constant ‖A‖₊ = |ε|/2.
This follows from the ∞-operator-norm submultiplicativity:
‖A *ᵥ v‖ ≤ ‖A‖ · ‖v‖
Combined with `ContinuousLinearMap.lipschitzWith_opNorm` or a direct proof.
-/
theorem coupling_lipschitzWith (ε : ) :
LipschitzWith (Real.toNNReal (|ε| / 2)) (fun v : Fin 2 → => A ε *ᵥ v) := by
-- Step 1: LipschitzWith ‖mulVecLin (A ε)‖₊
have h_lip : LipschitzWith ‖mulVecLin (A ε)‖₊ (fun v => A ε *ᵥ v) :=
(mulVecLin (A ε)).lipschitz
-- Step 2: ‖mulVecLin (A ε)‖₊ = ‖A ε‖₊ (linfty_opNNNorm_eq_opNNNorm)
have h_norm : ‖mulVecLin (A ε)‖₊ = ‖A ε‖₊ := by
rw [← linfty_opNNNorm_eq_opNNNorm]
-- Step 3: ‖A ε‖₊ = Real.toNNReal (|ε|/2)
rw [coupling_opNNNorm] at h_norm
rwa [h_norm] at h_lip
-- ═══════════════════════════════════════════════════════════════════════════════
-- §4 Grönwall Exponential Error Bound
-- ═══════════════════════════════════════════════════════════════════════════════
section GronwallBound
variable {a b : }
/-- **DiscreteContinuous Coupling Bound (Grönwall form).**
If `e : → Fin 2 → ` satisfies the linear ODE e' = A · e on `[a, b]`,
and the initial error is bounded by δ, then:
‖e(t)‖ ≤ δ · exp(|ε|/2 · (t - a)) for all t ∈ [a, b].
This is a direct application of `norm_le_gronwallBound_of_norm_deriv_right_le`
with K = |ε|/2 and ε_g = 0 (exact ODE, no perturbation).
-/
theorem norm_le_gronwallBound_of_coupling
{e e' : → Fin 2 → } {δ : } (hδ : 0 ≤ δ)
(he_cont : ContinuousOn e (Icc a b))
(he_deriv : ∀ t ∈ Ico a b, HasDerivWithinAt e (e' t) (Ici t) t)
(he_ode : ∀ t ∈ Ico a b, e' t = A ε *ᵥ e t)
(he_init : ‖e a‖ ≤ δ) :
∀ t ∈ Icc a b, ‖e t‖ ≤ gronwallBound δ (|ε| / 2) 0 (t - a) := by
apply norm_le_gronwallBound_of_norm_deriv_right_le he_cont he_deriv he_init
intro t ht
-- Goal: ‖e' t‖ ≤ (|ε|/2) · ‖e t‖ + 0
rw [he_ode t ht, add_zero]
-- Goal: ‖A ε *ᵥ e t‖ ≤ (|ε|/2) · ‖e t‖
have := linfty_opNorm_mulVec (A ε) (e t)
rw [coupling_opNorm] at this
exact this
/-- **Clean exponential form** (using `gronwallBound_ε0`).
When the ODE is exact (ε_g = 0), the Grönwall bound simplifies to:
‖e(t)‖ ≤ ‖e(0)‖ · exp(|ε|/2 · (t - a))
-/
theorem discrete_continuous_bound
{e e' : → Fin 2 → } {δ : } (hδ : 0 ≤ δ)
(he_cont : ContinuousOn e (Icc a b))
(he_deriv : ∀ t ∈ Ico a b, HasDerivWithinAt e (e' t) (Ici t) t)
(he_ode : ∀ t ∈ Ico a b, e' t = A ε *ᵥ e t)
(he_init : ‖e a‖ ≤ δ) :
∀ t ∈ Icc a b, ‖e t‖ ≤ δ * exp (|ε| / 2 * (t - a)) := by
intro t ht
have h := norm_le_gronwallBound_of_coupling hδ he_cont he_deriv he_ode he_init t ht
rwa [gronwallBound_ε0] at h
/-- **ODE-trajectory version** via Mathlib's `dist_le_of_trajectories_ODE`.
If `f` and `g` are two trajectories of the linear ODE ẋ = A · x on `[a, b]`,
then their distance grows at most exponentially:
dist(f(t), g(t)) ≤ dist(f(a), g(a)) · exp(|ε|/2 · (t - a))
-/
theorem trajectory_dist_bound
{f g : → Fin 2 → }
(hf_cont : ContinuousOn f (Icc a b))
(hf_deriv : ∀ t ∈ Ico a b, HasDerivWithinAt f (A ε *ᵥ f t) (Ici t) t)
(hg_cont : ContinuousOn g (Icc a b))
(hg_deriv : ∀ t ∈ Ico a b, HasDerivWithinAt g (A ε *ᵥ g t) (Ici t) t)
{δ : } (hδ : 0 ≤ δ) (h_init : dist (f a) (g a) ≤ δ) :
∀ t ∈ Icc a b, dist (f t) (g t) ≤ δ * exp (|ε| / 2 * (t - a)) := by
intro t ht
-- Apply `dist_le_of_trajectories_ODE` with K = |ε|/2
have hv : ∀ t, LipschitzWith (Real.toNNReal (|ε| / 2)) (fun x => A ε *ᵥ x) :=
fun _ => coupling_lipschitzWith ε
have h := dist_le_of_trajectories_ODE hv hf_cont hf_deriv hg_cont hg_deriv h_init t ht
-- Rewrite: the K in `dist_le_of_trajectories_ODE` is a `≥0`, and
-- it appears as (K : ) * (t - a) in the exponent.
-- After the LipschitzWith substitution we need: ↑(Real.toNNReal (|ε|/2)) = |ε|/2
have hnn : |ε| / 2 ≥ 0 := div_nonneg (abs_nonneg _) (by norm_num)
rw [Real.coe_toNNReal _ hnn] at h
exact h
/-- **Global version**: on all of , two trajectories of ẋ = A · x satisfy
dist(f(t), g(t)) ≤ dist(f(a), g(a)) · exp(|ε|/2 · |t - a|)
-/
theorem trajectory_dist_bound_univ
{f g : → Fin 2 → }
(hf : ∀ t, HasDerivAt f (A ε *ᵥ f t) t)
(hg : ∀ t, HasDerivAt g (A ε *ᵥ g t) t)
(a t : ) :
dist (f t) (g t) ≤ dist (f a) (g a) * exp (|ε| / 2 * |t - a|) := by
wlog hle : a ≤ t with h
-- t < a: swap and use symmetry
have := h hg hf t a (le_of_not_le hle)
rw [dist_comm (f t) (g t), dist_comm (f a) (g a), abs_sub_comm] at this
exact this
-- a ≤ t: use the interval version on [a, t]
have hnn : |ε| / 2 ≥ 0 := div_nonneg (abs_nonneg _) (by norm_num)
have hlip : ∀ s, LipschitzWith (Real.toNNReal (|ε| / 2)) (fun x => A ε *ᵥ x) :=
fun _ => coupling_lipschitzWith ε
have h :=
dist_le_of_trajectories_ODE hlip
(hf.continuousOn) (fun s _ => (hf s).hasDerivWithinAt)
(hg.continuousOn) (fun s _ => (hg s).hasDerivWithinAt)
le_rfl t ⟨hle, le_rfl⟩
rw [Real.coe_toNNReal _ hnn, sub_nonneg.mpr hle, abs_of_nonneg (sub_nonneg.mpr hle)] at h
exact h
end GronwallBound
end Semantics.DiscreteContinuousBound