From 1819eb968f2e88b01aa08b82326992765999c88e Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Thu, 3 Sep 2026 03:27:47 -0700 Subject: [PATCH 1/2] Import simple zeros of the Riemann zeta function --- LeanPool.lean | 33 + LeanPool/ZetaZeros.lean | 18 + LeanPool/ZetaZeros/Defs.lean | 149 + .../ZetaZeros/Hilbert/AlphaExpansion.lean | 2685 +++++++++++++++++ LeanPool/ZetaZeros/Hilbert/Basis.lean | 73 + LeanPool/ZetaZeros/Hilbert/Defs.lean | 87 + LeanPool/ZetaZeros/Hilbert/Dimensions.lean | 83 + LeanPool/ZetaZeros/Hilbert/FIdentity.lean | 110 + LeanPool/ZetaZeros/Hilbert/InnerReal.lean | 50 + LeanPool/ZetaZeros/Hilbert/InnerRealL2.lean | 51 + LeanPool/ZetaZeros/Hilbert/Integrals.lean | 101 + LeanPool/ZetaZeros/Hilbert/L2.lean | 115 + LeanPool/ZetaZeros/Hilbert/Subspaces.lean | 84 + LeanPool/ZetaZeros/Hilbert/Symmetry.lean | 61 + LeanPool/ZetaZeros/Hilbert/SymmetryL2.lean | 72 + LeanPool/ZetaZeros/Main.lean | 141 + LeanPool/ZetaZeros/Meta/Attr.lean | 42 + .../MontgomeryTaylor/AffineKernel.lean | 250 ++ .../ZetaZeros/MontgomeryTaylor/Basic.lean | 71 + .../MontgomeryTaylor/Evaluation.lean | 138 + .../MontgomeryTaylor/Integrability.lean | 69 + .../ZetaZeros/MontgomeryTaylor/Reduction.lean | 378 +++ .../MontgomeryTaylor/TestFunction.lean | 102 + .../ZetaZeros/Numeric/MontgomeryTaylor.lean | 181 ++ LeanPool/ZetaZeros/Zeta/Asymptotics.lean | 56 + LeanPool/ZetaZeros/Zeta/Basic.lean | 302 ++ LeanPool/ZetaZeros/Zeta/Cutoff.lean | 49 + LeanPool/ZetaZeros/Zeta/Defs.lean | 95 + LeanPool/ZetaZeros/Zeta/Finite.lean | 45 + LeanPool/ZetaZeros/Zeta/Kernel.lean | 806 +++++ LeanPool/ZetaZeros/Zeta/Mass.lean | 63 + LeanPool/ZetaZeros/Zeta/OrderConj.lean | 272 ++ LeanPool/ZetaZeros/Zeta/Proportion.lean | 68 + LeanPool/ZetaZeros/Zeta/Transfer.lean | 66 + LeanPool/projects.yml | 60 + 35 files changed, 7026 insertions(+) create mode 100644 LeanPool/ZetaZeros.lean create mode 100644 LeanPool/ZetaZeros/Defs.lean create mode 100644 LeanPool/ZetaZeros/Hilbert/AlphaExpansion.lean create mode 100644 LeanPool/ZetaZeros/Hilbert/Basis.lean create mode 100644 LeanPool/ZetaZeros/Hilbert/Defs.lean create mode 100644 LeanPool/ZetaZeros/Hilbert/Dimensions.lean create mode 100644 LeanPool/ZetaZeros/Hilbert/FIdentity.lean create mode 100644 LeanPool/ZetaZeros/Hilbert/InnerReal.lean create mode 100644 LeanPool/ZetaZeros/Hilbert/InnerRealL2.lean create mode 100644 LeanPool/ZetaZeros/Hilbert/Integrals.lean create mode 100644 LeanPool/ZetaZeros/Hilbert/L2.lean create mode 100644 LeanPool/ZetaZeros/Hilbert/Subspaces.lean create mode 100644 LeanPool/ZetaZeros/Hilbert/Symmetry.lean create mode 100644 LeanPool/ZetaZeros/Hilbert/SymmetryL2.lean create mode 100644 LeanPool/ZetaZeros/Main.lean create mode 100644 LeanPool/ZetaZeros/Meta/Attr.lean create mode 100644 LeanPool/ZetaZeros/MontgomeryTaylor/AffineKernel.lean create mode 100644 LeanPool/ZetaZeros/MontgomeryTaylor/Basic.lean create mode 100644 LeanPool/ZetaZeros/MontgomeryTaylor/Evaluation.lean create mode 100644 LeanPool/ZetaZeros/MontgomeryTaylor/Integrability.lean create mode 100644 LeanPool/ZetaZeros/MontgomeryTaylor/Reduction.lean create mode 100644 LeanPool/ZetaZeros/MontgomeryTaylor/TestFunction.lean create mode 100644 LeanPool/ZetaZeros/Numeric/MontgomeryTaylor.lean create mode 100644 LeanPool/ZetaZeros/Zeta/Asymptotics.lean create mode 100644 LeanPool/ZetaZeros/Zeta/Basic.lean create mode 100644 LeanPool/ZetaZeros/Zeta/Cutoff.lean create mode 100644 LeanPool/ZetaZeros/Zeta/Defs.lean create mode 100644 LeanPool/ZetaZeros/Zeta/Finite.lean create mode 100644 LeanPool/ZetaZeros/Zeta/Kernel.lean create mode 100644 LeanPool/ZetaZeros/Zeta/Mass.lean create mode 100644 LeanPool/ZetaZeros/Zeta/OrderConj.lean create mode 100644 LeanPool/ZetaZeros/Zeta/Proportion.lean create mode 100644 LeanPool/ZetaZeros/Zeta/Transfer.lean diff --git a/LeanPool.lean b/LeanPool.lean index 6440a2bbd1..cab9cbdacf 100644 --- a/LeanPool.lean +++ b/LeanPool.lean @@ -3992,6 +3992,39 @@ import LeanPool.ZetaH123.H1 import LeanPool.ZetaH123.H2 import LeanPool.ZetaH123.H3 import LeanPool.ZetaH123.Lem41 +import LeanPool.ZetaZeros +import LeanPool.ZetaZeros.Defs +import LeanPool.ZetaZeros.Hilbert.AlphaExpansion +import LeanPool.ZetaZeros.Hilbert.Basis +import LeanPool.ZetaZeros.Hilbert.Defs +import LeanPool.ZetaZeros.Hilbert.Dimensions +import LeanPool.ZetaZeros.Hilbert.FIdentity +import LeanPool.ZetaZeros.Hilbert.InnerReal +import LeanPool.ZetaZeros.Hilbert.InnerRealL2 +import LeanPool.ZetaZeros.Hilbert.Integrals +import LeanPool.ZetaZeros.Hilbert.L2 +import LeanPool.ZetaZeros.Hilbert.Subspaces +import LeanPool.ZetaZeros.Hilbert.Symmetry +import LeanPool.ZetaZeros.Hilbert.SymmetryL2 +import LeanPool.ZetaZeros.Main +import LeanPool.ZetaZeros.Meta.Attr +import LeanPool.ZetaZeros.MontgomeryTaylor.AffineKernel +import LeanPool.ZetaZeros.MontgomeryTaylor.Basic +import LeanPool.ZetaZeros.MontgomeryTaylor.Evaluation +import LeanPool.ZetaZeros.MontgomeryTaylor.Integrability +import LeanPool.ZetaZeros.MontgomeryTaylor.Reduction +import LeanPool.ZetaZeros.MontgomeryTaylor.TestFunction +import LeanPool.ZetaZeros.Numeric.MontgomeryTaylor +import LeanPool.ZetaZeros.Zeta.Asymptotics +import LeanPool.ZetaZeros.Zeta.Basic +import LeanPool.ZetaZeros.Zeta.Cutoff +import LeanPool.ZetaZeros.Zeta.Defs +import LeanPool.ZetaZeros.Zeta.Finite +import LeanPool.ZetaZeros.Zeta.Kernel +import LeanPool.ZetaZeros.Zeta.Mass +import LeanPool.ZetaZeros.Zeta.OrderConj +import LeanPool.ZetaZeros.Zeta.Proportion +import LeanPool.ZetaZeros.Zeta.Transfer import LeanPool.ZhangYeungInequality import LeanPool.ZhangYeungInequality.CopyLemma import LeanPool.ZhangYeungInequality.Delta diff --git a/LeanPool/ZetaZeros.lean b/LeanPool/ZetaZeros.lean new file mode 100644 index 0000000000..e0492196c8 --- /dev/null +++ b/LeanPool/ZetaZeros.lean @@ -0,0 +1,18 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Axiom Math, Kenny Lau +-/ + +import LeanPool.ZetaZeros.Main + +/-! +# Simple zeros of the Riemann zeta function + +Source: arxiv:2609.02882, doi:10.48550/arXiv.2609.02882, url:https://github.com/AxiomMath/ZetaZeros +Authors: Kenny Lau +Status: verified +Main declarations: `ZetaZeros.simple_proportion_d4` +Tags: analytic-number-theory, riemann-zeta-function, critical-line, simple-zeros, pair-correlation +MSC: 11M06, 11M26 +-/ diff --git a/LeanPool/ZetaZeros/Defs.lean b/LeanPool/ZetaZeros/Defs.lean new file mode 100644 index 0000000000..ae88ed6958 --- /dev/null +++ b/LeanPool/ZetaZeros/Defs.lean @@ -0,0 +1,149 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public import Mathlib.NumberTheory.LSeries.RiemannZeta +public import Mathlib.Analysis.Analytic.Order +public import LeanPool.ZetaZeros.Meta.Attr + +/-! # The vocabulary of the main results + +Every notion appearing in the statements of the headline theorems: the zero counts, the +Hilbert-space objects of the key proposition, the pair-correlation apparatus, and the two +classical analytic inputs. + +These are kept in one module, in the order `Challenge/Basic.lean` repeats them, and should stay +that way. Elaboration lifts a nested proof out of a definition's body into an auxiliary theorem +named after whichever definition in the *module* first needed it, so splitting these across +modules gives them names a single self-contained file cannot reproduce. +-/ + +@[expose] public section + +namespace ZetaZeros + +/-- The non-trivial zeros of the Riemann zeta function with imaginary part in `(0, T]`: the +zeros lying in the critical strip `0 < re s < 1`, as a set, so without multiplicity. -/ +@[zz_tag "def_nontrivial_zeros"] +def nontrivialZeros (T : ℝ) : Set ℂ := + {ρ | riemannZeta ρ = 0 ∧ 0 < ρ.re ∧ ρ.re < 1 ∧ 0 < ρ.im ∧ ρ.im ≤ T} + +/-- The multiplicity of `ρ` as a zero of the Riemann zeta function, i.e. its order of +vanishing there. -/ +@[zz_tag "def_multiplicity"] +noncomputable def zeroMultiplicity (ρ : ℂ) : ℕ := analyticOrderNatAt riemannZeta ρ + +/-- The number of non-trivial zeros with imaginary part in `(0, T]`, counted with multiplicity. +This is `N T` in the source. -/ +@[zz_tag "def_N"] +noncomputable def zeroCount (T : ℝ) : ℕ := ∑ᶠ ρ ∈ nontrivialZeros T, zeroMultiplicity ρ + +/-- The number of non-trivial zeros with imaginary part in `(0, T]` that are simple and lie on +the critical line `re s = 1/2`. This is `N₀ˢ T` in the source. -/ +@[zz_tag "def_N_simple"] +noncomputable def simpleOnLineCount (T : ℝ) : ℕ := + {ρ ∈ nontrivialZeros T | ρ.re = 1 / 2 ∧ zeroMultiplicity ρ = 1}.ncard + +/-- The number of distinct non-trivial zeros with imaginary part in `(0, T]`. This is `N_d T` +in the source. -/ +@[zz_tag "def_N_distinct"] +noncomputable def distinctZeroCount (T : ℝ) : ℕ := (nontrivialZeros T).ncard + +/-- The Fourier transform of a compactly supported real function, at a complex argument. -/ +@[zz_tag "def_fourier"] +noncomputable def fourierC (f : ℝ → ℝ) (ξ : ℂ) : ℂ := + ∫ u : ℝ, (f u : ℂ) * Complex.exp (-(2 * (Real.pi : ℂ)) * Complex.I * ξ * (u : ℂ)) + +/-- `eta` is `lam`-admissible: square-integrable, real-valued, even, supported in +`(-lam, lam)`, and normalised so that its square has Fourier transform `1` at `0`. -/ +@[zz_tag "def_admissible"] +structure IsAdmissible (lam : ℝ) (eta : ℝ → ℝ) : Prop where + /-- `eta` is square-integrable. -/ + memLp : MeasureTheory.MemLp eta 2 MeasureTheory.volume + /-- `eta` is even. -/ + even : ∀ x, eta (-x) = eta x + /-- `eta` vanishes off `(-lam, lam)`. -/ + support : ∀ x, lam ≤ |x| → eta x = 0 + /-- `eta` is normalised: its square has Fourier transform `1` at `0`. -/ + fourier_sq_zero : fourierC (eta ^ 2) 0 = 1 + +/-- The kernel of a test function, `K_eta = fourier transform of eta squared`. -/ +@[zz_tag "def_kernel"] +noncomputable def testKernel (eta : ℝ → ℝ) : ℂ → ℂ := fourierC (eta ^ 2) + +/-- The support `Z` with multiplicities `m` is conjugation-invariant: every multiplicity is at +least one, and conjugation permutes `Z` preserving multiplicity. -/ +@[zz_tag "def_conj_invariant"] +structure IsConjInvariant (Z : Finset ℂ) (m : ℂ → ℕ) : Prop where + /-- Every point of the support has multiplicity at least one. -/ + one_le : ∀ z ∈ Z, 1 ≤ m z + /-- Conjugation maps the support to itself. -/ + conj_mem : ∀ z ∈ Z, (starRingEnd ℂ) z ∈ Z + /-- Conjugation preserves multiplicity. -/ + mult_conj : ∀ z ∈ Z, m ((starRingEnd ℂ) z) = m z + +/-- The simple real part of the support: real points of multiplicity one. -/ +@[zz_tag "def_R1"] +noncomputable def simpleRealPart (Z : Finset ℂ) (m : ℂ → ℕ) : Finset ℂ := + Z.filter fun x => x.im = 0 ∧ m x = 1 + +/-- The weight `4 / (4 - z²)` carried by the unconditional pair-correlation formula. -/ +@[zz_tag "def_w"] +noncomputable def pairWeight (z : ℂ) : ℂ := 4 / (4 - z ^ 2) + +/-- The rescaled difference `i(ρ - ρ') log T / (2π)` of two zeros. -/ +@[zz_tag "def_z_rho"] +noncomputable def rescaledDiff (T : ℝ) (ρ ρ' : ℂ) : ℂ := + Complex.I * (ρ - ρ') * ((Real.log T / (2 * Real.pi) : ℝ) : ℂ) + +/-- The weighted sum of `fourierC f` over ordered pairs of non-trivial zeros with imaginary part +in `(0, T]`, each zero counted with multiplicity. -/ +@[zz_tag "def_B_T"] +noncomputable def pairCorrelationSum (f : ℝ → ℝ) (T : ℝ) : ℂ := + ∑ᶠ ρ ∈ nontrivialZeros T, ∑ᶠ ρ' ∈ nontrivialZeros T, + ((zeroMultiplicity ρ * zeroMultiplicity ρ' : ℕ) : ℂ) * + fourierC f (rescaledDiff T ρ ρ') * pairWeight (ρ - ρ') + +/-- The main term `f 0 + 2 ∫₀¹ α f α` of the pair-correlation formula. -/ +@[zz_tag "def_A_functional"] +noncomputable def pairMainTerm (f : ℝ → ℝ) : ℝ := f 0 + 2 * ∫ α in (0:ℝ)..1, α * f α + +/-- A test function admissible in the pair-correlation formula: even, integrable, supported in +`[-1, 1]`, and Lipschitz at the origin. + +The Lipschitz condition is imposed globally rather than only at `0`. That makes this predicate +*stronger*, hence `PairCorrelation` weaker and safer to assume — and the cited lemma still supplies +it. -/ +def IsPairTestFunction (f : ℝ → ℝ) : Prop := + (∀ x, f (-x) = f x) ∧ MeasureTheory.Integrable f ∧ (∀ x, 1 < |x| → f x = 0) ∧ + ∃ C : ℝ, ∀ x, |f x - f 0| ≤ C * |x| + +/-! ### The two external inputs + +The classical analytic results cited rather than proved here: the Riemann--von Mangoldt formula +and the unconditional pair-correlation formula. Each is a `Prop`, carried as a hypothesis, so +every result depending on it names it in its own statement. +-/ + +/-- **Riemann--von Mangoldt** (`lem_rvm`, external input). `N T ∼ (T / 2π) log T`. -/ +@[zz_tag "lem_rvm"] +def RiemannVonMangoldt : Prop := + ∀ ε > 0, ∃ T₀ : ℝ, ∀ T ≥ T₀, + |(zeroCount T : ℝ) / (T / (2 * Real.pi) * Real.log T) - 1| < ε + +/-- **Unconditional pair correlation** (`lem_bgst`, external input). For every admissible test +function the weighted pair-correlation sum is `(T / 2π) log T` times its main term, with an error +`O(1 / √log T)`. Lemma 5 of Baluyot--Goldston--Suriajaya--Turnage-Butterbaugh, *An unconditional +Montgomery theorem for pair correlation of zeros of the Riemann zeta-function*, Acta Arith. 214 +(2024), 357--376. -/ +@[zz_tag "lem_bgst"] +def PairCorrelation : Prop := + ∀ f : ℝ → ℝ, IsPairTestFunction f → + ∃ C : ℝ, 0 < C ∧ ∃ T₀ : ℝ, ∀ T ≥ T₀, + ‖pairCorrelationSum f T / ((T / (2 * Real.pi) * Real.log T : ℝ) : ℂ) - + ((pairMainTerm f : ℝ) : ℂ)‖ ≤ C / Real.sqrt (Real.log T) + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Hilbert/AlphaExpansion.lean b/LeanPool/ZetaZeros/Hilbert/AlphaExpansion.lean new file mode 100644 index 0000000000..1cd672b5c5 --- /dev/null +++ b/LeanPool/ZetaZeros/Hilbert/AlphaExpansion.lean @@ -0,0 +1,2685 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public import LeanPool.ZetaZeros.Hilbert.Basis +public import LeanPool.ZetaZeros.Hilbert.Dimensions +public import LeanPool.ZetaZeros.Hilbert.FIdentity +public import LeanPool.ZetaZeros.Hilbert.InnerRealL2 +public import LeanPool.ZetaZeros.Hilbert.Integrals +public import LeanPool.ZetaZeros.Hilbert.L2 +public import LeanPool.ZetaZeros.Hilbert.Subspaces +public import LeanPool.ZetaZeros.Meta.Attr +public import LeanPool.ZetaZeros.Numeric.MontgomeryTaylor +public import LeanPool.ZetaZeros.Zeta.Basic +public import LeanPool.ZetaZeros.Zeta.OrderConj +public import LeanPool.ZetaZeros.Zeta.Defs + +/-! +# Integrability and factorisation for the Bessel coefficients + +The analytic ingredients `alphaCoeff_eq` needs on top of the algebraic identity `bigF_eq`. + +Integrability is Hölder with `p = q = 2`: each factor is a twisted function, square-integrable by +`memLp_fz`, against a conjugated `L²` element, so the product is `L¹`. That is what licenses +splitting the finite sum under the integral. + +The factorisation of the double integral needs **no** integrability: pulling a constant out of a +Bochner integral is unconditional, so a separable integrand factors by two such pulls rather than +by Fubini. +-/ + +@[expose] public section + +namespace ZetaZeros + +open MeasureTheory + +variable {lam : ℝ} {eta : ℝ → ℝ} + +/-- A conjugated `L²` element is still in `L²`. -/ +theorem memLp_conj_coeFn (ψ : L2Interval lam) : + MemLp (fun u => (starRingEnd ℂ) ((ψ : ℝ → ℂ) u)) 2 + (volume.restrict (Set.Ioo (-lam) lam)) := by + have heq : (fun u => (starRingEnd ℂ) ((ψ : ℝ → ℂ) u)) = star (ψ : ℝ → ℂ) := by + funext u + rfl + rw [heq] + exact (Lp.memLp ψ).star + +/-- A twisted function against a conjugated `L²` element is integrable: Hölder with `p = q = 2`. -/ +theorem integrable_fz_mul_conj (h : IsAdmissible lam eta) (x : ℂ) (ψ : L2Interval lam) : + Integrable (fun u => fz eta x u * (starRingEnd ℂ) ((ψ : ℝ → ℂ) u)) + (volume.restrict (Set.Ioo (-lam) lam)) := + (memLp_fz h x).integrable_mul (memLp_conj_coeFn ψ) + +/-- The even part against a conjugated `L²` element is integrable. -/ +theorem integrable_gz_mul_conj (h : IsAdmissible lam eta) (z : ℂ) (ψ : L2Interval lam) : + Integrable (fun u => gz eta z u * (starRingEnd ℂ) ((ψ : ℝ → ℂ) u)) + (volume.restrict (Set.Ioo (-lam) lam)) := + (memLp_gz h z).integrable_mul (memLp_conj_coeFn ψ) + +/-- The odd part against a conjugated `L²` element is integrable. -/ +theorem integrable_hz_mul_conj (h : IsAdmissible lam eta) (z : ℂ) (ψ : L2Interval lam) : + Integrable (fun u => hz eta z u * (starRingEnd ℂ) ((ψ : ℝ → ℂ) u)) + (volume.restrict (Set.Ioo (-lam) lam)) := + (memLp_hz h z).integrable_mul (memLp_conj_coeFn ψ) + +/-- **The double integral of a finite sum of separable terms.** Splitting the sum under the +integral is what needs the integrability hypothesis; each term then factors as a square with none. + +This is the reusable core of `alphaCoeff_eq`, applied once per family (`fz`, `gz`, `hz`). -/ +theorem integral_double_finsetSum {ι : Type*} (s : Finset ι) (c : ι → ℂ) (Φ : ι → ℝ → ℂ) + (k : ℝ → ℂ) (lam : ℝ) + (hint : ∀ i ∈ s, Integrable (fun u => Φ i u * k u) + (volume.restrict (Set.Ioo (-lam) lam))) : + (∫ u in Set.Ioo (-lam) lam, ∫ v in Set.Ioo (-lam) lam, + (∑ i ∈ s, c i * Φ i u * Φ i v) * (k u * k v)) + = ∑ i ∈ s, c i * (∫ u in Set.Ioo (-lam) lam, Φ i u * k u) ^ 2 := by + have hstep : ∀ u : ℝ, + (∫ v in Set.Ioo (-lam) lam, (∑ i ∈ s, c i * Φ i u * Φ i v) * (k u * k v)) + = ∑ i ∈ s, c i * Φ i u * k u * ∫ v in Set.Ioo (-lam) lam, Φ i v * k v := by + intro u + have hfun : (fun v => (∑ i ∈ s, c i * Φ i u * Φ i v) * (k u * k v)) + = (fun v => ∑ i ∈ s, (c i * Φ i u * k u) * (Φ i v * k v)) := by + funext v + rw [Finset.sum_mul] + exact Finset.sum_congr rfl fun i _ => by ring + rw [hfun, MeasureTheory.integral_finsetSum _ fun i hi => (hint i hi).const_mul _] + exact Finset.sum_congr rfl fun i _ => MeasureTheory.integral_const_mul _ _ + rw [setIntegral_congr_fun measurableSet_Ioo fun u _ => hstep u] + have hfun2 : (fun x => ∑ i ∈ s, c i * Φ i x * k x * ∫ v in Set.Ioo (-lam) lam, Φ i v * k v) + = (fun x => ∑ i ∈ s, + (c i * ∫ v in Set.Ioo (-lam) lam, Φ i v * k v) * (Φ i x * k x)) := by + funext x + exact Finset.sum_congr rfl fun i _ => by ring + rw [hfun2, MeasureTheory.integral_finsetSum _ fun i hi => (hint i hi).const_mul _] + refine Finset.sum_congr rfl fun i _ => ?_ + rw [MeasureTheory.integral_const_mul, sq] + ring + +variable {Z : Finset ℂ} {m : ℂ → ℕ} + +/-- **The integral of `Φ * conj Ψ` is the `L²` inner product `⟪Ψ, Φ⟫`.** -/ +theorem integral_mul_conj_eq_inner (Φ Ψ : L2Interval lam) : + (∫ u in Set.Ioo (-lam) lam, (Φ : ℝ → ℂ) u * (starRingEnd ℂ) ((Ψ : ℝ → ℂ) u)) + = inner ℂ Ψ Φ := by + rw [MeasureTheory.L2.inner_def] + refine integral_congr_ae (.of_forall fun u => ?_) + simp only [RCLike.inner_apply] + +/-- **The Bessel coefficient in terms of one-variable integrals.** + +Stated for an arbitrary family `psi`: the identity is linearity together with `bigF_eq`, so the +source's adapted-orthonormal-basis hypothesis plays no role and is dropped. -/ +@[zz_tag "lem_alpha_expansion"] +theorem alphaCoeff_eq (h : IsAdmissible lam eta) (hZ : IsConjInvariant Z m) + (psi : ℕ → L2Interval lam) (j : ℕ) : + alphaCoeff eta lam Z m psi j + = (∑ x ∈ simpleRealPart Z m ∪ multipleRealPart Z m, + (m x : ℂ) * (∫ u in Set.Ioo (-lam) lam, + fz eta x u * (starRingEnd ℂ) ((psi j : ℝ → ℂ) u)) ^ 2) + + ∑ z ∈ nonRealPart Z, (m z : ℂ) * + ((∫ u in Set.Ioo (-lam) lam, + gz eta z u * (starRingEnd ℂ) ((psi j : ℝ → ℂ) u)) ^ 2 + - (∫ u in Set.Ioo (-lam) lam, + hz eta z u * (starRingEnd ℂ) ((psi j : ℝ → ℂ) u)) ^ 2) := by + classical + set c : ℂ ⊕ ℂ ⊕ ℂ → ℂ := + Sum.elim (fun x => (m x : ℂ)) (Sum.elim (fun z => (m z : ℂ)) fun z => -(m z : ℂ)) with hc + set Phi : ℂ ⊕ ℂ ⊕ ℂ → ℝ → ℂ := + Sum.elim (fun x => fz eta x) (Sum.elim (fun z => gz eta z) fun z => hz eta z) with hPhi + set S : Finset (ℂ ⊕ ℂ ⊕ ℂ) := + (simpleRealPart Z m ∪ multipleRealPart Z m).disjSum + ((nonRealPart Z).disjSum (nonRealPart Z)) with hS + have hint : ∀ i ∈ S, Integrable (fun u => Phi i u * (starRingEnd ℂ) ((psi j : ℝ → ℂ) u)) + (volume.restrict (Set.Ioo (-lam) lam)) := by + rintro (x | z | z) - + · exact integrable_fz_mul_conj h x (psi j) + · exact integrable_gz_mul_conj h z (psi j) + · exact integrable_hz_mul_conj h z (psi j) + have hkernel : ∀ u v : ℝ, + bigF eta Z m u v * (starRingEnd ℂ) ((psi j : ℝ → ℂ) u * (psi j : ℝ → ℂ) v) + = (∑ i ∈ S, c i * Phi i u * Phi i v) + * ((starRingEnd ℂ) ((psi j : ℝ → ℂ) u) + * (starRingEnd ℂ) ((psi j : ℝ → ℂ) v)) := by + intro u v + have hB : ∑ z ∈ nonRealPart Z, + (m z : ℂ) * (gz eta z u * gz eta z v - hz eta z u * hz eta z v) + = (∑ z ∈ nonRealPart Z, (m z : ℂ) * gz eta z u * gz eta z v) + + ∑ z ∈ nonRealPart Z, -(m z : ℂ) * hz eta z u * hz eta z v := by + rw [← Finset.sum_add_distrib] + exact Finset.sum_congr rfl fun z _ => by ring + rw [bigF_eq hZ, map_mul, hB, hS, Finset.sum_disjSum, Finset.sum_disjSum] + simp only [hc, hPhi, Sum.elim_inl, Sum.elim_inr] + calc alphaCoeff eta lam Z m psi j + = ∫ u in Set.Ioo (-lam) lam, ∫ v in Set.Ioo (-lam) lam, + (∑ i ∈ S, c i * Phi i u * Phi i v) + * ((starRingEnd ℂ) ((psi j : ℝ → ℂ) u) + * (starRingEnd ℂ) ((psi j : ℝ → ℂ) v)) := by + simp only [alphaCoeff, alphaOf, hkernel] + _ = ∑ i ∈ S, c i * (∫ u in Set.Ioo (-lam) lam, + Phi i u * (starRingEnd ℂ) ((psi j : ℝ → ℂ) u)) ^ 2 := + integral_double_finsetSum S c Phi + (fun u => (starRingEnd ℂ) ((psi j : ℝ → ℂ) u)) lam hint + _ = _ := by + rw [hS, Finset.sum_disjSum, Finset.sum_disjSum] + simp only [hc, hPhi, Sum.elim_inl, Sum.elim_inr] + rw [← Finset.sum_add_distrib] + refine congrArg (_ + ·) (Finset.sum_congr rfl fun z _ => by ring) + +/-- `alphaCoeff` is `alphaOf` at the indexed member: the family parameter is spurious. -/ +theorem alphaCoeff_eq_alphaOf (psi : ℕ → L2Interval lam) (j : ℕ) : + alphaCoeff eta lam Z m psi j = alphaOf eta lam Z m (psi j) := + rfl + +/-- **The set-integral form of `inner_symmetric_im_eq_zero`.** The pairing of two symmetric +functions over the symmetric interval is real. -/ +theorem integral_symmetric_im_eq_zero {Φ₁ Φ₂ : ℝ → ℂ} (h1 : IsSymmetric Φ₁) (h2 : IsSymmetric Φ₂) + (lam : ℝ) : + (∫ u in Set.Ioo (-lam) lam, Φ₁ u * (starRingEnd ℂ) (Φ₂ u)).im = 0 := by + refine Complex.conj_eq_iff_im.mp ?_ + rw [← integral_conj] + have hpt : ∀ u : ℝ, (starRingEnd ℂ) (Φ₁ u * (starRingEnd ℂ) (Φ₂ u)) + = Φ₁ (-u) * (starRingEnd ℂ) (Φ₂ (-u)) := by + intro u + rw [map_mul, Complex.conj_conj, h1 u] + congr 1 + rw [h2 (-u), neg_neg] + calc ∫ u in Set.Ioo (-lam) lam, (starRingEnd ℂ) (Φ₁ u * (starRingEnd ℂ) (Φ₂ u)) + = ∫ u in Set.Ioo (-lam) lam, Φ₁ (-u) * (starRingEnd ℂ) (Φ₂ (-u)) := + integral_congr_ae (.of_forall hpt) + _ = ∫ u in Set.Ioo (-lam) lam, Φ₁ u * (starRingEnd ℂ) (Φ₂ u) := + integral_comp_neg_Ioo (fun v => Φ₁ v * (starRingEnd ℂ) (Φ₂ v)) lam + +/-- **The Bessel coefficients are real.** -/ +@[zz_tag "lem_alpha_real"] +theorem alphaCoeff_im_eq_zero (h : IsAdmissible lam eta) (hZ : IsConjInvariant Z m) + (psi : ℕ → L2Interval lam) (j : ℕ) (hpsi : IsSymmetric ((psi j : ℝ → ℂ))) : + (alphaCoeff eta lam Z m psi j).im = 0 := by + classical + rw [alphaCoeff_eq h hZ psi j, Complex.add_im, Complex.im_sum, Complex.im_sum] + have hreal : ∀ x ∈ simpleRealPart Z m ∪ multipleRealPart Z m, x.im = 0 := by + intro x hx + rcases Finset.mem_union.mp hx with hx | hx + · exact ((Finset.mem_filter.mp hx).2).1 + · exact ((Finset.mem_filter.mp hx).2).1 + rw [Finset.sum_eq_zero, Finset.sum_eq_zero, add_zero] + · intro z _ + have hg := integral_symmetric_im_eq_zero (isSymmetric_gz h z) hpsi lam + have hh := integral_symmetric_im_eq_zero (isSymmetric_hz h z) hpsi lam + simp [Complex.mul_im, Complex.sub_im, sq, hg, hh] + · intro x hx + have hf := integral_symmetric_im_eq_zero (isSymmetric_fz h (hreal x hx)) hpsi lam + simp [Complex.mul_im, sq, hf] + +/-- Orthogonality to a spanning set extends to the span. -/ +theorem inner_eq_zero_of_mem_span {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℂ E] + {s : Set E} {v w : E} (hv : ∀ y ∈ s, inner ℂ v y = 0) (hw : w ∈ Submodule.span ℂ s) : + inner ℂ v w = 0 := by + induction hw using Submodule.span_induction with + | mem y hy => exact hv y hy + | zero => exact inner_zero_right v + | add x y _ _ hx hy => rw [inner_add_right, hx, hy, add_zero] + | smul a x _ hx => rw [inner_smul_right, hx, mul_zero] + +/-- `fzL2` agrees a.e. with `fz`. -/ +theorem coeFn_fzL2 (h : IsAdmissible lam eta) (z : ℂ) : + (fzL2 h z : ℝ → ℂ) =ᵐ[volume.restrict (Set.Ioo (-lam) lam)] fz eta z := + MemLp.coeFn_toLp (memLp_fz h z) + +/-- `gzL2` agrees a.e. with `gz`. -/ +theorem coeFn_gzL2 (h : IsAdmissible lam eta) (z : ℂ) : + (gzL2 h z : ℝ → ℂ) =ᵐ[volume.restrict (Set.Ioo (-lam) lam)] gz eta z := + MemLp.coeFn_toLp (memLp_gz h z) + +/-- `hzL2` agrees a.e. with `hz`. -/ +theorem coeFn_hzL2 (h : IsAdmissible lam eta) (z : ℂ) : + (hzL2 h z : ℝ → ℂ) =ᵐ[volume.restrict (Set.Ioo (-lam) lam)] hz eta z := + MemLp.coeFn_toLp (memLp_hz h z) + +/-- Pointwise symmetry of a representing function gives a.e. symmetry of the `L²` element. -/ +theorem isSymmetricL2_of_isSymmetric {F : L2Interval lam} {phi : ℝ → ℂ} + (hF : (F : ℝ → ℂ) =ᵐ[volume.restrict (Set.Ioo (-lam) lam)] phi) + (hphi : IsSymmetric phi) : IsSymmetricL2 F := by + filter_upwards [hF, ae_restrict_Ioo_neg hF] with u h1 h2 + rw [h1, h2, hphi u] + +/-- At a real point the twisted function is a symmetric `L²` element. -/ +theorem fzL2_mem_symmetricSubspace (h : IsAdmissible lam eta) {x : ℂ} (hx : x.im = 0) : + fzL2 h x ∈ symmetricSubspace lam := + isSymmetricL2_of_isSymmetric (coeFn_fzL2 h x) (isSymmetric_fz h hx) + +/-- The even part is a symmetric `L²` element. -/ +theorem gzL2_mem_symmetricSubspace (h : IsAdmissible lam eta) (z : ℂ) : + gzL2 h z ∈ symmetricSubspace lam := + isSymmetricL2_of_isSymmetric (coeFn_gzL2 h z) (isSymmetric_gz h z) + +/-- The odd part is a symmetric `L²` element. -/ +theorem hzL2_mem_symmetricSubspace (h : IsAdmissible lam eta) (z : ℂ) : + hzL2 h z ∈ symmetricSubspace lam := + isSymmetricL2_of_isSymmetric (coeFn_hzL2 h z) (isSymmetric_hz h z) + +/-- Inner products of symmetric `L²` elements are real. -/ +theorem inner_symmetricL2_im_eq_zero {F G : L2Interval lam} + (hF : IsSymmetricL2 F) (hG : IsSymmetricL2 G) : + (inner ℂ F G : ℂ).im = 0 := by + refine Complex.conj_eq_iff_im.mp ?_ + rw [MeasureTheory.L2.inner_def, ← integral_conj] + calc ∫ u in Set.Ioo (-lam) lam, + (starRingEnd ℂ) (inner ℂ ((F : ℝ → ℂ) u) ((G : ℝ → ℂ) u)) + = ∫ u in Set.Ioo (-lam) lam, (F : ℝ → ℂ) u * (starRingEnd ℂ) ((G : ℝ → ℂ) u) := by + refine integral_congr_ae (.of_forall fun u => ?_) + simp only [RCLike.inner_apply, map_mul, Complex.conj_conj] + ring + _ = ∫ u in Set.Ioo (-lam) lam, (F : ℝ → ℂ) u * (G : ℝ → ℂ) (-u) := by + refine integral_congr_ae ?_ + filter_upwards [hG] with u hu + rw [hu] + _ = ∫ u in Set.Ioo (-lam) lam, (F : ℝ → ℂ) (-u) * (G : ℝ → ℂ) u := by + have h := integral_comp_neg_Ioo + (fun v => (F : ℝ → ℂ) v * (G : ℝ → ℂ) (-v)) lam + simp only [neg_neg] at h + exact h.symm + _ = ∫ u in Set.Ioo (-lam) lam, inner ℂ ((F : ℝ → ℂ) u) ((G : ℝ → ℂ) u) := by + refine integral_congr_ae ?_ + filter_upwards [hF] with u hu + simp only [RCLike.inner_apply, ← hu] + ring + +/-- The integral of `Φ * conj ψ`, for a function `Φ` agreeing a.e. with an `L²` element, is the +inner product `⟪ψ, Φ⟫`. -/ +theorem integral_mul_conj_eq_inner_of_ae {Φ : ℝ → ℂ} {F : L2Interval lam} + (hF : (F : ℝ → ℂ) =ᵐ[volume.restrict (Set.Ioo (-lam) lam)] Φ) (ψ : L2Interval lam) : + (∫ u in Set.Ioo (-lam) lam, Φ u * (starRingEnd ℂ) ((ψ : ℝ → ℂ) u)) = inner ℂ ψ F := by + rw [← integral_mul_conj_eq_inner F ψ] + refine integral_congr_ae ?_ + filter_upwards [hF] with u hu + rw [hu] + +/-- `alphaCoeff_eq`, restated for a single vector. Definitionally the same statement. -/ +theorem alphaOf_eq (h : IsAdmissible lam eta) (hZ : IsConjInvariant Z m) (φ : L2Interval lam) : + alphaOf eta lam Z m φ + = (∑ x ∈ simpleRealPart Z m ∪ multipleRealPart Z m, + (m x : ℂ) * (∫ u in Set.Ioo (-lam) lam, + fz eta x u * (starRingEnd ℂ) ((φ : ℝ → ℂ) u)) ^ 2) + + ∑ z ∈ nonRealPart Z, (m z : ℂ) * + ((∫ u in Set.Ioo (-lam) lam, + gz eta z u * (starRingEnd ℂ) ((φ : ℝ → ℂ) u)) ^ 2 + - (∫ u in Set.Ioo (-lam) lam, + hz eta z u * (starRingEnd ℂ) ((φ : ℝ → ℂ) u)) ^ 2) := + alphaCoeff_eq h hZ (fun _ => φ) 0 + +/-- A Bessel coefficient against a symmetric `L²` element is real. -/ +theorem alphaOf_im_eq_zero_l2 (h : IsAdmissible lam eta) + (hZ : IsConjInvariant Z m) {φ : L2Interval lam} + (hsym : IsSymmetricL2 φ) : + (alphaOf eta lam Z m φ).im = 0 := by + classical + rw [alphaOf_eq h hZ, Complex.add_im, Complex.im_sum, Complex.im_sum] + rw [Finset.sum_eq_zero, Finset.sum_eq_zero, add_zero] + · intro z _ + have hg : (inner ℂ φ (gzL2 h z) : ℂ).im = 0 := + inner_symmetricL2_im_eq_zero hsym (gzL2_mem_symmetricSubspace h z) + have hh : (inner ℂ φ (hzL2 h z) : ℂ).im = 0 := + inner_symmetricL2_im_eq_zero hsym (hzL2_mem_symmetricSubspace h z) + rw [integral_mul_conj_eq_inner_of_ae (coeFn_gzL2 h z), + integral_mul_conj_eq_inner_of_ae (coeFn_hzL2 h z)] + simp [Complex.mul_im, Complex.sub_im, sq, hg, hh] + · intro x hx + have hxre : x.im = 0 := by + rcases Finset.mem_union.mp hx with hx | hx + · exact ((Finset.mem_filter.mp hx).2).1 + · exact ((Finset.mem_filter.mp hx).2).1 + have hf : (inner ℂ φ (fzL2 h x) : ℂ).im = 0 := + inner_symmetricL2_im_eq_zero hsym (fzL2_mem_symmetricSubspace h hxre) + rw [integral_mul_conj_eq_inner_of_ae (coeFn_fzL2 h x)] + simp [Complex.mul_im, sq, hf] + +/-- **Past `dim V` the Bessel coefficient is non-positive.** -/ +@[zz_tag "lem_alpha_third_nonpos"] +theorem alphaOf_re_nonpos (h : IsAdmissible lam eta) (hZ : IsConjInvariant Z m) + {psi : Fin (Module.finrank ℂ (subspaceW h Z m)) → L2Interval lam} + (hb : IsAdaptedBasis h Z m psi) {j : Fin (Module.finrank ℂ (subspaceW h Z m))} + (hsym : IsSymmetricL2 (psi j)) + (hj : Module.finrank ℂ (subspaceV h Z m) ≤ (j : ℕ)) : + (alphaOf eta lam Z m (psi j)).re ≤ 0 := by + classical + -- past `dim V`, the basis vector is orthogonal to every element of `V` + have hV : ∀ w ∈ subspaceV h Z m, inner ℂ (psi j) w = 0 := by + intro w hw + rw [← hb.span_V] at hw + refine inner_eq_zero_of_mem_span ?_ hw + rintro y ⟨i, hi, rfl⟩ + have hlt : (i : ℕ) < Module.finrank ℂ (subspaceV h Z m) := hi + refine hb.orthonormal.2 ?_ + intro hEq + rw [hEq] at hj + omega + -- so the `fz` and `gz` pairings vanish + have hf : ∀ x ∈ simpleRealPart Z m ∪ multipleRealPart Z m, + (∫ u in Set.Ioo (-lam) lam, fz eta x u * (starRingEnd ℂ) ((psi j : ℝ → ℂ) u)) = 0 := by + intro x hx + rw [integral_mul_conj_eq_inner_of_ae (coeFn_fzL2 h x)] + exact hV _ (Submodule.subset_span (Set.mem_union_left _ ⟨x, by simpa using hx, rfl⟩)) + have hg : ∀ z ∈ nonRealPart Z, + (∫ u in Set.Ioo (-lam) lam, gz eta z u * (starRingEnd ℂ) ((psi j : ℝ → ℂ) u)) = 0 := by + intro z hz + rw [integral_mul_conj_eq_inner_of_ae (coeFn_gzL2 h z)] + exact hV _ (Submodule.subset_span (Set.mem_union_right _ ⟨z, by simpa using hz, rfl⟩)) + have hA : (∑ x ∈ simpleRealPart Z m ∪ multipleRealPart Z m, + (m x : ℂ) * (∫ u in Set.Ioo (-lam) lam, + fz eta x u * (starRingEnd ℂ) ((psi j : ℝ → ℂ) u)) ^ 2) = 0 := by + refine Finset.sum_eq_zero fun x hx => ?_ + rw [hf x hx] + ring + rw [alphaOf_eq h hZ, hA, zero_add, Complex.re_sum] + -- `hzmem`, not `hz`: the odd part is called `hz`, and a hypothesis of that name shadows it + refine Finset.sum_nonpos fun z hzmem => ?_ + rw [hg z hzmem] + -- the surviving pairing is real, so the term is a real cast and its sign is visible + set I := ∫ u in Set.Ioo (-lam) lam, + hz eta z u * (starRingEnd ℂ) ((psi j : ℝ → ℂ) u) with hIdef + have hIre : I = (I.re : ℂ) := + Complex.ext rfl (by + simp only [hIdef, integral_mul_conj_eq_inner_of_ae (coeFn_hzL2 h z)] + exact inner_symmetricL2_im_eq_zero hsym (hzL2_mem_symmetricSubspace h z)) + have hterm : (m z : ℂ) * ((0 : ℂ) ^ 2 - (I.re : ℂ) ^ 2) + = ((-((m z : ℝ) * I.re ^ 2) : ℝ) : ℂ) := by + push_cast + ring + rw [hIre, hterm, Complex.ofReal_re] + have hnn : (0 : ℝ) ≤ (m z : ℝ) * I.re ^ 2 := by positivity + linarith + +/-- The conjugate of a twisted function is still square-integrable. -/ +theorem memLp_conj_fz (h : IsAdmissible lam eta) (w : ℂ) : + MemLp (fun u => (starRingEnd ℂ) (fz eta w u)) 2 + (volume.restrict (Set.Ioo (-lam) lam)) := by + have heq : (fun u => (starRingEnd ℂ) (fz eta w u)) = star (fz eta w) := by + funext u + rfl + rw [heq] + exact (memLp_fz h w).star + +/-- **The even and odd parts differ by exactly one in squared `L²` norm** (`lem_g_h_norm_diff`). + +The kernel at `0` is `1`, and by the Gram identity it is the pairing of `f_z` against +`f_{conj z}`. Pointwise the real part of that pairing is already `‖g_z‖² - ‖h_z‖²`, because the +cross term is `g conj h + h conj g = 2 re (g conj h)`, which is real, so `I` times it contributes +nothing. Taking real parts once — rather than expanding the product into four integrals and +discharging integrability for each — is what keeps this short. -/ +@[zz_tag "lem_g_h_norm_diff"] +theorem integral_norm_gz_sq_sub_integral_norm_hz_sq (h : IsAdmissible lam eta) (z : ℂ) : + (∫ u in Set.Ioo (-lam) lam, ‖gz eta z u‖ ^ 2) + - (∫ u in Set.Ioo (-lam) lam, ‖hz eta z u‖ ^ 2) = 1 := by + have hker : (∫ u in Set.Ioo (-lam) lam, + fz eta z u * (starRingEnd ℂ) (fz eta ((starRingEnd ℂ) z) u)) = 1 := by + have hfac := testKernel_sub_conj h z ((starRingEnd ℂ) z) + rw [Complex.conj_conj, sub_self] at hfac + rw [← hfac, testKernel] + exact h.fourier_sq_zero + have hint : Integrable + (fun u => fz eta z u * (starRingEnd ℂ) (fz eta ((starRingEnd ℂ) z) u)) + (volume.restrict (Set.Ioo (-lam) lam)) := + (memLp_fz h z).integrable_mul (memLp_conj_fz h ((starRingEnd ℂ) z)) + have hnorm : ∀ w : ℂ, ‖w‖ ^ 2 = w.re ^ 2 + w.im ^ 2 := by + intro w + rw [Complex.sq_norm, Complex.normSq_apply] + ring + have hpt : ∀ u : ℝ, ‖gz eta z u‖ ^ 2 - ‖hz eta z u‖ ^ 2 + = (fz eta z u * (starRingEnd ℂ) (fz eta ((starRingEnd ℂ) z) u)).re := by + intro u + rw [fz_eq_gz_add_I_mul_hz eta z u, fz_eq_gz_add_I_mul_hz eta ((starRingEnd ℂ) z) u] + simp only [gz_conj, hz_conj, hnorm, Complex.mul_re, Complex.mul_im, Complex.add_re, + Complex.add_im, Complex.conj_re, Complex.conj_im, Complex.I_re, Complex.I_im, + Complex.neg_re, Complex.neg_im] + ring + have hgi : Integrable (fun u => ‖gz eta z u‖ ^ 2) + (volume.restrict (Set.Ioo (-lam) lam)) := + (memLp_two_iff_integrable_sq_norm (memLp_gz h z).aestronglyMeasurable).mp (memLp_gz h z) + have hhi : Integrable (fun u => ‖hz eta z u‖ ^ 2) + (volume.restrict (Set.Ioo (-lam) lam)) := + (memLp_two_iff_integrable_sq_norm (memLp_hz h z).aestronglyMeasurable).mp (memLp_hz h z) + calc (∫ u in Set.Ioo (-lam) lam, ‖gz eta z u‖ ^ 2) + - (∫ u in Set.Ioo (-lam) lam, ‖hz eta z u‖ ^ 2) + = ∫ u in Set.Ioo (-lam) lam, (‖gz eta z u‖ ^ 2 - ‖hz eta z u‖ ^ 2) := + (integral_sub hgi hhi).symm + _ = ∫ u in Set.Ioo (-lam) lam, + (fz eta z u * (starRingEnd ℂ) (fz eta ((starRingEnd ℂ) z) u)).re := + integral_congr_ae (.of_forall hpt) + _ = (∫ u in Set.Ioo (-lam) lam, + fz eta z u * (starRingEnd ℂ) (fz eta ((starRingEnd ℂ) z) u)).re := + integral_re hint + _ = 1 := by rw [hker, Complex.one_re] + +/-- Conjugating the index leaves the even part unchanged as an `L²` element. -/ +theorem gzL2_conj (h : IsAdmissible lam eta) (z : ℂ) : + gzL2 h ((starRingEnd ℂ) z) = gzL2 h z := by + simp only [gzL2, gz_conj] + +/-- Membership in the non-real support, unfolded. -/ +theorem mem_nonRealPart {z : ℂ} : z ∈ nonRealPart Z ↔ z ∈ Z ∧ z.im ≠ 0 := + Finset.mem_filter + +/-- Conjugation is injective on the complex numbers. -/ +theorem conj_injective : Function.Injective (starRingEnd ℂ) := by + intro a b hab + have h := congrArg (starRingEnd ℂ) hab + simpa [Complex.conj_conj] using h + +/-- Conjugation maps the non-real support to itself. -/ +theorem conj_mem_nonRealPart (hZ : IsConjInvariant Z m) {z : ℂ} + (hz : z ∈ nonRealPart Z) : (starRingEnd ℂ) z ∈ nonRealPart Z := by + obtain ⟨hzZ, hzim⟩ := mem_nonRealPart.mp hz + refine mem_nonRealPart.mpr ⟨hZ.conj_mem z hzZ, ?_⟩ + rw [Complex.conj_im] + simpa using hzim + +/-- The lower half of the non-real support is exactly the conjugate image of the upper half. +Conjugation has no fixed point there, since every point has non-zero imaginary part. -/ +theorem filter_not_pos_eq_image_filter_pos (hZ : IsConjInvariant Z m) : + ((nonRealPart Z).filter fun z => ¬ 0 < z.im) + = ((nonRealPart Z).filter fun z => 0 < z.im).image (starRingEnd ℂ) := by + classical + ext w + simp only [Finset.mem_image] + constructor + · intro hw + obtain ⟨hwT, hwle⟩ := Finset.mem_filter.mp hw + have hwim : w.im ≠ 0 := (mem_nonRealPart.mp hwT).2 + refine ⟨(starRingEnd ℂ) w, Finset.mem_filter.mpr + ⟨conj_mem_nonRealPart hZ hwT, ?_⟩, Complex.conj_conj w⟩ + rw [Complex.conj_im] + have hneg : w.im < 0 := lt_of_le_of_ne (not_lt.mp hwle) hwim + linarith + · rintro ⟨z, hz, rfl⟩ + obtain ⟨hzT, hzpos⟩ := Finset.mem_filter.mp hz + refine Finset.mem_filter.mpr ⟨conj_mem_nonRealPart hZ hzT, ?_⟩ + rw [Complex.conj_im, not_lt] + linarith + +/-- The non-real support has exactly twice as many points as its upper half. -/ +theorem card_nonRealPart_eq_two_mul (hZ : IsConjInvariant Z m) : + (nonRealPart Z).card + = 2 * ((nonRealPart Z).filter fun z => 0 < z.im).card := by + classical + have hsplit := Finset.card_filter_add_card_filter_not + (s := nonRealPart Z) (p := fun z : ℂ => 0 < z.im) + rw [filter_not_pos_eq_image_filter_pos hZ, + Finset.card_image_of_injective _ conj_injective] at hsplit + omega + +/-- **The dimension of `U`** (`lem_dim_U`). It is spanned by one vector per multiple real point +together with the even parts at the non-real points, and conjugation identifies those in pairs, so +they contribute only half of `|S|`. The half is exact rather than a floor, because `|S|` is even. -/ +@[zz_tag "lem_dim_U"] +theorem finrank_subspaceU_le (h : IsAdmissible lam eta) (hZ : IsConjInvariant Z m) : + Module.finrank ℂ (subspaceU h Z m) + ≤ (multipleRealPart Z m).card + (nonRealPart Z).card / 2 := by + classical + set P := (nonRealPart Z).filter fun z => 0 < z.im with hP + have hTsplit : nonRealPart Z = P ∪ P.image (starRingEnd ℂ) := by + rw [hP, ← filter_not_pos_eq_image_filter_pos hZ, Finset.filter_union_filter_not_eq] + have himg : gzL2 h '' (nonRealPart Z : Set ℂ) = gzL2 h '' (P : Set ℂ) := by + rw [hTsplit, Finset.coe_union, Set.image_union, Finset.coe_image, Set.image_image] + simp only [gzL2_conj] + rw [Set.union_self] + have hsup : subspaceU h Z m + = Submodule.span ℂ (fzL2 h '' (multipleRealPart Z m : Set ℂ)) + ⊔ Submodule.span ℂ (gzL2 h '' (P : Set ℂ)) := by + rw [subspaceU, ← Submodule.span_union, himg] + have : FiniteDimensional ℂ + (Submodule.span ℂ (fzL2 h '' (multipleRealPart Z m : Set ℂ))) := + FiniteDimensional.span_of_finite ℂ ((Finset.finite_toSet _).image _) + have : FiniteDimensional ℂ (Submodule.span ℂ (gzL2 h '' (P : Set ℂ))) := + FiniteDimensional.span_of_finite ℂ ((Finset.finite_toSet _).image _) + have hle : Module.finrank ℂ (subspaceU h Z m) + ≤ Module.finrank ℂ (Submodule.span ℂ (fzL2 h '' (multipleRealPart Z m : Set ℂ))) + + Module.finrank ℂ (Submodule.span ℂ (gzL2 h '' (P : Set ℂ))) := by + rw [hsup] + exact finrank_sup_le _ _ + have hf : Module.finrank ℂ + (Submodule.span ℂ (fzL2 h '' (multipleRealPart Z m : Set ℂ))) + ≤ (multipleRealPart Z m).card := by + rw [show fzL2 h '' (multipleRealPart Z m : Set ℂ) + = (((multipleRealPart Z m).image (fzL2 h) : Finset (L2Interval lam)) : Set _) by + rw [Finset.coe_image]] + exact le_trans (finrank_span_finset_le_card _) Finset.card_image_le + have hg : Module.finrank ℂ (Submodule.span ℂ (gzL2 h '' (P : Set ℂ))) ≤ P.card := by + rw [show gzL2 h '' (P : Set ℂ) + = ((P.image (gzL2 h) : Finset (L2Interval lam)) : Set _) by rw [Finset.coe_image]] + exact le_trans (finrank_span_finset_le_card _) Finset.card_image_le + have hhalf := card_nonRealPart_eq_two_mul (m := m) hZ + -- stated after `set`, so it still mentions the filter; fold it to `P` or omega + -- sees two unrelated quantities + rw [← hP] at hhalf + omega + +/-! +### Alternating series, and the three external inputs +-/ + +/-- **Alternating series bracketing** (`lem_alt_bracket`). The partial sums of an alternating +series with antitone terms bracket its limit: even-length sums from below, odd-length from above. + +Generalised off the source, which also assumes the terms non-negative and null: neither is needed +once the limit is a hypothesis, and Mathlib's two one-sided bounds ask only for antitonicity. -/ +@[zz_tag "lem_alt_bracket"] +theorem alternating_series_bracket {a : ℕ → ℝ} (ha : Antitone a) {L : ℝ} + (hL : Filter.Tendsto (fun n => ∑ i ∈ Finset.range n, (-1 : ℝ) ^ i * a i) + Filter.atTop (nhds L)) (n : ℕ) : + (∑ i ∈ Finset.range (2 * n), (-1 : ℝ) ^ i * a i) ≤ L ∧ + L ≤ ∑ i ∈ Finset.range (2 * n + 1), (-1 : ℝ) ^ i * a i := + ⟨ha.alternating_series_le_tendsto hL n, ha.tendsto_le_alternating_series hL n⟩ + +/-! +### Bessel input for the second-range estimate +-/ + +/-- The squared `L²` norm as an integral of squared pointwise norms. -/ +theorem norm_sq_eq_integral (F : L2Interval lam) : + ‖F‖ ^ 2 = ∫ u in Set.Ioo (-lam) lam, ‖(F : ℝ → ℂ) u‖ ^ 2 := by + have hL : (∫ u in Set.Ioo (-lam) lam, + (F : ℝ → ℂ) u * (starRingEnd ℂ) ((F : ℝ → ℂ) u)) + = ((∫ u in Set.Ioo (-lam) lam, ‖(F : ℝ → ℂ) u‖ ^ 2 : ℝ) : ℂ) := by + rw [← integral_complex_ofReal] + refine integral_congr_ae (.of_forall fun u => ?_) + simp only [Complex.mul_conj, ← Complex.sq_norm] + rw [integral_mul_conj_eq_inner F F, inner_self_eq_norm_sq_to_K] at hL + -- hL carries the cast INSIDE the square; the goal wants it outside + have hcast : ((‖F‖ ^ 2 : ℝ) : ℂ) + = ((∫ u in Set.Ioo (-lam) lam, ‖(F : ℝ → ℂ) u‖ ^ 2 : ℝ) : ℂ) := by + push_cast + exact hL + exact Complex.ofReal_inj.mp hcast + +/-- At a real point the twisted function is a unit vector of `L²`. -/ +theorem norm_fzL2_sq_eq_one (h : IsAdmissible lam eta) {x : ℂ} (hx : x.im = 0) : + ‖fzL2 h x‖ ^ 2 = 1 := by + rw [norm_sq_eq_integral] + rw [show (∫ u in Set.Ioo (-lam) lam, ‖((fzL2 h x : L2Interval lam) : ℝ → ℂ) u‖ ^ 2) + = ∫ u in Set.Ioo (-lam) lam, ‖fz eta x u‖ ^ 2 from + integral_congr_ae (by filter_upwards [coeFn_fzL2 h x] with u hu; rw [hu])] + exact integral_norm_fz_sq h hx + +/-- **Orthogonality to an initial segment's span.** If the first `d` members of an orthonormal +family span `K`, then every later member is orthogonal to all of `K`. + +Factored out because both range estimates need it — for `U` and for `V` — with only `d` changing. -/ +theorem inner_eq_zero_of_span_initial {N : ℕ} {psi : Fin N → L2Interval lam} + (horth : Orthonormal ℂ psi) {d : ℕ} {K : Submodule ℂ (L2Interval lam)} + (hspan : Submodule.span ℂ (psi '' {i | (i : ℕ) < d}) = K) + {j : Fin N} (hj : d ≤ (j : ℕ)) : + ∀ w ∈ K, inner ℂ (psi j) w = 0 := by + intro w hw + rw [← hspan] at hw + refine inner_eq_zero_of_mem_span ?_ hw + rintro y ⟨i, hi, rfl⟩ + have hlt : (i : ℕ) < d := hi + refine horth.2 ?_ + intro hEq + rw [hEq] at hj + omega + +/-- `R₁` and `R₂` are disjoint: multiplicity exactly one versus at least two. -/ +theorem disjoint_simpleRealPart_multipleRealPart : + Disjoint (simpleRealPart Z m) (multipleRealPart Z m) := by + classical + refine Finset.disjoint_left.mpr fun x hx1 hx2 => ?_ + have h1 : m x = 1 := ((Finset.mem_filter.mp hx1).2).2 + have h2 : 2 ≤ m x := ((Finset.mem_filter.mp hx2).2).2 + omega + +/-- **The second-range estimate** (`lem_alpha_second_upper`). Summed over `dim U < j ≤ dim V`, the +Bessel coefficients are bounded by the number of simple real points. + +Past `dim U` the basis vector is orthogonal to `U`, so the `R₂` and `S` pairings drop out and only +`R₁` survives — with weight one, since that is what `R₁` means. Discarding the subtracted `h_z` sum +(non-negative) and applying Bessel to each `f_x`, which is a unit vector at a real point, gives one +per point of `R₁`. -/ +@[zz_tag "lem_alpha_second_upper"] +theorem sum_alphaOf_re_le_card_simpleRealPart (h : IsAdmissible lam eta) + (hZ : IsConjInvariant Z m) + {psi : Fin (Module.finrank ℂ (subspaceW h Z m)) → L2Interval lam} + (hb : IsAdaptedBasis h Z m psi) (hsym : ∀ j, IsSymmetricL2 (psi j)) : + ∑ j ∈ Finset.univ.filter (fun j : Fin (Module.finrank ℂ (subspaceW h Z m)) => + Module.finrank ℂ (subspaceU h Z m) ≤ (j : ℕ) ∧ + (j : ℕ) < Module.finrank ℂ (subspaceV h Z m)), + (alphaOf eta lam Z m (psi j)).re + ≤ (simpleRealPart Z m).card := by + classical + set s := Finset.univ.filter (fun j : Fin (Module.finrank ℂ (subspaceW h Z m)) => + Module.finrank ℂ (subspaceU h Z m) ≤ (j : ℕ) ∧ + (j : ℕ) < Module.finrank ℂ (subspaceV h Z m)) with hs + have hstep : ∀ j : Fin (Module.finrank ℂ (subspaceW h Z m)), + Module.finrank ℂ (subspaceU h Z m) ≤ (j : ℕ) → + (alphaOf eta lam Z m (psi j)).re + ≤ ∑ x ∈ simpleRealPart Z m, ‖inner ℂ (psi j) (fzL2 h x)‖ ^ 2 := by + intro j hj + have hU := inner_eq_zero_of_span_initial hb.orthonormal hb.span_U hj + have hf2 : ∀ x ∈ multipleRealPart Z m, + (∫ u in Set.Ioo (-lam) lam, + fz eta x u * (starRingEnd ℂ) ((psi j : ℝ → ℂ) u)) = 0 := by + intro x hx + rw [integral_mul_conj_eq_inner_of_ae (coeFn_fzL2 h x)] + exact hU _ (Submodule.subset_span (Set.mem_union_left _ ⟨x, by simpa using hx, rfl⟩)) + have hgz : ∀ z ∈ nonRealPart Z, + (∫ u in Set.Ioo (-lam) lam, + gz eta z u * (starRingEnd ℂ) ((psi j : ℝ → ℂ) u)) = 0 := by + intro z hz + rw [integral_mul_conj_eq_inner_of_ae (coeFn_gzL2 h z)] + exact hU _ (Submodule.subset_span (Set.mem_union_right _ ⟨z, by simpa using hz, rfl⟩)) + have hR1 : ∀ x ∈ simpleRealPart Z m, + ((m x : ℂ) * (∫ u in Set.Ioo (-lam) lam, + fz eta x u * (starRingEnd ℂ) ((psi j : ℝ → ℂ) u)) ^ 2).re + = ‖inner ℂ (psi j) (fzL2 h x)‖ ^ 2 := by + intro x hx + have hm : m x = 1 := ((Finset.mem_filter.mp hx).2).2 + have hxre : x.im = 0 := ((Finset.mem_filter.mp hx).2).1 + have hbridge := integral_mul_conj_eq_inner_of_ae (coeFn_fzL2 h x) (psi j) + have hIim : (inner ℂ (psi j) (fzL2 h x) : ℂ).im = 0 := by + exact inner_symmetricL2_im_eq_zero (hsym j) (fzL2_mem_symmetricSubspace h hxre) + -- rewrite the norm BEFORE `sq` fires, or `Complex.sq_norm` no longer matches + rw [hm, hbridge, Complex.sq_norm, Complex.normSq_apply] + simp only [Nat.cast_one, one_mul, sq, Complex.mul_re, hIim] + ring + have hR2 : ∑ x ∈ multipleRealPart Z m, + ((m x : ℂ) * (∫ u in Set.Ioo (-lam) lam, + fz eta x u * (starRingEnd ℂ) ((psi j : ℝ → ℂ) u)) ^ 2).re = 0 := by + refine Finset.sum_eq_zero fun x hx => ?_ + rw [hf2 x hx] + simp + have hSneg : ∑ z ∈ nonRealPart Z, + ((m z : ℂ) * ((∫ u in Set.Ioo (-lam) lam, + gz eta z u * (starRingEnd ℂ) ((psi j : ℝ → ℂ) u)) ^ 2 + - (∫ u in Set.Ioo (-lam) lam, + hz eta z u * (starRingEnd ℂ) ((psi j : ℝ → ℂ) u)) ^ 2)).re ≤ 0 := by + refine Finset.sum_nonpos fun z hzmem => ?_ + rw [hgz z hzmem] + set I := ∫ u in Set.Ioo (-lam) lam, + hz eta z u * (starRingEnd ℂ) ((psi j : ℝ → ℂ) u) with hIdef + have hIre : I = (I.re : ℂ) := + Complex.ext rfl (by + simp only [hIdef, integral_mul_conj_eq_inner_of_ae (coeFn_hzL2 h z)] + exact inner_symmetricL2_im_eq_zero (hsym j) (hzL2_mem_symmetricSubspace h z)) + have hterm : (m z : ℂ) * ((0 : ℂ) ^ 2 - (I.re : ℂ) ^ 2) + = ((-((m z : ℝ) * I.re ^ 2) : ℝ) : ℂ) := by + push_cast + ring + rw [hIre, hterm, Complex.ofReal_re] + have hnn : (0 : ℝ) ≤ (m z : ℝ) * I.re ^ 2 := by positivity + linarith + rw [alphaOf_eq h hZ, Complex.add_re, Complex.re_sum, Complex.re_sum, + Finset.sum_union disjoint_simpleRealPart_multipleRealPart, + Finset.sum_congr rfl hR1] + linarith + calc ∑ j ∈ s, (alphaOf eta lam Z m (psi j)).re + ≤ ∑ j ∈ s, ∑ x ∈ simpleRealPart Z m, ‖inner ℂ (psi j) (fzL2 h x)‖ ^ 2 := + Finset.sum_le_sum fun j hj => hstep j ((Finset.mem_filter.mp hj).2).1 + _ = ∑ x ∈ simpleRealPart Z m, ∑ j ∈ s, ‖inner ℂ (psi j) (fzL2 h x)‖ ^ 2 := + Finset.sum_comm + _ ≤ ∑ _x ∈ simpleRealPart Z m, (1 : ℝ) := by + refine Finset.sum_le_sum fun x hx => ?_ + have hxre : x.im = 0 := ((Finset.mem_filter.mp hx).2).1 + have hbessel := hb.orthonormal.sum_inner_products_le (s := s) (fzL2 h x) + rw [norm_fzL2_sq_eq_one h hxre] at hbessel + exact hbessel + _ = (simpleRealPart Z m).card := by simp + +/-! +### The cutoff normalising constant +-/ + +/-- The extremal test function is non-negative everywhere: positive on `[-1/2, 1/2]` and zero +off it. -/ +theorem extremalTest_nonneg (x : ℝ) : 0 ≤ extremalTest x := by + rcases le_or_gt |x| (1 / 2) with hx | hx + · exact (extremalTest_pos hx).le + · have hz : extremalTest x = 0 := by rw [extremalTest, ite_eq_right (not_le.mpr hx)] + exact hz.ge + +/-- **The normalising constant is positive.** + +The integrand `ψ²f₀` is non-negative, and on `|x| ≤ 1/2 - δ` it equals `f₀ > 0`, a set of positive +measure. Note it is NOT positive throughout `(-1/2, 1/2)`: a cutoff may vanish between `1/2 - δ` +and `1/2`, so the argument has to go through the support rather than through positivity on the +whole interval. + +`0 < δ` is genuinely needed, not decoration: with `δ` negative the shrunken interval `|x| ≤ 1/2 - δ` +would be LARGER than `[-1/2, 1/2]`, and `f₀` is only positive on the latter. -/ +@[zz_tag "lem_normaliser_pos"] +theorem cutoffNormaliser_pos {delta : ℝ} (hd : 0 < delta) (hd4 : delta < 1 / 4) {psi : ℝ → ℝ} + (hpsi : IsCutoff delta psi) : 0 < cutoffNormaliser psi := by + have hzero : ∀ x ∉ Set.Icc (-(1/2) : ℝ) (1/2), psi x ^ 2 * extremalTest x = 0 := by + intro x hx + have h : 1 / 2 ≤ |x| := by + by_contra hlt + exact hx (Set.mem_Icc.mpr (abs_le.mp (le_of_lt (not_le.mp hlt)))) + rw [hpsi.support x h] + ring + have hcont : ContinuousOn (fun x => psi x ^ 2 * extremalTest x) + (Set.Icc (-(1/2) : ℝ) (1/2)) := by + have hG : Continuous fun x : ℝ => psi x ^ 2 * + (Real.cos (Real.sqrt 2 * x) / (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2))) := + (hpsi.smooth.continuous.pow 2).mul + ((Real.continuous_cos.comp (continuous_const.mul continuous_id)).div_const _) + refine hG.continuousOn.congr ?_ + intro x hx + have hx' : |x| ≤ 1 / 2 := abs_le.mpr (Set.mem_Icc.mp hx) + change psi x ^ 2 * extremalTest x + = psi x ^ 2 * (Real.cos (Real.sqrt 2 * x) / (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2))) + rw [extremalTest, ite_eq_left hx'] + have hint : MeasureTheory.IntegrableOn (fun x => psi x ^ 2 * extremalTest x) + (Set.Icc (-(1/2) : ℝ) (1/2)) := hcont.integrableOn_Icc + have hnonneg : (0 : ℝ → ℝ) + ≤ᵐ[MeasureTheory.volume.restrict (Set.Icc (-(1/2) : ℝ) (1/2))] + fun x => psi x ^ 2 * extremalTest x := + Filter.Eventually.of_forall fun x => mul_nonneg (sq_nonneg _) (extremalTest_nonneg x) + rw [cutoffNormaliser, ← MeasureTheory.setIntegral_eq_integral_of_forall_compl_eq_zero hzero, + MeasureTheory.setIntegral_pos_iff_support_of_nonneg_ae hnonneg hint] + have hhalf : (0 : ℝ) < 1 / 2 - delta := by linarith + have hsub : Set.Ioo (-(1/2 - delta)) (1/2 - delta) + ⊆ Function.support (fun x => psi x ^ 2 * extremalTest x) + ∩ Set.Icc (-(1/2) : ℝ) (1/2) := by + intro x hx + have habs : |x| ≤ 1 / 2 - delta := abs_le.mpr ⟨hx.1.le, hx.2.le⟩ + have hx12 : |x| ≤ 1 / 2 := by linarith + refine ⟨?_, Set.mem_Icc.mpr (abs_le.mp hx12)⟩ + have h1 : psi x = 1 := hpsi.eq_one x habs + have h2 : 0 < extremalTest x := extremalTest_pos hx12 + simp only [Function.mem_support, ne_eq, h1, one_pow, one_mul] + exact ne_of_gt h2 + have hvol : 0 < MeasureTheory.volume (Set.Ioo (-(1/2 - delta)) (1/2 - delta)) := by + rw [Real.volume_Ioo, ENNReal.ofReal_pos] + linarith + exact lt_of_lt_of_le hvol (MeasureTheory.measure_mono hsub) + +/-- **Parseval at elements of the span**, for an arbitrary finite index type. + +Mathlib has Bessel for an arbitrary element (`Orthonormal.sum_inner_products_le`) and the equality +only through `OrthonormalBasis`, which wants the family to span the WHOLE space. Here the family +spans a proper submodule, so the statement is proved from the expansion directly: the inner product +against a basis vector picks out that coefficient, and the squared norm is the sum of the squared +coefficients. -/ +theorem sum_sq_norm_inner_eq_norm_sq_of_span {iota : Type*} [Fintype iota] + {psi : iota → L2Interval lam} + (horth : Orthonormal ℂ psi) {x : L2Interval lam} + (hx : x ∈ Submodule.span ℂ (Set.range psi)) : + ∑ j, ‖inner ℂ (psi j) x‖ ^ 2 = ‖x‖ ^ 2 := by + classical + obtain ⟨c, hc⟩ := (Submodule.mem_span_range_iff_exists_fun ℂ).mp hx + subst hc + -- the pairing against a family member picks out that coefficient + simp only [horth.inner_right_fintype c] + have hself : (inner ℂ (∑ j, c j • psi j) (∑ j, c j • psi j) : ℂ) + = ((∑ j, ‖c j‖ ^ 2 : ℝ) : ℂ) := by + rw [horth.inner_sum c c Finset.univ] + push_cast + exact Finset.sum_congr rfl fun j _ => by + rw [mul_comm, Complex.mul_conj, Complex.normSq_eq_norm_sq] + push_cast + ring + -- take REAL PARTS rather than pulling casts: the coercion in `inner_self_eq_norm_sq_to_K` is + -- RCLike's, not `Complex.ofReal`, so `ofReal_pow` cannot match it + have hre : RCLike.re (inner ℂ (∑ j, c j • psi j) (∑ j, c j • psi j)) + = ‖∑ j, c j • psi j‖ ^ 2 := inner_self_eq_norm_sq _ + rw [hself] at hre + -- simp distributes the cast into the sum, putting it inside each power; fold it back + simpa [← Complex.ofReal_pow] using hre + +/-- The `Fin N` specialization of `sum_sq_norm_inner_eq_norm_sq_of_span`. -/ +theorem sum_sq_norm_inner_eq_norm_sq_of_mem_span {N : ℕ} {psi : Fin N → L2Interval lam} + (horth : Orthonormal ℂ psi) {x : L2Interval lam} + (hx : x ∈ Submodule.span ℂ (Set.range psi)) : + ∑ j, ‖inner ℂ (psi j) x‖ ^ 2 = ‖x‖ ^ 2 := + sum_sq_norm_inner_eq_norm_sq_of_span horth hx + +/-- **The tensor squares pair as the square of the inner product**, hence are orthonormal in +`L²(I²)`. + +This needs no Fubini: the double integral factors by pulling a constant out of each integral, the +same route `integral_double_finsetSum` takes. -/ +@[zz_tag "lem_Psi_orthonormal"] +theorem integral_tensor_square_pairing {lam : ℝ} {ι : Type*} [DecidableEq ι] + (psi : ι → Lp ℂ 2 (volume.restrict (Set.Ioo (-lam) lam))) + (h : Orthonormal ℂ psi) (j l : ι) : + (∫ u in Set.Ioo (-lam) lam, ∫ v in Set.Ioo (-lam) lam, + ((psi j : ℝ → ℂ) u * (psi j : ℝ → ℂ) v) * + (starRingEnd ℂ) ((psi l : ℝ → ℂ) u * (psi l : ℝ → ℂ) v)) + = if j = l then 1 else 0 := by + let s : Set ℝ := Set.Ioo (-lam) lam + let μ : Measure ℝ := volume.restrict s + let a : ℝ → ℂ := fun x => (psi j : ℝ → ℂ) x * (starRingEnd ℂ) ((psi l : ℝ → ℂ) x) + have ha : ∫ x, a x ∂μ = (if j = l then 1 else 0 : ℂ) := by + have hij := (orthonormal_iff_ite.mp h) l j + rw [MeasureTheory.L2.inner_def] at hij + simpa [a, μ, RCLike.inner_apply', mul_comm, eq_comm] using hij + change ∫ u, ∫ v, ((psi j : ℝ → ℂ) u * (psi j : ℝ → ℂ) v) * + (starRingEnd ℂ) ((psi l : ℝ → ℂ) u * (psi l : ℝ → ℂ) v) ∂μ ∂μ = + (if j = l then 1 else 0 : ℂ) + calc + ∫ u, ∫ v, ((psi j : ℝ → ℂ) u * (psi j : ℝ → ℂ) v) * + (starRingEnd ℂ) ((psi l : ℝ → ℂ) u * (psi l : ℝ → ℂ) v) ∂μ ∂μ + = ∫ u, ∫ v, a u * a v ∂μ ∂μ := by + simp [a, mul_assoc, mul_left_comm, mul_comm] + _ = ∫ u, a u * (∫ v, a v ∂μ) ∂μ := by + simp [MeasureTheory.integral_const_mul] + _ = (∫ u, a u ∂μ) * (∫ v, a v ∂μ) := by + simp [MeasureTheory.integral_mul_const] + _ = (if j = l then 1 else 0 : ℂ) := by + rw [ha] + by_cases h_eq : j = l <;> simp [h_eq] + +/-! +### The total Bessel sum +-/ + +/-- `gzL2` has the squared norm of `gz`. -/ +theorem norm_gzL2_sq (h : IsAdmissible lam eta) (z : ℂ) : + ‖gzL2 h z‖ ^ 2 = ∫ u in Set.Ioo (-lam) lam, ‖gz eta z u‖ ^ 2 := by + rw [norm_sq_eq_integral] + exact integral_congr_ae (by filter_upwards [coeFn_gzL2 h z] with u hu; rw [hu]) + +/-- `hzL2` has the squared norm of `hz`. -/ +theorem norm_hzL2_sq (h : IsAdmissible lam eta) (z : ℂ) : + ‖hzL2 h z‖ ^ 2 = ∫ u in Set.Ioo (-lam) lam, ‖hz eta z u‖ ^ 2 := by + rw [norm_sq_eq_integral] + exact integral_congr_ae (by filter_upwards [coeFn_hzL2 h z] with u hu; rw [hu]) + +/-- `integral_norm_gz_sq_sub_integral_norm_hz_sq` at the level of `L²` elements. -/ +theorem norm_gzL2_sq_sub_norm_hzL2_sq (h : IsAdmissible lam eta) (z : ℂ) : + ‖gzL2 h z‖ ^ 2 - ‖hzL2 h z‖ ^ 2 = 1 := by + rw [norm_gzL2_sq, norm_hzL2_sq] + exact integral_norm_gz_sq_sub_integral_norm_hz_sq h z + +/-- The real part of a natural multiple of the square of a real complex number. + +Not `private`: a private name is mangled, so the axiom probe reports `Unknown constant` for it and +then cannot gate the file at all. -/ +theorem natCast_mul_sq_re {w : ℂ} (hw : w.im = 0) (n : ℕ) : + ((n : ℂ) * w ^ 2).re = (n : ℝ) * ‖w‖ ^ 2 := by + rw [Complex.sq_norm, Complex.normSq_apply] + simp only [sq, Complex.mul_re, Complex.natCast_re, Complex.natCast_im, hw] + ring + +/-- **The total Bessel sum** (`lem_alpha_sum_total`). Over the whole basis the coefficients sum to +the total multiplicity. + +Parseval, not Bessel: the pairings are summed over the FULL basis, so each spanning vector +contributes exactly its squared norm — one for each `f_x` at a real point, and one for each +non-real `z` by the norm defect between `g_z` and `h_z`. -/ +@[zz_tag "lem_alpha_sum_total"] +theorem sum_alphaOf_re_eq_sum_mult (h : IsAdmissible lam eta) (hZ : IsConjInvariant Z m) + {psi : Fin (Module.finrank ℂ (subspaceW h Z m)) → L2Interval lam} + (hb : IsAdaptedBasis h Z m psi) (hsym : ∀ j, IsSymmetricL2 (psi j)) : + ∑ j, (alphaOf eta lam Z m (psi j)).re = ∑ z ∈ Z, (m z : ℝ) := by + classical + -- every spanning vector lies in the span of the basis + have hmemW : ∀ y ∈ (fzL2 h '' ((simpleRealPart Z m ∪ multipleRealPart Z m : Finset ℂ) : Set ℂ)) + ∪ (gzL2 h '' (nonRealPart Z : Set ℂ)) ∪ (hzL2 h '' (nonRealPart Z : Set ℂ)), + y ∈ Submodule.span ℂ (Set.range psi) := by + intro y hy + rw [hb.span_W, subspaceW] + exact Submodule.subset_span hy + have hfz : ∀ x ∈ simpleRealPart Z m ∪ multipleRealPart Z m, + fzL2 h x ∈ Submodule.span ℂ (Set.range psi) := fun x hx => + hmemW _ (Set.mem_union_left _ (Set.mem_union_left _ ⟨x, by simpa using hx, rfl⟩)) + have hgz : ∀ z ∈ nonRealPart Z, gzL2 h z ∈ Submodule.span ℂ (Set.range psi) := fun z hz => + hmemW _ (Set.mem_union_left _ (Set.mem_union_right _ ⟨z, by simpa using hz, rfl⟩)) + have hhz : ∀ z ∈ nonRealPart Z, hzL2 h z ∈ Submodule.span ℂ (Set.range psi) := fun z hz => + hmemW _ (Set.mem_union_right _ ⟨z, by simpa using hz, rfl⟩) + -- termwise: the real part of each summand, in terms of squared pairings + have hterm : ∀ j, + (alphaOf eta lam Z m (psi j)).re + = (∑ x ∈ simpleRealPart Z m ∪ multipleRealPart Z m, + (m x : ℝ) * ‖inner ℂ (psi j) (fzL2 h x)‖ ^ 2) + + ∑ z ∈ nonRealPart Z, (m z : ℝ) * + (‖inner ℂ (psi j) (gzL2 h z)‖ ^ 2 - ‖inner ℂ (psi j) (hzL2 h z)‖ ^ 2) := by + intro j + rw [alphaOf_eq h hZ, Complex.add_re, Complex.re_sum, Complex.re_sum] + congr 1 + · refine Finset.sum_congr rfl fun x hx => ?_ + have hxre : x.im = 0 := by + rcases Finset.mem_union.mp hx with hx' | hx' + · exact ((Finset.mem_filter.mp hx').2).1 + · exact ((Finset.mem_filter.mp hx').2).1 + rw [integral_mul_conj_eq_inner_of_ae (coeFn_fzL2 h x)] + refine natCast_mul_sq_re ?_ _ + exact inner_symmetricL2_im_eq_zero (hsym j) (fzL2_mem_symmetricSubspace h hxre) + · refine Finset.sum_congr rfl fun z _ => ?_ + have hgim : (inner ℂ (psi j) (gzL2 h z) : ℂ).im = 0 := by + exact inner_symmetricL2_im_eq_zero (hsym j) (gzL2_mem_symmetricSubspace h z) + have hhim : (inner ℂ (psi j) (hzL2 h z) : ℂ).im = 0 := by + exact inner_symmetricL2_im_eq_zero (hsym j) (hzL2_mem_symmetricSubspace h z) + rw [integral_mul_conj_eq_inner_of_ae (coeFn_gzL2 h z), + integral_mul_conj_eq_inner_of_ae (coeFn_hzL2 h z)] + rw [Complex.sq_norm, Complex.sq_norm, Complex.normSq_apply, Complex.normSq_apply] + simp only [Complex.mul_re, Complex.sub_re, Complex.sub_im, Complex.mul_im, sq, + Complex.natCast_re, Complex.natCast_im, hgim, hhim] + ring + rw [Finset.sum_congr rfl fun j _ => hterm j, Finset.sum_add_distrib] + rw [Finset.sum_comm, Finset.sum_comm (s := Finset.univ) (t := nonRealPart Z)] + have hone : ∀ x ∈ simpleRealPart Z m ∪ multipleRealPart Z m, + ∑ j, (m x : ℝ) * ‖inner ℂ (psi j) (fzL2 h x)‖ ^ 2 = (m x : ℝ) := by + intro x hx + have hxre : x.im = 0 := by + rcases Finset.mem_union.mp hx with hx' | hx' + · exact ((Finset.mem_filter.mp hx').2).1 + · exact ((Finset.mem_filter.mp hx').2).1 + rw [← Finset.mul_sum, + sum_sq_norm_inner_eq_norm_sq_of_mem_span hb.orthonormal (hfz x hx), + norm_fzL2_sq_eq_one h hxre, mul_one] + have htwo : ∀ z ∈ nonRealPart Z, + ∑ j, (m z : ℝ) * + (‖inner ℂ (psi j) (gzL2 h z)‖ ^ 2 - ‖inner ℂ (psi j) (hzL2 h z)‖ ^ 2) = (m z : ℝ) := by + intro z hz + rw [← Finset.mul_sum, Finset.sum_sub_distrib, + sum_sq_norm_inner_eq_norm_sq_of_mem_span hb.orthonormal (hgz z hz), + sum_sq_norm_inner_eq_norm_sq_of_mem_span hb.orthonormal (hhz z hz), + norm_gzL2_sq_sub_norm_hzL2_sq h z, mul_one] + rw [Finset.sum_congr rfl hone, Finset.sum_congr rfl htwo, + ← Finset.sum_union disjoint_realPart_nonRealPart, union_realPart_nonRealPart hZ] + +/-! +### The adapted orthonormal basis + +The new ingredient over standard Gram--Schmidt is that every vector stays in the prescribed real +subspace, which is what carries the paper's symmetry. + +Scoped in a section: it opens `Set` and `Submodule`, which the rest of this file does not. +-/ + +section AdaptedBasis + +open Module InnerProductSpace Submodule Set + +/-- **(Crux, uses `hreal`.)** Every Gram-Schmidt vector of a family lying in the real subspace `S` +stays in `S`. + +Argument: strong induction on `i` using the recursion +`gramSchmidt ℂ v i = v i - ∑_{k simp [hc] + rw [this, Complex.coe_smul] + exact S.smul_mem c.re hx + -- strong induction on `i.val`. + intro i + induction hm : i.val using Nat.strong_induction_on generalizing i with + | _ m IH => + subst hm + have IH' : ∀ k : Fin n, k < i → gramSchmidt ℂ v k ∈ S := by + intro k hk + exact IH k.val hk k rfl + rw [gramSchmidt_def ℂ v i] + apply S.sub_mem (hv i) + apply S.sum_mem + intro k hk + rw [Finset.mem_Iio] at hk + rw [Submodule.starProjection_singleton ℂ (v i)] + apply smul_mem + · -- the coefficient is real + have hnum : (inner ℂ (gramSchmidt ℂ v k) (v i) : ℂ).im = 0 := + hreal _ (IH' k hk) _ (hv i) + have hden : ((‖gramSchmidt ℂ v k‖ : ℂ) ^ 2).im = 0 := by + simp [pow_two, Complex.mul_im] + simp [Complex.div_im, hnum, hden] + · exact IH' k hk + +/-- The *normalized* Gram-Schmidt vectors also stay in `S`: `gn i = ‖g i‖⁻¹ • g i` is a real scalar +multiple of `g i ∈ S`. -/ +theorem gramSchmidtNormed_mem_S + {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℂ E] + (S : Submodule ℝ E) + (hreal : ∀ a ∈ S, ∀ b ∈ S, (inner ℂ a b : ℂ).im = 0) + {n : ℕ} (v : Fin n → E) (hv : ∀ i, v i ∈ S) : + ∀ i, gramSchmidtNormed ℂ v i ∈ S := by + have smul_mem : ∀ (c : ℂ), c.im = 0 → ∀ x ∈ S, c • x ∈ S := by + intro c hc x hx + have : c = ((c.re : ℝ) : ℂ) := by + apply Complex.ext <;> simp [hc] + rw [this, Complex.coe_smul] + exact S.smul_mem c.re hx + intro i + have hmem : gramSchmidt ℂ v i ∈ S := gramSchmidt_mem_S S hreal v hv i + unfold gramSchmidtNormed + apply smul_mem + · simp + · exact hmem + +-- The number of nonzero normalized Gram-Schmidt vectors equals the rank of the family. +-- +-- The nonzero `gn i` form an orthonormal family (`gramSchmidtNormed_orthonormal'`), hence linearly +-- independent, and span `span ℂ (range v)` +-- (via `span_gramSchmidtNormed_range` + `span_gramSchmidt`); +-- an orthonormal spanning set of a space of finite rank `r` has exactly `r` elements. +open Classical in +theorem card_nonzero_gramSchmidtNormed + {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℂ E] + {n : ℕ} (v : Fin n → E) : + (Finset.univ.filter (fun i : Fin n => gramSchmidtNormed ℂ v i ≠ 0)).card + = finrank ℂ (Submodule.span ℂ (Set.range v)) := by + classical + set gn : Fin n → E := gramSchmidtNormed ℂ v with hgn + -- The nonzero normalized vectors form an orthonormal (hence linearly independent) family. + have hon : Orthonormal ℂ (fun i : {i : Fin n // gn i ≠ 0} => gn ↑i) := + gramSchmidtNormed_orthonormal' v + have hli : LinearIndependent ℂ (fun i : {i : Fin n // gn i ≠ 0} => gn ↑i) := + hon.linearIndependent + -- The span of this family equals `span (range v)`. + have hrange : Set.range (fun i : {i : Fin n // gn i ≠ 0} => gn ↑i) + = gn '' {i | gn i ≠ 0} := by + ext x + constructor + · rintro ⟨⟨i, hi⟩, rfl⟩; exact ⟨i, hi, rfl⟩ + · rintro ⟨i, hi, rfl⟩; exact ⟨⟨i, hi⟩, rfl⟩ + have himg : gn '' {i | gn i ≠ 0} = Set.range gn \ {0} := by + ext x + constructor + · rintro ⟨i, hi, rfl⟩; exact ⟨⟨i, rfl⟩, hi⟩ + · rintro ⟨⟨i, rfl⟩, hx⟩; exact ⟨i, hx, rfl⟩ + have hspan : Submodule.span ℂ (Set.range (fun i : {i : Fin n // gn i ≠ 0} => gn ↑i)) + = Submodule.span ℂ (Set.range v) := by + rw [hrange, himg, Submodule.span_sdiff_singleton_zero, hgn, + span_gramSchmidtNormed_range, span_gramSchmidt] + -- Count via `finrank_span_eq_card`. + rw [← hspan] + rw [finrank_span_eq_card hli] + -- `Fintype.card {i // gn i ≠ 0} = card of filter`. + rw [Fintype.card_subtype] + +open Classical in +private theorem span_ordered_gramSchmidtNormed_initial + {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℂ E] + {n : ℕ} (v g gn : Fin n → E) + (hg : g = gramSchmidt ℂ v) (hgn : gn = gramSchmidtNormed ℂ v) + (s : Finset (Fin n)) (hs : s = Finset.univ.filter fun i => gn i ≠ 0) + (r : ℕ) (e : Fin r → Fin n) + (e_mem : ∀ j, e j ∈ s) (e_inj : Function.Injective e) + (e_range : Set.range e = (s : Set (Fin n))) + (e_lt : ∀ x y, e x < e y ↔ x < y) (e_le : ∀ x y, e x ≤ e y ↔ x ≤ y) + (d0 : ℕ) (hd0 : d0 ≤ n) : + Submodule.span ℂ ((fun j => gn (e j)) '' + {j : Fin r | + (j : ℕ) < finrank ℂ (Submodule.span ℂ (v '' {i : Fin n | (i : ℕ) < d0}))}) = + Submodule.span ℂ (v '' {i : Fin n | (i : ℕ) < d0}) := by + classical + have hspanA : Submodule.span ℂ (g '' {i : Fin n | (i : ℕ) < d0}) = + Submodule.span ℂ (v '' {i : Fin n | (i : ℕ) < d0}) := by + rcases eq_or_lt_of_le hd0 with h | h + · have hset : {i : Fin n | (i : ℕ) < d0} = Set.univ := by + ext i + simp only [Set.mem_ofPred_eq, Set.mem_univ, iff_true] + have := i.isLt + omega + rw [hset, Set.image_univ, Set.image_univ, hg, span_gramSchmidt] + · have hset : {i : Fin n | (i : ℕ) < d0} = Set.Iio (⟨d0, h⟩ : Fin n) := by + ext i + simp [Set.mem_Iio, Fin.lt_def] + rw [hset, hg, span_gramSchmidt_Iio] + set A : Set (Fin n) := {i : Fin n | (i : ℕ) < d0} with hA + set d : ℕ := finrank ℂ (Submodule.span ℂ (v '' A)) with hd + set T : Finset (Fin n) := s.filter (fun i => (i : ℕ) < d0) with hT + have hcountA : T.card = d := by + have hon : Orthonormal ℂ (fun i : {i : Fin n // gn i ≠ 0} => gn ↑i) := by + rw [hgn] + exact gramSchmidtNormed_orthonormal' v + have hTsub : ∀ i ∈ T, gn i ≠ 0 := by + intro i hi + rw [hT] at hi + rw [Finset.mem_filter, hs, Finset.mem_filter] at hi + exact hi.1.2 + set ι : {x : Fin n // x ∈ T} → {i : Fin n // gn i ≠ 0} := + fun x => ⟨x.1, hTsub x.1 x.2⟩ with hι + have hιinj : Function.Injective ι := by + intro a b hab + apply Subtype.ext + simpa [hι] using hab + have honT : Orthonormal ℂ (fun x : {x : Fin n // x ∈ T} => gn x.1) := by + exact hon.comp ι hιinj + have hliT : LinearIndependent ℂ (fun x : {x : Fin n // x ∈ T} => gn x.1) := + honT.linearIndependent + have hrangeT : Set.range (fun x : {x : Fin n // x ∈ T} => gn x.1) = + gn '' (T : Set (Fin n)) := by + ext y + constructor + · rintro ⟨⟨i, hi⟩, rfl⟩ + exact ⟨i, hi, rfl⟩ + · rintro ⟨i, hi, rfl⟩ + exact ⟨⟨i, hi⟩, rfl⟩ + have hspanT : Submodule.span ℂ (gn '' (T : Set (Fin n))) = + Submodule.span ℂ (v '' A) := by + have hTA : (T : Set (Fin n)) ⊆ A := by + intro i hi + rw [Finset.mem_coe, hT, Finset.mem_filter] at hi + exact hi.2 + have hle : Submodule.span ℂ (gn '' (T : Set (Fin n))) ≤ + Submodule.span ℂ (gn '' A) := Submodule.span_mono (Set.image_mono hTA) + have hge : Submodule.span ℂ (gn '' A) ≤ + Submodule.span ℂ (gn '' (T : Set (Fin n))) := by + rw [Submodule.span_le] + rintro _ ⟨i, hiA, rfl⟩ + by_cases hzero : gn i = 0 + · rw [hzero] + exact Submodule.zero_mem _ + · have hiT : i ∈ T := by + rw [hT, Finset.mem_filter, hs, Finset.mem_filter] + exact ⟨⟨Finset.mem_univ i, hzero⟩, hiA⟩ + exact Submodule.subset_span ⟨i, hiT, rfl⟩ + have heqAT : Submodule.span ℂ (gn '' (T : Set (Fin n))) = + Submodule.span ℂ (gn '' A) := le_antisymm hle hge + rw [heqAT, hgn, span_gramSchmidtNormed, ← hg, hspanA] + calc + T.card = Fintype.card {x : Fin n // x ∈ T} := (Fintype.card_coe T).symm + _ = finrank ℂ (Submodule.span ℂ + (Set.range (fun x : {x : Fin n // x ∈ T} => gn x.1))) := + (finrank_span_eq_card hliT).symm + _ = finrank ℂ (Submodule.span ℂ (gn '' (T : Set (Fin n)))) := by rw [hrangeT] + _ = finrank ℂ (Submodule.span ℂ (v '' A)) := by rw [hspanT] + _ = d := rfl + set P : Finset (Fin r) := Finset.univ.filter (fun k => (e k : ℕ) < d0) with hP + have hPcard : P.card = d := by + have himg : P.image e = T := by + ext x + simp only [hP, hT, Finset.mem_image, Finset.mem_filter, Finset.mem_univ, true_and] + constructor + · rintro ⟨k, hk, rfl⟩ + exact ⟨e_mem k, hk⟩ + · rintro ⟨hxs, hxd0⟩ + have hxr : x ∈ Set.range e := by + rw [e_range] + exact Finset.mem_coe.mpr hxs + obtain ⟨k, rfl⟩ := hxr + exact ⟨k, hxd0, rfl⟩ + have hcardImage := Finset.card_image_of_injective P e_inj + rw [himg, hcountA] at hcardImage + exact hcardImage.symm + have echar : ∀ j : Fin r, ((j : ℕ) < d ↔ (e j : ℕ) < d0) := by + intro j + constructor + · intro hjd + by_contra hcon + push Not at hcon + have hsub : P ⊆ Finset.Iio j := by + intro k hk + rw [hP, Finset.mem_filter] at hk + have hlt : (e k : ℕ) < (e j : ℕ) := lt_of_lt_of_le hk.2 hcon + rw [Finset.mem_Iio] + exact (e_lt k j).mp hlt + have hcardle : P.card ≤ (Finset.Iio j).card := Finset.card_le_card hsub + rw [hPcard, Fin.card_Iio] at hcardle + omega + · intro hej + have hsub : Finset.Iic j ⊆ P := by + intro k hk + rw [Finset.mem_Iic] at hk + rw [hP, Finset.mem_filter] + refine ⟨Finset.mem_univ k, ?_⟩ + have hkle : e k ≤ e j := (e_le k j).mpr hk + omega + have hcardle : (Finset.Iic j).card ≤ P.card := Finset.card_le_card hsub + rw [hPcard, Fin.card_Iic] at hcardle + omega + have hseteq : e '' {j : Fin r | (j : ℕ) < d} = (T : Set (Fin n)) := by + ext x + simp only [Set.mem_image, Set.mem_ofPred_eq] + constructor + · rintro ⟨j, hj, rfl⟩ + rw [hT, Finset.mem_coe, Finset.mem_filter] + exact ⟨e_mem j, (echar j).mp hj⟩ + · intro hx + rw [hT, Finset.mem_coe, Finset.mem_filter] at hx + obtain ⟨hxs, hxd0⟩ := hx + have hxr : x ∈ Set.range e := by + rw [e_range] + exact Finset.mem_coe.mpr hxs + obtain ⟨j, rfl⟩ := hxr + exact ⟨j, (echar j).mpr hxd0, rfl⟩ + have himage : (fun j => gn (e j)) '' {j : Fin r | (j : ℕ) < d} = + gn '' (e '' {j : Fin r | (j : ℕ) < d}) := by rw [Set.image_image] + change Submodule.span ℂ ((fun j => gn (e j)) '' {j : Fin r | (j : ℕ) < d}) = + Submodule.span ℂ (v '' A) + rw [himage, hseteq] + have hTA : (T : Set (Fin n)) ⊆ A := by + intro i hi + rw [Finset.mem_coe, hT, Finset.mem_filter] at hi + exact hi.2 + have hle : Submodule.span ℂ (gn '' (T : Set (Fin n))) ≤ + Submodule.span ℂ (gn '' A) := Submodule.span_mono (Set.image_mono hTA) + have hge : Submodule.span ℂ (gn '' A) ≤ Submodule.span ℂ (gn '' (T : Set (Fin n))) := by + rw [Submodule.span_le] + rintro _ ⟨i, hiA, rfl⟩ + by_cases hzero : gn i = 0 + · rw [hzero] + exact Submodule.zero_mem _ + · have hiT : i ∈ T := by + rw [hT, Finset.mem_filter, hs, Finset.mem_filter] + exact ⟨⟨Finset.mem_univ i, hzero⟩, hiA⟩ + exact Submodule.subset_span ⟨i, hiT, rfl⟩ + have heqAT : Submodule.span ℂ (gn '' (T : Set (Fin n))) = + Submodule.span ℂ (gn '' A) := le_antisymm hle hge + rw [heqAT, hgn, span_gramSchmidtNormed, ← hg, hspanA] + +/-- **Existence of an adapted orthonormal basis inside a real subspace.** + +`v` is a finite ordered family spanning `W`; its initial segments of lengths `dU` and `dV` span two +nested subspaces. There is an orthonormal family spanning the same `W`, lying in `S`, whose own +initial segments (of the appropriate dimensions) span those same two subspaces. -/ +theorem exists_adapted_orthonormal_basis + {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℂ E] + (S : Submodule ℝ E) + (hreal : ∀ a ∈ S, ∀ b ∈ S, (inner ℂ a b : ℂ).im = 0) + {n : ℕ} (v : Fin n → E) (hv : ∀ i, v i ∈ S) + (dU dV : ℕ) (hUV : dU ≤ dV) (hVn : dV ≤ n) : + ∃ psi : Fin (finrank ℂ (Submodule.span ℂ (Set.range v))) → E, + Orthonormal ℂ psi ∧ + (∀ j, psi j ∈ S) ∧ + Submodule.span ℂ (Set.range psi) = Submodule.span ℂ (Set.range v) ∧ + Submodule.span ℂ + (psi '' {j | (j : ℕ) < finrank ℂ (Submodule.span ℂ (v '' {i | (i : ℕ) < dU}))}) + = Submodule.span ℂ (v '' {i | (i : ℕ) < dU}) ∧ + Submodule.span ℂ + (psi '' {j | (j : ℕ) < finrank ℂ (Submodule.span ℂ (v '' {i | (i : ℕ) < dV}))}) + = Submodule.span ℂ (v '' {i | (i : ℕ) < dV}) := by + classical + -- Abbreviations + set g : Fin n → E := gramSchmidt ℂ v with hg + set gn : Fin n → E := gramSchmidtNormed ℂ v with hgn + -- The Finset of indices whose normalized Gram-Schmidt vector is nonzero, listed in order. + set s : Finset (Fin n) := Finset.univ.filter (fun i => gn i ≠ 0) with hs + -- Its cardinality is the rank `r`. + have hcard : s.card = finrank ℂ (Submodule.span ℂ (Set.range v)) := by + rw [hs, hgn] + exact card_nonzero_gramSchmidtNormed v + -- Order-preserving reindexing `Fin r → Fin n` whose image is `s`. + set r : ℕ := finrank ℂ (Submodule.span ℂ (Set.range v)) with hr + set e : Fin r → Fin n := ⇑(s.orderEmbOfFin hcard) with he + have e_mem : ∀ j, e j ∈ s := fun j => Finset.orderEmbOfFin_mem s hcard j + have e_ne : ∀ j, gn (e j) ≠ 0 := by + intro j + have := e_mem j + rw [hs] at this + simpa using this + have e_inj : Function.Injective e := (s.orderEmbOfFin hcard).injective + have e_range : Set.range e = (s : Set (Fin n)) := Finset.range_orderEmbOfFin s hcard + have e_lt : ∀ (x y : Fin r), e x < e y ↔ x < y := fun x y => + (s.orderEmbOfFin hcard).lt_iff_lt + have e_le : ∀ (x y : Fin r), e x ≤ e y ↔ x ≤ y := fun x y => + (s.orderEmbOfFin hcard).le_iff_le + have key : ∀ d0 : ℕ, d0 ≤ n → + Submodule.span ℂ ((fun j => gn (e j)) '' + {j : Fin r | (j : ℕ) < finrank ℂ (Submodule.span ℂ (v '' {i : Fin n | (i : ℕ) < d0}))}) + = Submodule.span ℂ (v '' {i : Fin n | (i : ℕ) < d0}) := by + intro d0 hd0 + exact span_ordered_gramSchmidtNormed_initial v g gn hg hgn s hs r e e_mem e_inj + e_range e_lt e_le d0 hd0 + -- The candidate basis. + refine ⟨fun j => gn (e j), ?_, ?_, ?_, ?_, ?_⟩ + · -- (1) Orthonormal. + have hon : Orthonormal ℂ (fun i : {i : Fin n // gn i ≠ 0} => gn ↑i) := by + rw [hgn]; exact gramSchmidtNormed_orthonormal' v + set φ : Fin r → {i : Fin n // gn i ≠ 0} := fun j => ⟨e j, e_ne j⟩ with hφ + have hφinj : Function.Injective φ := by + intro a b hab + apply e_inj + simpa [hφ] using hab + have h := hon.comp φ hφinj + exact h + · -- (2) Membership in S. + intro j + have : gramSchmidtNormed ℂ v (e j) ∈ S := gramSchmidtNormed_mem_S S hreal v hv (e j) + rw [hgn] + exact this + · -- (3) span (range psi) = span (range v). + have hrange : Set.range (fun j => gn (e j)) = gn '' s := by + rw [Set.range_comp' gn e, e_range] + rw [hrange] + have himg : gn '' (s : Set (Fin n)) = Set.range gn \ {0} := by + ext x + constructor + · rintro ⟨i, hi, rfl⟩ + rw [hs] at hi; simp only [Finset.coe_filter, Set.mem_ofPred_eq] at hi + exact ⟨⟨i, rfl⟩, hi.2⟩ + · rintro ⟨⟨i, rfl⟩, hx⟩ + refine ⟨i, ?_, rfl⟩ + rw [hs] + simp only [Finset.coe_filter, Set.mem_ofPred_eq] + exact ⟨Finset.mem_univ i, hx⟩ + rw [himg, Submodule.span_sdiff_singleton_zero, hgn, + span_gramSchmidtNormed_range, span_gramSchmidt] + · -- (4) Initial segment at dimension of `span (v '' {i < dU})`. + -- Let `dU' = finrank ℂ (span ℂ (v '' {i < dU}))`. Because `e` is strictly monotone with image + -- `s`, the values `e 0, …, e (dU'-1)` are exactly the elements of `s` that are `< dU` + -- (there are exactly `dU'` of them: the count of nonzero `gn i` among `i < dU` equals the rank + -- of `span (g '' {i ((s.equivFin.symm i : s) : E)) = (s : Set E) := by + ext x + constructor + · rintro ⟨i, rfl⟩ + exact (s.equivFin.symm i).2 + · intro hx + exact ⟨s.equivFin ⟨x, hx⟩, by simp⟩ + +/-- The range of two finite families appended together is the union of their ranges. -/ +theorem range_fin_append {E : Type*} {a b : ℕ} (f : Fin a → E) (g : Fin b → E) : + Set.range (Fin.append f g) = Set.range f ∪ Set.range g := by + ext x + constructor + · rintro ⟨i, rfl⟩ + exact Fin.addCases (fun j => Or.inl ⟨j, (Fin.append_left f g j).symm⟩) + (fun j => Or.inr ⟨j, (Fin.append_right f g j).symm⟩) i + · rintro (⟨i, rfl⟩ | ⟨j, rfl⟩) + · exact ⟨Fin.castAdd b i, Fin.append_left f g i⟩ + · exact ⟨Fin.natAdd a j, Fin.append_right f g j⟩ + +/-- The first block in an appended finite family has the expected image. -/ +theorem image_append_lt_left {E : Type*} {a b : ℕ} (f : Fin a → E) (g : Fin b → E) : + Fin.append f g '' {i : Fin (a + b) | (i : ℕ) < a} = Set.range f := by + ext x + constructor + · rintro ⟨i, hi, rfl⟩ + let j : Fin a := ⟨i, hi⟩ + have hij : i = Fin.castAdd b j := Fin.ext rfl + exact ⟨j, by rw [hij, Fin.append_left]⟩ + · rintro ⟨j, rfl⟩ + refine ⟨Fin.castAdd b j, ?_, ?_⟩ + · exact j.isLt + · exact Fin.append_left f g j + +/-- An initial segment lying in the left block of an appended family is computed in that block. -/ +theorem image_append_lt_of_le {E : Type*} {a b d : ℕ} (f : Fin a → E) (g : Fin b → E) + (hd : d ≤ a) : + Fin.append f g '' {i : Fin (a + b) | (i : ℕ) < d} + = f '' {i : Fin a | (i : ℕ) < d} := by + ext x + constructor + · rintro ⟨i, hi, rfl⟩ + have hia : (i : ℕ) < a := lt_of_lt_of_le hi hd + let j : Fin a := ⟨i, hia⟩ + have hij : i = Fin.castAdd b j := Fin.ext rfl + exact ⟨j, hi, by rw [hij, Fin.append_left]⟩ + · rintro ⟨j, hj, rfl⟩ + exact ⟨Fin.castAdd b j, hj, Fin.append_left f g j⟩ + +/-- **A symmetric adapted basis exists.** + +Order the natural spanning vectors in three blocks: the generators of `U`, the further +generators of `V`, and the further generators of `W`. The real Gram–Schmidt construction above +then preserves symmetry and its initial segments span exactly `U` and `V`. -/ +@[zz_tag "lem_adapted_basis_exists"] +theorem exists_symmetric_adapted_basis + (h : IsAdmissible lam eta) (Z : Finset ℂ) (m : ℂ → ℕ) : + ∃ psi : Fin (Module.finrank ℂ (subspaceW h Z m)) → L2Interval lam, + IsAdaptedBasis h Z m psi ∧ ∀ j, IsSymmetricL2 (psi j) := by + classical + let A : Finset (L2Interval lam) := + (multipleRealPart Z m).image (fzL2 h) ∪ (nonRealPart Z).image (gzL2 h) + let B : Finset (L2Interval lam) := (simpleRealPart Z m).image (fzL2 h) + let C : Finset (L2Interval lam) := (nonRealPart Z).image (hzL2 h) + let enumA : Fin A.card → L2Interval lam := + fun i => ((A.equivFin.symm i : A) : L2Interval lam) + let enumB : Fin B.card → L2Interval lam := + fun i => ((B.equivFin.symm i : B) : L2Interval lam) + let enumC : Fin C.card → L2Interval lam := + fun i => ((C.equivFin.symm i : C) : L2Interval lam) + let v : Fin (A.card + B.card + C.card) → L2Interval lam := + Fin.append (Fin.append enumA enumB) enumC + have hrA : Set.range enumA = (A : Set (L2Interval lam)) := range_finset_enum A + have hrB : Set.range enumB = (B : Set (L2Interval lam)) := range_finset_enum B + have hrC : Set.range enumC = (C : Set (L2Interval lam)) := range_finset_enum C + have himgA : v '' {i | (i : ℕ) < A.card} = (A : Set (L2Interval lam)) := by + rw [show v = Fin.append (Fin.append enumA enumB) enumC from rfl, + image_append_lt_of_le _ _ (Nat.le_add_right A.card B.card), + image_append_lt_left, hrA] + have himgAB : v '' {i | (i : ℕ) < A.card + B.card} + = (A : Set (L2Interval lam)) ∪ (B : Set (L2Interval lam)) := by + rw [show v = Fin.append (Fin.append enumA enumB) enumC from rfl, + image_append_lt_left, range_fin_append, hrA, hrB] + have hrange : Set.range v = (A : Set (L2Interval lam)) ∪ (B : Set (L2Interval lam)) ∪ + (C : Set (L2Interval lam)) := by + rw [show v = Fin.append (Fin.append enumA enumB) enumC from rfl, + range_fin_append, range_fin_append, hrA, hrB, hrC] + have hA : Submodule.span ℂ (A : Set (L2Interval lam)) = subspaceU h Z m := by + simp only [A, Finset.coe_union, Finset.coe_image, subspaceU] + have hAB : Submodule.span ℂ + ((A : Set (L2Interval lam)) ∪ (B : Set (L2Interval lam))) = subspaceV h Z m := by + simp only [A, B, Finset.coe_union, Finset.coe_image, subspaceV] + rw [show fzL2 h '' (multipleRealPart Z m : Set ℂ) ∪ + gzL2 h '' (nonRealPart Z : Set ℂ) ∪ fzL2 h '' (simpleRealPart Z m : Set ℂ) + = fzL2 h '' ((simpleRealPart Z m ∪ multipleRealPart Z m : Finset ℂ) : Set ℂ) ∪ + gzL2 h '' (nonRealPart Z : Set ℂ) by + rw [Finset.coe_union, Set.image_union] + ac_rfl] + rw [Finset.coe_union, Set.image_union] + have hABC : Submodule.span ℂ + ((A : Set (L2Interval lam)) ∪ (B : Set (L2Interval lam)) ∪ + (C : Set (L2Interval lam))) = subspaceW h Z m := by + simp only [A, B, C, Finset.coe_union, Finset.coe_image, subspaceW] + rw [show (fzL2 h '' (multipleRealPart Z m : Set ℂ) ∪ + gzL2 h '' (nonRealPart Z : Set ℂ)) ∪ fzL2 h '' (simpleRealPart Z m : Set ℂ) ∪ + hzL2 h '' (nonRealPart Z : Set ℂ) + = (fzL2 h '' ((simpleRealPart Z m ∪ multipleRealPart Z m : Finset ℂ) : Set ℂ) ∪ + gzL2 h '' (nonRealPart Z : Set ℂ)) ∪ hzL2 h '' (nonRealPart Z : Set ℂ) by + rw [Finset.coe_union, Set.image_union] + ac_rfl] + rw [Finset.coe_union, Set.image_union] + have hv : ∀ i, v i ∈ symmetricSubspace lam := by + intro i + have hi : v i ∈ (A : Set (L2Interval lam)) ∪ (B : Set (L2Interval lam)) ∪ + (C : Set (L2Interval lam)) := by + rw [← hrange] + exact ⟨i, rfl⟩ + rcases hi with (hi | hi) | hi + · simp only [A, Finset.coe_union, Finset.coe_image] at hi + rcases hi with ⟨x, hx, hxi⟩ | ⟨z, hz, hzi⟩ + · rw [← hxi] + exact fzL2_mem_symmetricSubspace h ((Finset.mem_filter.mp hx).2).1 + · rw [← hzi] + exact gzL2_mem_symmetricSubspace h z + · simp only [B, Finset.coe_image] at hi + rcases hi with ⟨x, hx, hxi⟩ + rw [← hxi] + exact fzL2_mem_symmetricSubspace h ((Finset.mem_filter.mp hx).2).1 + · simp only [C, Finset.coe_image] at hi + rcases hi with ⟨z, hz, hzi⟩ + rw [← hzi] + exact hzL2_mem_symmetricSubspace h z + have hW : Submodule.span ℂ (Set.range v) = subspaceW h Z m := by + rw [hrange, hABC] + have hU : Submodule.span ℂ (v '' {i | (i : ℕ) < A.card}) = subspaceU h Z m := by + rw [himgA, hA] + have hV : Submodule.span ℂ (v '' {i | (i : ℕ) < A.card + B.card}) = subspaceV h Z m := by + rw [himgAB, hAB] + have hex := + exists_adapted_orthonormal_basis (symmetricSubspace lam) + (fun a ha b hb => inner_symmetricL2_im_eq_zero ha hb) v hv + A.card (A.card + B.card) (Nat.le_add_right _ _) (Nat.le_add_right _ _) + rw [hW, hU, hV] at hex + obtain ⟨psi, horth, hsym, hspanW, hspanU, hspanV⟩ := hex + refine ⟨psi, ?_, hsym⟩ + exact ⟨horth, hspanW, hspanU, hspanV⟩ + +/-! +### Multiplicity of a non-trivial zero +-/ + +/-- **Every non-trivial zero has multiplicity at least one** (`lem_mult_pos`). + +Two things have to be ruled out, and only one of them is the obvious one. The order is not `0` +because zeta is analytic at the point and vanishes there. It is also not `⊤`: zeta is not +identically zero near the point, and the only route to that is the identity theorem on the +punctured plane, where `zeta 2 ≠ 0` supplies the contradiction. -/ +@[zz_tag "lem_mult_pos"] +theorem one_le_zeroMultiplicity {T : ℝ} {rho : ℂ} (hrho : rho ∈ nontrivialZeros T) : + 1 ≤ zeroMultiplicity rho := by + obtain ⟨hzero, -, hre1, -, -⟩ := hrho + have hne1 : rho ≠ 1 := by + intro hEq + rw [hEq] at hre1 + simp at hre1 + have hanaOn : AnalyticOnNhd ℂ riemannZeta {(1 : ℂ)}ᶜ := by + intro w hw + rw [Complex.analyticAt_iff_eventually_differentiableAt] + filter_upwards [isOpen_ne.mem_nhds hw] with v hv + exact differentiableAt_riemannZeta hv + have hana : AnalyticAt ℂ riemannZeta rho := hanaOn rho hne1 + have hnetop : analyticOrderAt riemannZeta rho ≠ ⊤ := by + rw [Ne, analyticOrderAt_eq_top] + intro hev + have hconn : IsPreconnected ({(1 : ℂ)}ᶜ : Set ℂ) := + (isConnected_compl_singleton_of_one_lt_rank (by simp) 1).isPreconnected + have hEqOn := hanaOn.eqOn_zero_of_preconnected_of_eventuallyEq_zero hconn hne1 hev + have h2 : riemannZeta 2 = 0 := hEqOn (by norm_num : (2 : ℂ) ≠ 1) + exact riemannZeta_ne_zero_of_one_le_re (by norm_num) h2 + have hne0 : analyticOrderAt riemannZeta rho ≠ 0 := by + rw [Ne, analyticOrderAt_eq_zero] + push Not + exact ⟨hana, hzero⟩ + have hcast := Nat.cast_analyticOrderNatAt hnetop + simp only [zeroMultiplicity] + refine Nat.one_le_iff_ne_zero.mpr fun h0 => hne0 ?_ + rw [← hcast, h0] + rfl + +/-! +### The rescaled zeros carry the three counts +-/ + +/-- The rescaling is injective: it is affine with non-zero linear coefficient. -/ +theorem rescale_injective {T : ℝ} (hT : 1 < T) : Function.Injective (rescale T) := by + have hlog : 0 < Real.log T := Real.log_pos hT + have hc : ((Real.log T / (2 * Real.pi) : ℝ) : ℂ) ≠ 0 := by + simp only [ne_eq, Complex.ofReal_eq_zero] + positivity + intro a b hab + simp only [rescale] at hab + have h1 := mul_right_cancel₀ hc hab + have h2 := mul_left_cancel₀ Complex.I_ne_zero h1 + linear_combination h2 + +/-- The transported multiplicity agrees with the original at a rescaled point. -/ +theorem rescaledMult_rescale {T : ℝ} (hT : 1 < T) (rho : ℂ) : + rescaledMult T (rescale T rho) = zeroMultiplicity rho := by + have hlog : 0 < Real.log T := Real.log_pos hT + have hc : ((Real.log T / (2 * Real.pi) : ℝ) : ℂ) ≠ 0 := by + simp only [ne_eq, Complex.ofReal_eq_zero] + positivity + simp only [rescaledMult, rescale] + congr 1 + field_simp + ring + +/-- **The rescaled zeros carry the counted total** (part of `lem_Z_T_counts`). -/ +@[zz_tag "lem_Z_T_counts"] +theorem sum_rescaledMult_eq_zeroCount {T : ℝ} (hT : 1 < T) : + ∑ z ∈ rescaledZerosFinset T, rescaledMult T z = zeroCount T := by + classical + rw [rescaledZerosFinset, Finset.sum_image fun a _ b _ hab => rescale_injective hT hab] + rw [zeroCount, ← finsum_mem_coe_finset] + rw [Set.Finite.coe_toFinset] + exact finsum_congr fun rho => by + by_cases hmem : rho ∈ nontrivialZeros T <;> + simp [hmem, rescaledMult_rescale hT] + +/-- **The rescaled zeros carry the distinct count** (part of `lem_Z_T_counts`). -/ +@[zz_tag "lem_Z_T_counts"] +theorem card_rescaledZerosFinset_eq_distinctZeroCount {T : ℝ} (hT : 1 < T) : + (rescaledZerosFinset T).card = distinctZeroCount T := by + classical + -- the zero set is only known FINITE, not a Fintype, so the Fintype-flavoured + -- `ncard_eq_toFinset_card'` does not apply; the Finite-hypothesis form does + rw [rescaledZerosFinset, Finset.card_image_of_injective _ (rescale_injective hT), + distinctZeroCount, Set.ncard_eq_toFinset_card _ (nontrivialZeros_finite T)] + +/-- **The rescaled zeros carry the simple-on-line count** (part of `lem_Z_T_counts`). + +The third of the entity's three conjuncts. Under the bijection a rescaled point is real exactly +when the zero is on the critical line (`rescale_im_eq_zero_iff`) and has multiplicity one exactly +when the zero does. -/ +@[zz_tag "lem_Z_T_counts"] +theorem card_simpleRealPart_rescaled_eq_simpleOnLineCount {T : ℝ} (hT : 1 < T) : + (simpleRealPart (rescaledZerosFinset T) (rescaledMult T)).card = simpleOnLineCount T := by + classical + have hfin := nontrivialZeros_finite T + have hsub : {rho ∈ nontrivialZeros T | rho.re = 1 / 2 ∧ zeroMultiplicity rho = 1}.Finite := + hfin.subset fun x hx => hx.1 + have hset : simpleRealPart (rescaledZerosFinset T) (rescaledMult T) + = (hfin.toFinset.filter fun rho => rho.re = 1 / 2 ∧ zeroMultiplicity rho = 1).image + (rescale T) := by + ext w + simp only [simpleRealPart, rescaledZerosFinset, Finset.mem_filter, Finset.mem_image, + Set.Finite.mem_toFinset] + constructor + · rintro ⟨⟨rho, hrho, rfl⟩, him, hm⟩ + refine ⟨rho, ⟨hrho, (rescale_im_eq_zero_iff hT rho).mp him, ?_⟩, rfl⟩ + rwa [rescaledMult_rescale hT] at hm + · rintro ⟨rho, ⟨hrho, hre, hm⟩, rfl⟩ + refine ⟨⟨rho, hrho, rfl⟩, (rescale_im_eq_zero_iff hT rho).mpr hre, ?_⟩ + rw [rescaledMult_rescale hT] + exact hm + rw [hset, Finset.card_image_of_injective _ (rescale_injective hT), simpleOnLineCount, + Set.ncard_eq_toFinset_card _ hsub] + congr 1 + ext rho + simp only [Set.Finite.mem_toFinset, Finset.mem_filter, Set.mem_ofPred_eq] + + +/-- **Parseval over a sub-family**, at elements of that sub-family's span. -/ +theorem sum_sq_norm_inner_eq_norm_sq_of_mem_span_finset {iota : Type*} + {psi : iota → L2Interval lam} (horth : Orthonormal ℂ psi) + (s : Finset iota) {x : L2Interval lam} + (hx : x ∈ Submodule.span ℂ (psi '' (s : Set iota))) : + ∑ j ∈ s, ‖inner ℂ (psi j) x‖ ^ 2 = ‖x‖ ^ 2 := by + classical + have hsub : Orthonormal ℂ fun i : {i // i ∈ s} => psi (i : iota) := + horth.comp _ Subtype.val_injective + have hrange : Set.range (fun i : {i // i ∈ s} => psi (i : iota)) = psi '' (s : Set iota) := by + ext y + simp only [Set.mem_range, Set.mem_image, Finset.mem_coe, Subtype.exists] + tauto + rw [← Finset.sum_coe_sort s fun j => ‖inner ℂ (psi j) x‖ ^ 2] + exact sum_sq_norm_inner_eq_norm_sq_of_span hsub (by rw [hrange]; exact hx) + +/-! +## The normalised test function has total mass one +-/ + +variable {delta : ℝ} {psi : ℝ → ℝ} + +/-- The normalised square, written out: `f_psi = psi² f₀ / A_psi`. The square roots cancel because +both `f₀` and `A_psi` are non-negative. -/ +theorem cutoffTestSq_eq (hd : 0 < delta) (hd4 : delta < 1 / 4) (h : IsCutoff delta psi) (x : ℝ) : + cutoffTestSq psi x = psi x ^ 2 * extremalTest x / cutoffNormaliser psi := by + have hA := cutoffNormaliser_pos hd hd4 h + simp only [cutoffTestSq, cutoffTest, Pi.pow_apply] + rw [div_pow, mul_pow, Real.sq_sqrt (extremalTest_nonneg x), Real.sq_sqrt hA.le] + +/-- **The normalised test function has total mass one** (`lem_f_psi_integral`). -/ +@[zz_tag "lem_f_psi_integral"] +theorem integral_cutoffTestSq (hd : 0 < delta) (hd4 : delta < 1 / 4) (h : IsCutoff delta psi) : + ∫ x : ℝ, cutoffTestSq psi x = 1 := by + have hA := cutoffNormaliser_pos hd hd4 h + rw [integral_congr_ae (.of_forall fun x => cutoffTestSq_eq hd hd4 h x), + MeasureTheory.integral_div, ← cutoffNormaliser] + exact div_self (ne_of_gt hA) + +/-- **Its Fourier transform at zero is one** (`lem_f_psi_integral`). -/ +@[zz_tag "lem_f_psi_integral"] +theorem fourierC_cutoffTestSq_zero (hd : 0 < delta) (hd4 : delta < 1 / 4) + (h : IsCutoff delta psi) : fourierC (cutoffTestSq psi) 0 = 1 := by + have hpt : ∀ u : ℝ, ((cutoffTestSq psi u : ℝ) : ℂ) * + Complex.exp (-(2 * (Real.pi : ℂ)) * Complex.I * 0 * (u : ℂ)) + = ((cutoffTestSq psi u : ℝ) : ℂ) := by + intro u + simp + rw [fourierC, integral_congr_ae (.of_forall hpt), integral_complex_ofReal, + integral_cutoffTestSq hd hd4 h] + norm_num + +/-! ## Bessel for a kernel against tensor squares + +The abstract statement: it mentions no admissible `eta`, no `bigF` and no `alphaCoeff`. What the +instantiation still needs is that `bigF` and the tensor squares are `MemLp 2` for the product +measure; the orthonormality hypothesis is discharged by `integral_tensor_square_pairing`. +-/ + +open MeasureTheory + +/-- Bridging lemma: the `L²` inner product of two tensor-type kernels on the product measure +equals the iterated integral of `f * conj g`. -/ +private lemma inner_toLp_eq_iterated (lam : ℝ) (f g : ℝ → ℝ → ℂ) + (hf : MemLp (fun p : ℝ × ℝ => f p.1 p.2) 2 + ((volume.restrict (Set.Ioo (-lam) lam)).prod (volume.restrict (Set.Ioo (-lam) lam)))) + (hg : MemLp (fun p : ℝ × ℝ => g p.1 p.2) 2 + ((volume.restrict (Set.Ioo (-lam) lam)).prod (volume.restrict (Set.Ioo (-lam) lam)))) : + inner ℂ (hg.toLp _) (hf.toLp _) + = ∫ u in Set.Ioo (-lam) lam, ∫ v in Set.Ioo (-lam) lam, + f u v * (starRingEnd ℂ) (g u v) := by + have hae : ∀ᵐ p : ℝ × ℝ ∂((volume.restrict (Set.Ioo (-lam) lam)).prod + (volume.restrict (Set.Ioo (-lam) lam))), + inner ℂ ((hg.toLp _ : Lp ℂ 2 _) p) ((hf.toLp _ : Lp ℂ 2 _) p) + = f p.1 p.2 * (starRingEnd ℂ) (g p.1 p.2) := by + filter_upwards [hf.coeFn_toLp, hg.coeFn_toLp] with p hp hq + simp [hp, hq, RCLike.inner_apply] + have hint : Integrable (fun p : ℝ × ℝ => f p.1 p.2 * (starRingEnd ℂ) (g p.1 p.2)) + ((volume.restrict (Set.Ioo (-lam) lam)).prod (volume.restrict (Set.Ioo (-lam) lam))) := + (MeasureTheory.L2.integrable_inner (𝕜 := ℂ) (hg.toLp _) (hf.toLp _)).congr hae + rw [MeasureTheory.L2.inner_def, integral_integral hint] + exact integral_congr_ae hae + +/-- **Bessel for a kernel paired against tensor squares.** + +`phi` is a finite family whose tensor squares are orthonormal in the iterated-integral pairing +(`horth`). Then the squared pairings of the kernel `K` against them are dominated by the squared +`L²` norm of `K`. -/ +theorem sum_sq_pairing_le_integral_norm_sq {lam : ℝ} {N : ℕ} + (K : ℝ → ℝ → ℂ) (phi : Fin N → ℝ → ℂ) + (hK : MemLp (fun p : ℝ × ℝ => K p.1 p.2) 2 + ((volume.restrict (Set.Ioo (-lam) lam)).prod (volume.restrict (Set.Ioo (-lam) lam)))) + (hphi : ∀ j, MemLp (fun p : ℝ × ℝ => phi j p.1 * phi j p.2) 2 + ((volume.restrict (Set.Ioo (-lam) lam)).prod (volume.restrict (Set.Ioo (-lam) lam)))) + (horth : ∀ j l : Fin N, + (∫ u in Set.Ioo (-lam) lam, ∫ v in Set.Ioo (-lam) lam, + (phi j u * phi j v) * (starRingEnd ℂ) (phi l u * phi l v)) + = if j = l then 1 else 0) : + ∑ j, ‖∫ u in Set.Ioo (-lam) lam, ∫ v in Set.Ioo (-lam) lam, + K u v * (starRingEnd ℂ) (phi j u * phi j v)‖ ^ 2 + ≤ ∫ u in Set.Ioo (-lam) lam, ∫ v in Set.Ioo (-lam) lam, ‖K u v‖ ^ 2 := by + classical + -- the orthonormal family in `L²` of the product + have horthE : Orthonormal ℂ (fun j : Fin N => + (hphi j).toLp (fun p : ℝ × ℝ => phi j p.1 * phi j p.2)) := by + rw [orthonormal_iff_ite] + intro j l + rw [inner_toLp_eq_iterated lam (fun u v => phi l u * phi l v) + (fun u v => phi j u * phi j v) (hphi l) (hphi j), horth l j] + by_cases h : j = l + · subst h; simp + · simp [h, Ne.symm h] + -- Bessel's inequality in `L²` + have hbessel := horthE.sum_inner_products_le + (hK.toLp (fun p : ℝ × ℝ => K p.1 p.2)) (s := Finset.univ) + -- identify the pairings + have hpair : ∀ j : Fin N, + inner ℂ ((hphi j).toLp (fun p : ℝ × ℝ => phi j p.1 * phi j p.2)) + (hK.toLp (fun p : ℝ × ℝ => K p.1 p.2)) + = ∫ u in Set.Ioo (-lam) lam, ∫ v in Set.Ioo (-lam) lam, + K u v * (starRingEnd ℂ) (phi j u * phi j v) := fun j => + inner_toLp_eq_iterated lam K (fun u v => phi j u * phi j v) hK (hphi j) + -- identify the squared norm + have hnorm : ‖hK.toLp (fun p : ℝ × ℝ => K p.1 p.2)‖ ^ 2 + = ∫ u in Set.Ioo (-lam) lam, ∫ v in Set.Ioo (-lam) lam, ‖K u v‖ ^ 2 := by + have h1 := inner_toLp_eq_iterated lam K K hK hK + have h2 : ∀ u : ℝ, (∫ v in Set.Ioo (-lam) lam, K u v * (starRingEnd ℂ) (K u v)) + = ((∫ v in Set.Ioo (-lam) lam, ‖K u v‖ ^ 2 : ℝ) : ℂ) := by + intro u + rw [← integral_complex_ofReal] + refine integral_congr_ae (Filter.Eventually.of_forall fun v => ?_) + simp [Complex.mul_conj'] + rw [← inner_self_eq_norm_sq (𝕜 := ℂ), h1] + simp only [h2] + rw [integral_complex_ofReal] + simp + rw [← hnorm] + refine le_trans (le_of_eq ?_) hbessel + refine Finset.sum_congr rfl fun j _ => ?_ + rw [hpair j] + +/-- The tensor product of two `L²` functions is `L²` for the product measure. -/ +theorem memLp_tensor_two {alpha beta : Type*} [MeasurableSpace alpha] [MeasurableSpace beta] + {mu : Measure alpha} {nu : Measure beta} + {f : alpha → ℂ} {g : beta → ℂ} (hf : MemLp f 2 mu) (hg : MemLp g 2 nu) : + MemLp (fun p : alpha × beta => f p.1 * g p.2) 2 (mu.prod nu) := by + have h1 : Integrable (fun x => ‖f x‖ ^ 2) mu := (memLp_two_iff_integrable_sq_norm hf.1).mp hf + have h2 : Integrable (fun y => ‖g y‖ ^ 2) nu := (memLp_two_iff_integrable_sq_norm hg.1).mp hg + have hmul : Integrable (fun z : alpha × beta => ‖f z.1‖ ^ 2 * ‖g z.2‖ ^ 2) (mu.prod nu) := + h1.mul_prod h2 + have hmeas : AEStronglyMeasurable (fun p : alpha × beta => f p.1 * g p.2) (mu.prod nu) := + hf.1.comp_fst.mul hg.1.comp_snd + rw [memLp_two_iff_integrable_sq_norm hmeas] + refine hmul.congr ?_ + filter_upwards with z + simp [mul_pow] + +/-- **The normalised cutoff test function is `1/2`-admissible.** + +Three of the four fields are the smoothness, support and integral facts for `cutoffTest` read off +directly. Only square-integrability is new, and it is immediate once the function is known to be +continuous with compact support -- which is why `HasCompactSupport` was worth stating there rather +than leaving the support condition purely pointwise. -/ +@[zz_tag "lem_eta_psi_admissible"] +theorem isAdmissible_cutoffTest {delta : ℝ} (hd : 0 < delta) (hd4 : delta < 1 / 4) {psi : ℝ → ℝ} + (h : IsCutoff delta psi) : IsAdmissible (1 / 2) (cutoffTest psi) where + memLp := (cutoffTest_contDiff h).continuous.memLp_of_hasCompactSupport + (cutoffTest_hasCompactSupport h) + even := cutoffTest_neg h + support := fun _ => cutoffTest_eq_zero_of_half_le_abs h + fourier_sq_zero := fourierC_cutoffTestSq_zero hd hd4 h + +/-! ### The kernel sums agree + +The double sum over the rescaled zeros and the double sum over the actual +zeros are the same sum, reindexed along the rescaling. Two facts carry it: the rescaling is +injective, so `Finset.sum_image` applies at both levels; and it turns a difference of zeros into +exactly the rescaled difference the pair-correlation formula is stated with. + +Stated without an admissibility hypothesis on `eta`: nothing in a reindexing can use it. -/ + +/-- The rescaling turns a difference of zeros into the rescaled difference. -/ +theorem rescale_sub_rescale (T : ℝ) (rho rho' : ℂ) : + rescale T rho - rescale T rho' = rescaledDiff T rho rho' := by + simp only [rescale, rescaledDiff] + ring + +/-- **The kernel sums agree** (`lem_kernel_sum_identity`). -/ +@[zz_tag "lem_kernel_sum_identity"] +theorem sum_testKernel_sq_eq_finsum_rescaledDiff {T : ℝ} (hT : 1 < T) (eta : ℝ → ℝ) : + ∑ z ∈ rescaledZerosFinset T, ∑ s ∈ rescaledZerosFinset T, + ((rescaledMult T z * rescaledMult T s : ℕ) : ℂ) * testKernel eta (z - s) ^ 2 = + ∑ᶠ rho ∈ nontrivialZeros T, ∑ᶠ rho' ∈ nontrivialZeros T, + ((zeroMultiplicity rho * zeroMultiplicity rho' : ℕ) : ℂ) * + testKernel eta (rescaledDiff T rho rho') ^ 2 := by + classical + have hinj : ∀ a ∈ (nontrivialZeros_finite T).toFinset, + ∀ b ∈ (nontrivialZeros_finite T).toFinset, rescale T a = rescale T b → a = b := + fun a _ b _ hab => rescale_injective hT hab + have hinner : ∀ rho : ℂ, ∑ rho' ∈ (nontrivialZeros_finite T).toFinset, + ((zeroMultiplicity rho * zeroMultiplicity rho' : ℕ) : ℂ) * + testKernel eta (rescaledDiff T rho rho') ^ 2 = + ∑ᶠ rho' ∈ nontrivialZeros T, + ((zeroMultiplicity rho * zeroMultiplicity rho' : ℕ) : ℂ) * + testKernel eta (rescaledDiff T rho rho') ^ 2 := fun _ => by + rw [← finsum_mem_coe_finset, Set.Finite.coe_toFinset] + rw [rescaledZerosFinset] + simp only [Finset.sum_image hinj, rescaledMult_rescale hT, rescale_sub_rescale, hinner] + rw [← finsum_mem_coe_finset, Set.Finite.coe_toFinset] + + +/-! ### The Fourier transform of the self-convolution + +The convolution theorem, at a complex frequency. The move worth keeping is to abstract the +character: the statement holds for any continuous `E : ℝ → ℂ` with +`E (a + b) = E a * E b`, and the complex frequency then stops being special -- no +analytic-continuation argument is needed, only Fubini on the compactly supported integrand. + +### The bookkeeping behind the second moment + +A double integral whose integrand is a finite double sum with factored terms equals the double sum +of the products of the single integrals. Also stated abstractly. -/ + +section FourierSelfConv + +/-- Abstract version: for any continuous multiplicative character `E`, the "transform" of a +self-convolution factors. -/ +theorem selfConv_aux (f : ℝ → ℝ) (hf : Continuous f) (hsupp : HasCompactSupport f) + (E : ℝ → ℂ) (hEadd : ∀ a b : ℝ, E (a + b) = E a * E b) (hEcont : Continuous E) : + ∫ x : ℝ, ((∫ t : ℝ, f t * f (x - t) : ℝ) : ℂ) * E x = (∫ u : ℝ, (f u : ℂ) * E u) ^ 2 := by + set F : ℝ → ℂ := fun u => (f u : ℂ) * E u with hFdef + have hFcont : Continuous F := (Complex.continuous_ofReal.comp hf).mul hEcont + have hFsupp : HasCompactSupport F := by + apply HasCompactSupport.intro hsupp + intro x hx + simp [hFdef, image_eq_zero_of_notMem_tsupport hx] + have hFint : Integrable F := hFcont.integrable_of_hasCompactSupport hFsupp + have hEsplit : ∀ x t : ℝ, E (x - t) * E t = E x := by + intro x t + rw [← hEadd] + congr 1 + ring + have hcast : ∀ x : ℝ, ((∫ t : ℝ, f t * f (x - t) : ℝ) : ℂ) + = ∫ t : ℝ, (f t : ℂ) * (f (x - t) : ℂ) := by + intro x + rw [← integral_complex_ofReal] + simp only [Complex.ofReal_mul] + have hint2 : Integrable (Function.uncurry fun x t : ℝ => (f t : ℂ) * (f (x - t) : ℂ) * E x) + ((volume : Measure ℝ).prod volume) := by + have h1 : Integrable (fun z : ℝ × ℝ => F z.1 * F z.2) ((volume : Measure ℝ).prod volume) := + hFint.mul_prod hFint + have h2 := (measurePreserving_sub_prod (volume : Measure ℝ) + (volume : Measure ℝ)).integrable_comp_of_integrable h1 + have heq : ((fun z : ℝ × ℝ => F z.1 * F z.2) ∘ fun z : ℝ × ℝ => (z.1 - z.2, z.2)) + = Function.uncurry fun x t : ℝ => (f t : ℂ) * (f (x - t) : ℂ) * E x := by + funext z + obtain ⟨x, t⟩ := z + simp only [Function.comp_apply, Function.uncurry_apply_pair, hFdef] + rw [← hEsplit x t] + ring + rwa [heq] at h2 + calc ∫ x : ℝ, ((∫ t : ℝ, f t * f (x - t) : ℝ) : ℂ) * E x + = ∫ x : ℝ, ∫ t : ℝ, (f t : ℂ) * (f (x - t) : ℂ) * E x := by + refine integral_congr_ae (Filter.Eventually.of_forall fun x => ?_) + change ((∫ t : ℝ, f t * f (x - t) : ℝ) : ℂ) * E x + = ∫ t : ℝ, (f t : ℂ) * (f (x - t) : ℂ) * E x + rw [hcast x, ← integral_mul_const] + _ = ∫ t : ℝ, ∫ x : ℝ, (f t : ℂ) * (f (x - t) : ℂ) * E x := integral_integral_swap hint2 + _ = ∫ t : ℝ, F t * ∫ y : ℝ, F y := by + refine integral_congr_ae (Filter.Eventually.of_forall fun t => ?_) + change (∫ x : ℝ, (f t : ℂ) * (f (x - t) : ℂ) * E x) = F t * ∫ y : ℝ, F y + rw [← integral_add_right_eq_self (fun x => (f t : ℂ) * (f (x - t) : ℂ) * E x) t] + have hpt : ∀ y : ℝ, (f t : ℂ) * (f (y + t - t) : ℂ) * E (y + t) = F t * F y := by + intro y + rw [hEadd] + simp only [hFdef, add_sub_cancel_right] + ring + simp only [hpt] + exact integral_const_mul _ _ + _ = (∫ u : ℝ, F u) ^ 2 := by rw [integral_mul_const, sq] + +/-- **The transform of a self-convolution is the square of the transform.** -/ +theorem fourierC_selfConv (f : ℝ → ℝ) (hf : Continuous f) (hsupp : HasCompactSupport f) (ξ : ℂ) : + fourierC (fun x => ∫ t : ℝ, f t * f (x - t)) ξ = fourierC f ξ ^ 2 := by + have key := selfConv_aux f hf hsupp + (fun u : ℝ => Complex.exp (-(2 * (Real.pi : ℂ)) * Complex.I * ξ * (u : ℂ))) + (fun a b => by rw [← Complex.exp_add]; congr 1; push_cast; ring) + (Complex.continuous_exp.comp (by fun_prop)) + simpa only [fourierC] using key + +/-- **A double integral of a factored double sum.** -/ +theorem integral_integral_double_sum_factored {iota : Type*} (F : Finset iota) (mu : Measure ℝ) + (c : iota → iota → ℂ) (a : iota → iota → ℝ → ℂ) (ha : ∀ i j, Integrable (a i j) mu) : + (∫ u, ∫ v, ∑ i ∈ F, ∑ j ∈ F, c i j * a i j u * a i j v ∂mu ∂mu) + = ∑ i ∈ F, ∑ j ∈ F, c i j * (∫ u, a i j u ∂mu) * (∫ v, a i j v ∂mu) := by + have key : ∀ (g : iota → iota → ℂ) (b : iota → iota → ℝ → ℂ), + (∀ i j, Integrable (b i j) mu) → + (∫ u, ∑ i ∈ F, ∑ j ∈ F, g i j * b i j u ∂mu) + = ∑ i ∈ F, ∑ j ∈ F, g i j * ∫ u, b i j u ∂mu := by + intro g b hb + rw [integral_finsetSum _ + (fun i _ => integrable_finsetSum _ (fun j _ => (hb i j).const_mul (g i j)))] + refine Finset.sum_congr rfl fun i _ => ?_ + rw [integral_finsetSum _ (fun j _ => (hb i j).const_mul (g i j))] + exact Finset.sum_congr rfl fun j _ => integral_const_mul _ _ + have h2 : ∀ u, (∫ v, ∑ i ∈ F, ∑ j ∈ F, c i j * a i j u * a i j v ∂mu) + = ∑ i ∈ F, ∑ j ∈ F, (c i j * ∫ v, a i j v ∂mu) * a i j u := by + intro u + rw [key (fun i j => c i j * a i j u) a ha] + exact Finset.sum_congr rfl fun i _ => + Finset.sum_congr rfl fun j _ => by ring + rw [show (∫ u, ∫ v, ∑ i ∈ F, ∑ j ∈ F, c i j * a i j u * a i j v ∂mu ∂mu) + = ∫ u, ∑ i ∈ F, ∑ j ∈ F, (c i j * ∫ v, a i j v ∂mu) * a i j u ∂mu from by + simp only [h2]] + rw [key (fun i j => c i j * ∫ v, a i j v ∂mu) a ha] + +/-! ### The two instantiations -/ + +/-- **The transform of `Q_psi` is the square of the kernel** (`lem_Q_psi_hat`). -/ +@[zz_tag "lem_Q_psi_hat"] +theorem fourierC_cutoffSelfConv {delta : ℝ} {psi : ℝ → ℝ} (h : IsCutoff delta psi) (z : ℂ) : + fourierC (cutoffSelfConv psi) z = testKernel (cutoffTest psi) z ^ 2 := by + have hQ : cutoffSelfConv psi + = fun x => ∫ t : ℝ, cutoffTestSq psi t * cutoffTestSq psi (x - t) := rfl + rw [hQ, fourierC_selfConv _ (cutoffTestSq_contDiff h).continuous + (cutoffTestSq_hasCompactSupport h) z] + rfl + +/-- A twisted function against another twisted function's conjugate is integrable. + +Distinct from the existing `integrable_fz_mul_conj`, which pairs against an `L²` ELEMENT. That +one is not syntactically usable here: `(fzL2 h s : ℝ → ℂ)` is only a.e. equal to `fz eta s`, so +applying it would need an `Integrable.congr` where `memLp_fz` directly needs nothing. -/ +theorem integrable_fz_mul_conj_fz (h : IsAdmissible lam eta) (z s : ℂ) : + Integrable (fun u => fz eta z u * (starRingEnd ℂ) (fz eta s u)) + (volume.restrict (Set.Ioo (-lam) lam)) := + (memLp_fz h z).integrable_mul ((memLp_fz h s).star) + +/-- **The second moment is the `L²(I²)` norm of the two-variable kernel** (`lem_second_moment`). -/ +@[zz_tag "lem_second_moment"] +theorem sum_testKernel_sq_eq_integral_bigF_mul_conj (h : IsAdmissible lam eta) + (Z : Finset ℂ) (m : ℂ → ℕ) : + ∑ z ∈ Z, ∑ s ∈ Z, ((m z * m s : ℕ) : ℂ) * testKernel eta (z - (starRingEnd ℂ) s) ^ 2 = + ∫ u in Set.Ioo (-lam) lam, ∫ v in Set.Ioo (-lam) lam, + bigF eta Z m u v * (starRingEnd ℂ) (bigF eta Z m u v) := by + classical + have hexp : ∀ u v : ℝ, bigF eta Z m u v * (starRingEnd ℂ) (bigF eta Z m u v) + = ∑ z ∈ Z, ∑ t ∈ Z, ((m t * m z : ℕ) : ℂ) * + (fz eta t u * (starRingEnd ℂ) (fz eta z u)) * + (fz eta t v * (starRingEnd ℂ) (fz eta z v)) := by + intro u v + simp only [bigF, map_sum, Finset.sum_mul, Finset.mul_sum, map_mul, Complex.conj_natCast, + Nat.cast_mul] + exact Finset.sum_congr rfl fun z _ => Finset.sum_congr rfl fun t _ => by ring + simp only [hexp] + rw [integral_integral_double_sum_factored Z _ (fun z t => ((m t * m z : ℕ) : ℂ)) + (fun z t u => fz eta t u * (starRingEnd ℂ) (fz eta z u)) + (fun z t => integrable_fz_mul_conj_fz h t z), Finset.sum_comm] + exact Finset.sum_congr rfl fun s _ => Finset.sum_congr rfl fun z _ => by + rw [testKernel_sub_conj h z s]; ring + +end FourierSelfConv + +/-! ### Bessel's inequality for the two-variable kernel + +The abstract inequality is `sum_sq_pairing_le_integral_norm_sq`, and the orthonormality of the +tensor squares is `integral_tensor_square_pairing`. What is left is the integrability, which is +proved here rather than assumed as a hypothesis. + +`bigF` is a finite sum of tensor products, so `memLp_tensor_two` on each summand and +`memLp_finsetSum` over `Z` does it; the basis members give their tensor squares the same way. + +Realness of the coefficients is not needed. The usual argument invokes `alphaCoeff_im_eq_zero` to +turn `|alpha_j|^2` into `alpha_j^2`; but the norm form is the stronger inequality and +`(re a)^2 ≤ ‖a‖^2` holds for every complex `a`, so the real-coefficient form follows with no appeal +to realness, no conjugation-invariance and no symmetry hypothesis. That matters practically: +`alphaCoeff_im_eq_zero` wants pointwise `IsSymmetric`, whereas a Gram--Schmidt basis is only +symmetric almost everywhere. -/ + +/-- The two-variable kernel is `L²` on the square: it is a finite sum of tensor products. -/ +theorem memLp_bigF (h : IsAdmissible lam eta) (Z : Finset ℂ) (m : ℂ → ℕ) : + MemLp (fun p : ℝ × ℝ => bigF eta Z m p.1 p.2) 2 + ((volume.restrict (Set.Ioo (-lam) lam)).prod (volume.restrict (Set.Ioo (-lam) lam))) := by + have hsum : (fun p : ℝ × ℝ => bigF eta Z m p.1 p.2) + = fun p : ℝ × ℝ => ∑ z ∈ Z, ((m z : ℂ) * fz eta z p.1) * fz eta z p.2 := rfl + rw [hsum] + exact memLp_finsetSum Z fun z _ => + memLp_tensor_two ((memLp_fz h z).const_mul (m z : ℂ)) (memLp_fz h z) + +/-- The tensor square of an `L²` element is `L²` on the square. -/ +theorem memLp_tensor_square (psi : L2Interval lam) : + MemLp (fun p : ℝ × ℝ => (psi : ℝ → ℂ) p.1 * (psi : ℝ → ℂ) p.2) 2 + ((volume.restrict (Set.Ioo (-lam) lam)).prod (volume.restrict (Set.Ioo (-lam) lam))) := + memLp_tensor_two (Lp.memLp psi) (Lp.memLp psi) + +/-- **Bessel's inequality for the kernel, in norm form.** Stronger than the real-coefficient form, +and what actually gets proved. -/ +theorem sum_norm_alphaOf_sq_le_integral_norm_bigF_sq (h : IsAdmissible lam eta) {Z : Finset ℂ} + {m : ℂ → ℕ} {N : ℕ} (psi : Fin N → L2Interval lam) (horth : Orthonormal ℂ psi) : + ∑ j, ‖alphaOf eta lam Z m (psi j)‖ ^ 2 + ≤ ∫ u in Set.Ioo (-lam) lam, ∫ v in Set.Ioo (-lam) lam, ‖bigF eta Z m u v‖ ^ 2 := by + classical + exact sum_sq_pairing_le_integral_norm_sq (bigF eta Z m) (fun j => (psi j : ℝ → ℂ)) + (memLp_bigF h Z m) (fun j => memLp_tensor_square (psi j)) + (fun j l => integral_tensor_square_pairing psi horth j l) + +/-- **Bessel's inequality for the kernel** (`lem_bessel_F`). + +Stated without conjugation-invariance of `(Z, m)` and without symmetry of the basis members: the +norm form above needs neither, and `(re a)^2 <= ‖a‖^2` delivers this from it. -/ +@[zz_tag "lem_bessel_F"] +theorem sum_alphaOf_re_sq_le_integral_norm_bigF_sq (h : IsAdmissible lam eta) {Z : Finset ℂ} + {m : ℂ → ℕ} {N : ℕ} (psi : Fin N → L2Interval lam) (horth : Orthonormal ℂ psi) : + ∑ j, (alphaOf eta lam Z m (psi j)).re ^ 2 + ≤ ∫ u in Set.Ioo (-lam) lam, ∫ v in Set.Ioo (-lam) lam, ‖bigF eta Z m u v‖ ^ 2 := by + refine le_trans (Finset.sum_le_sum fun j _ => ?_) + (sum_norm_alphaOf_sq_le_integral_norm_bigF_sq h psi horth) + have hn : ‖alphaOf eta lam Z m (psi j)‖ ^ 2 + = (alphaOf eta lam Z m (psi j)).re ^ 2 + (alphaOf eta lam Z m (psi j)).im ^ 2 := by + rw [Complex.sq_norm, Complex.normSq_apply] + ring + rw [hn] + nlinarith [sq_nonneg (alphaOf eta lam Z m (psi j)).im] + +/-! ### The rescaled multiset is conjugation-invariant + +The composite symmetry `rho ↦ 1 - conj rho` is what makes the rescaled zeros conjugation-invariant. +Both halves are available: `zeroMultiplicity_conj` for conjugation and `zeroMultiplicity_one_sub` +for the reflection. + +The computation is that conjugating a rescaled zero rescales the REFLECTED CONJUGATE: +`conj (rescale T rho) = rescale T (1 - conj rho)`. Everything else follows, and the vanishing of +zeta at `1 - conj rho` comes straight from the functional equation rather than through the +multiplicity -- `zeta (conj rho) = conj (zeta rho) = 0`, so the product form vanishes. -/ + +/-- Conjugating a rescaled point rescales the reflected conjugate. -/ +theorem conj_rescale (T : ℝ) (rho : ℂ) : + (starRingEnd ℂ) (rescale T rho) = rescale T (1 - (starRingEnd ℂ) rho) := by + simp only [rescale, map_mul, map_sub, map_one, Complex.conj_I, Complex.conj_ofReal, + map_div₀, map_ofNat] + ring + +/-- The reflected conjugate of a non-trivial zero is a non-trivial zero at the same height. -/ +theorem one_sub_conj_mem_nontrivialZeros {T : ℝ} {rho : ℂ} (hrho : rho ∈ nontrivialZeros T) : + 1 - (starRingEnd ℂ) rho ∈ nontrivialZeros T := by + obtain ⟨hzero, hre0, hre1, him0, himT⟩ := hrho + have hzc : riemannZeta ((starRingEnd ℂ) rho) = 0 := by + rw [riemannZeta_conj, hzero, map_zero] + have hne1 : (starRingEnd ℂ) rho ≠ 1 := by + intro hEq + have : ((starRingEnd ℂ) rho).re = 1 := by rw [hEq]; simp + rw [Complex.conj_re] at this + linarith + have hnen : ∀ n : ℕ, (starRingEnd ℂ) rho ≠ -(n : ℂ) := by + intro n hEq + have : ((starRingEnd ℂ) rho).re = -(n : ℝ) := by rw [hEq]; simp + rw [Complex.conj_re] at this + have : (0 : ℝ) ≤ (n : ℝ) := Nat.cast_nonneg n + linarith + refine ⟨?_, ?_, ?_, ?_, ?_⟩ + · rw [riemannZeta_one_sub hnen hne1, hzc, mul_zero] + · simp only [Complex.sub_re, Complex.one_re, Complex.conj_re] + linarith + · simp only [Complex.sub_re, Complex.one_re, Complex.conj_re] + linarith + · simpa [Complex.sub_im, Complex.conj_im] using him0 + · simpa [Complex.sub_im, Complex.conj_im] using himT + +/-- The multiplicity is unchanged by the composite symmetry. -/ +theorem zeroMultiplicity_one_sub_conj {rho : ℂ} (hre0 : 0 < rho.re) (hre1 : rho.re < 1) : + zeroMultiplicity (1 - (starRingEnd ℂ) rho) = zeroMultiplicity rho := by + have hne1 : rho ≠ 1 := fun hEq => by rw [hEq] at hre1; simp at hre1 + rw [zeroMultiplicity_one_sub (by simpa [Complex.conj_re] using hre0) + (by simpa [Complex.conj_re] using hre1), zeroMultiplicity_conj hne1] + +/-- **The rescaled multiset is conjugation-invariant** (`lem_Z_T_conj`). -/ +@[zz_tag "lem_Z_T_conj"] +theorem isConjInvariant_rescaledZerosFinset {T : ℝ} (hT : 1 < T) : + IsConjInvariant (rescaledZerosFinset T) (rescaledMult T) where + one_le := by + classical + intro z hz + simp only [rescaledZerosFinset, Finset.mem_image, Set.Finite.mem_toFinset] at hz + obtain ⟨rho, hrho, rfl⟩ := hz + rw [rescaledMult_rescale hT] + exact one_le_zeroMultiplicity hrho + conj_mem := by + classical + intro z hz + simp only [rescaledZerosFinset, Finset.mem_image, Set.Finite.mem_toFinset] at hz ⊢ + obtain ⟨rho, hrho, rfl⟩ := hz + exact ⟨1 - (starRingEnd ℂ) rho, one_sub_conj_mem_nontrivialZeros hrho, + (conj_rescale T rho).symm⟩ + mult_conj := by + classical + intro z hz + simp only [rescaledZerosFinset, Finset.mem_image, Set.Finite.mem_toFinset] at hz + obtain ⟨rho, hrho, rfl⟩ := hz + rw [conj_rescale, rescaledMult_rescale hT, rescaledMult_rescale hT, + zeroMultiplicity_one_sub_conj hrho.2.1 hrho.2.2.1] + +/-- The tensor square of an `L²` function belongs to `L²` for the product measure. -/ +theorem memLp_tensor_square_of_memLp {lam : ℝ} {f : ℝ → ℂ} + (hf : MemLp f 2 (volume.restrict (Set.Ioo (-lam) lam))) : + MemLp (fun p : ℝ × ℝ => f p.1 * f p.2) 2 + ((volume.restrict (Set.Ioo (-lam) lam)).prod + (volume.restrict (Set.Ioo (-lam) lam))) := + memLp_tensor_two hf hf + + +/-! ## The kernel second moment -/ + +/-- Expanding `F * conj F` gives the sum over ordered pairs of support points. -/ +private lemma sum_pair_fz_mul_conj_eq_bigF_mul_conj_alpha + (Z : Finset ℂ) (m : ℂ → ℕ) (u v : ℝ) : + ∑ p ∈ Z ×ˢ Z, (m p.1 * m p.2 : ℂ) * + (fz eta p.1 u * (starRingEnd ℂ) (fz eta p.2 u)) * + (fz eta p.1 v * (starRingEnd ℂ) (fz eta p.2 v)) + = bigF eta Z m u v * (starRingEnd ℂ) (bigF eta Z m u v) := by + simp only [Finset.sum_product, bigF, map_sum, map_mul, Complex.conj_natCast] + rw [Finset.sum_mul] + apply Finset.sum_congr rfl + intro z hz + rw [Finset.mul_sum] + apply Finset.sum_congr rfl + intro s hs + ring + +/-- The unconjugated finite kernel sum is the product-space second moment of `bigF`. + +The Gram identity initially conjugates the second support point; conjugation invariance permits +the change of variables back to the difference `z - s`. -/ +theorem sum_testKernel_sq_eq_integral_norm_bigF_sq + (h : IsAdmissible lam eta) (hZ : IsConjInvariant Z m) : + ∑ z ∈ Z, ∑ s ∈ Z, (m z * m s : ℂ) * testKernel eta (z - s) ^ 2 + = ∫ u in Set.Ioo (-lam) lam, ∫ v in Set.Ioo (-lam) lam, + ((‖bigF eta Z m u v‖ ^ 2 : ℝ) : ℂ) := by + rw [sum_testKernel_sq_eq_sum_conj hZ] + have hint : ∀ p ∈ Z ×ˢ Z, + Integrable + (fun u => (fz eta p.1 u * (starRingEnd ℂ) (fz eta p.2 u)) * (1 : ℂ)) + (volume.restrict (Set.Ioo (-lam) lam)) := by + intro p hp + simp only [mul_one] + change Integrable (fz eta p.1 * star (fz eta p.2)) + (volume.restrict (Set.Ioo (-lam) lam)) + exact (memLp_fz h p.1).integrable_mul (memLp_fz h p.2).star + have hfactor := integral_double_finsetSum (Z ×ˢ Z) + (fun p => (m p.1 * m p.2 : ℂ)) + (fun p u => fz eta p.1 u * (starRingEnd ℂ) (fz eta p.2 u)) + (fun _ => (1 : ℂ)) lam hint + simp only [mul_one] at hfactor + calc + (∑ z ∈ Z, ∑ s ∈ Z, + (m z * m s : ℂ) * testKernel eta (z - (starRingEnd ℂ) s) ^ 2) = + ∑ p ∈ Z ×ˢ Z, (m p.1 * m p.2 : ℂ) * + (∫ u in Set.Ioo (-lam) lam, + fz eta p.1 u * (starRingEnd ℂ) (fz eta p.2 u)) ^ 2 := by + rw [Finset.sum_product] + apply Finset.sum_congr rfl + intro z hz + apply Finset.sum_congr rfl + intro s hs + rw [testKernel_sub_conj h z s] + _ = ∫ u in Set.Ioo (-lam) lam, ∫ v in Set.Ioo (-lam) lam, + ∑ p ∈ Z ×ˢ Z, (m p.1 * m p.2 : ℂ) * + (fz eta p.1 u * (starRingEnd ℂ) (fz eta p.2 u)) * + (fz eta p.1 v * (starRingEnd ℂ) (fz eta p.2 v)) := hfactor.symm + _ = ∫ u in Set.Ioo (-lam) lam, ∫ v in Set.Ioo (-lam) lam, + bigF eta Z m u v * (starRingEnd ℂ) (bigF eta Z m u v) := by + apply integral_congr_ae + filter_upwards [] with u + apply integral_congr_ae + filter_upwards [] with v + exact sum_pair_fz_mul_conj_eq_bigF_mul_conj_alpha Z m u v + _ = ∫ u in Set.Ioo (-lam) lam, ∫ v in Set.Ioo (-lam) lam, + ((‖bigF eta Z m u v‖ ^ 2 : ℝ) : ℂ) := by + apply integral_congr_ae + filter_upwards [] with u + apply integral_congr_ae + filter_upwards [] with v + rw [Complex.mul_conj'] + norm_cast + +/-- Real-valued form of the second-moment identity, matching the right side of Bessel's +inequality. -/ +theorem sum_testKernel_sq_re_eq_integral_norm_bigF_sq + (h : IsAdmissible lam eta) (hZ : IsConjInvariant Z m) : + (∑ z ∈ Z, ∑ s ∈ Z, (m z * m s : ℂ) * testKernel eta (z - s) ^ 2).re + = ∫ u in Set.Ioo (-lam) lam, ∫ v in Set.Ioo (-lam) lam, + ‖bigF eta Z m u v‖ ^ 2 := by + rw [sum_testKernel_sq_eq_integral_norm_bigF_sq h hZ] + simp only [integral_complex_ofReal, Complex.ofReal_re] + +/-! ## The first-range coefficient estimate -/ + +/-- The real part of one coefficient expressed entirely in squared Hilbert-space pairings. + +This is the common pointwise form used by the Parseval/Bessel arguments for the three basis +ranges. Symmetry makes all the pairings real, so the real part of their complex squares is their +squared norm. -/ +theorem alphaOf_re_eq_sum_norm (h : IsAdmissible lam eta) + (hZ : IsConjInvariant Z m) {phi : L2Interval lam} + (hsym : IsSymmetricL2 phi) : + (alphaOf eta lam Z m phi).re + = (∑ x ∈ simpleRealPart Z m ∪ multipleRealPart Z m, + (m x : ℝ) * ‖inner ℂ phi (fzL2 h x)‖ ^ 2) + + ∑ z ∈ nonRealPart Z, (m z : ℝ) * + (‖inner ℂ phi (gzL2 h z)‖ ^ 2 - ‖inner ℂ phi (hzL2 h z)‖ ^ 2) := by + classical + rw [alphaOf_eq h hZ, Complex.add_re, Complex.re_sum, Complex.re_sum] + congr 1 + · refine Finset.sum_congr rfl fun x hx => ?_ + have hxre : x.im = 0 := by + rcases Finset.mem_union.mp hx with hx' | hx' + · exact ((Finset.mem_filter.mp hx').2).1 + · exact ((Finset.mem_filter.mp hx').2).1 + rw [integral_mul_conj_eq_inner_of_ae (coeFn_fzL2 h x)] + refine natCast_mul_sq_re ?_ _ + exact inner_symmetricL2_im_eq_zero hsym (fzL2_mem_symmetricSubspace h hxre) + · refine Finset.sum_congr rfl fun z _ => ?_ + have hgim : (inner ℂ phi (gzL2 h z) : ℂ).im = 0 := + inner_symmetricL2_im_eq_zero hsym (gzL2_mem_symmetricSubspace h z) + have hhim : (inner ℂ phi (hzL2 h z) : ℂ).im = 0 := + inner_symmetricL2_im_eq_zero hsym (hzL2_mem_symmetricSubspace h z) + rw [integral_mul_conj_eq_inner_of_ae (coeFn_gzL2 h z), + integral_mul_conj_eq_inner_of_ae (coeFn_hzL2 h z)] + rw [Complex.sq_norm, Complex.sq_norm, Complex.normSq_apply, Complex.normSq_apply] + simp only [Complex.mul_re, Complex.sub_re, Complex.sub_im, Complex.mul_im, sq, + Complex.natCast_re, Complex.natCast_im, hgim, hhim] + ring + +/-- **First range: lower bound for the coefficient sum** (`lem_alpha_first_lower`). + +Parseval on the initial segment spanning `U` is exact for the `f_x` at multiple real points and +the `g_z` at non-real points. Bessel bounds the subtracted `h_z` terms, leaving the unit norm +defect. Finally multiplicities are at least two on `R₂` and at least one on the support. -/ +@[zz_tag "lem_alpha_first_lower"] +theorem sum_alphaOf_re_first_lower (h : IsAdmissible lam eta) + (hZ : IsConjInvariant Z m) + {psi : Fin (Module.finrank ℂ (subspaceW h Z m)) → L2Interval lam} + (hb : IsAdaptedBasis h Z m psi) (hsym : ∀ j, IsSymmetricL2 (psi j)) : + 2 * ((multipleRealPart Z m).card : ℝ) + ((nonRealPart Z).card : ℝ) + ≤ ∑ j ∈ Finset.univ.filter (fun j : Fin (Module.finrank ℂ (subspaceW h Z m)) => + (j : ℕ) < Module.finrank ℂ (subspaceU h Z m)), + (alphaOf eta lam Z m (psi j)).re := by + classical + let s := Finset.univ.filter (fun j : Fin (Module.finrank ℂ (subspaceW h Z m)) => + (j : ℕ) < Module.finrank ℂ (subspaceU h Z m)) + have hspan : Submodule.span ℂ (psi '' (s : Set _)) = subspaceU h Z m := by + simpa [s] using hb.span_U + have hfz : ∀ x ∈ multipleRealPart Z m, + fzL2 h x ∈ Submodule.span ℂ (psi '' (s : Set _)) := by + intro x hx + rw [hspan, subspaceU] + exact Submodule.subset_span (Set.mem_union_left _ ⟨x, by simpa using hx, rfl⟩) + have hgz : ∀ z ∈ nonRealPart Z, + gzL2 h z ∈ Submodule.span ℂ (psi '' (s : Set _)) := by + intro z hz + rw [hspan, subspaceU] + exact Submodule.subset_span (Set.mem_union_right _ ⟨z, by simpa using hz, rfl⟩) + have hone_f : ∀ x ∈ multipleRealPart Z m, + ∑ j ∈ s, ‖inner ℂ (psi j) (fzL2 h x)‖ ^ 2 = 1 := by + intro x hx + rw [sum_sq_norm_inner_eq_norm_sq_of_mem_span_finset hb.orthonormal s (hfz x hx)] + exact norm_fzL2_sq_eq_one h ((Finset.mem_filter.mp hx).2).1 + have hg_parseval : ∀ z ∈ nonRealPart Z, + ∑ j ∈ s, ‖inner ℂ (psi j) (gzL2 h z)‖ ^ 2 = ‖gzL2 h z‖ ^ 2 := by + intro z hz + exact sum_sq_norm_inner_eq_norm_sq_of_mem_span_finset hb.orthonormal s (hgz z hz) + have hh_bessel : ∀ z : ℂ, + ∑ j ∈ s, ‖inner ℂ (psi j) (hzL2 h z)‖ ^ 2 ≤ ‖hzL2 h z‖ ^ 2 := by + intro z + exact hb.orthonormal.sum_inner_products_le (s := s) (hzL2 h z) + rw [show (∑ j ∈ Finset.univ.filter + (fun j : Fin (Module.finrank ℂ (subspaceW h Z m)) => + (j : ℕ) < Module.finrank ℂ (subspaceU h Z m)), + (alphaOf eta lam Z m (psi j)).re) = + ∑ j ∈ s, (alphaOf eta lam Z m (psi j)).re by rfl] + simp_rw [alphaOf_re_eq_sum_norm h hZ (hsym _)] + rw [Finset.sum_add_distrib, Finset.sum_comm, Finset.sum_comm (s := s) (t := nonRealPart Z)] + have hR1nonneg : 0 ≤ ∑ x ∈ simpleRealPart Z m, + ∑ j ∈ s, (m x : ℝ) * ‖inner ℂ (psi j) (fzL2 h x)‖ ^ 2 := by positivity + rw [Finset.sum_union disjoint_simpleRealPart_multipleRealPart] + have hR2 : ∑ x ∈ multipleRealPart Z m, + ∑ j ∈ s, (m x : ℝ) * ‖inner ℂ (psi j) (fzL2 h x)‖ ^ 2 + = ∑ x ∈ multipleRealPart Z m, (m x : ℝ) := by + refine Finset.sum_congr rfl fun x hx => ?_ + rw [← Finset.mul_sum, hone_f x hx, mul_one] + rw [hR2] + have hS : ∑ z ∈ nonRealPart Z, + ∑ j ∈ s, (m z : ℝ) * + (‖inner ℂ (psi j) (gzL2 h z)‖ ^ 2 - ‖inner ℂ (psi j) (hzL2 h z)‖ ^ 2) + ≥ ∑ z ∈ nonRealPart Z, (m z : ℝ) := by + refine Finset.sum_le_sum fun z hz => ?_ + rw [← Finset.mul_sum, Finset.sum_sub_distrib, hg_parseval z hz] + have hm : (0 : ℝ) ≤ (m z : ℝ) := by positivity + have hd := hh_bessel z + have hdef := norm_gzL2_sq_sub_norm_hzL2_sq h z + nlinarith + have hR2card : 2 * ((multipleRealPart Z m).card : ℝ) + ≤ ∑ x ∈ multipleRealPart Z m, (m x : ℝ) := by + calc + 2 * ((multipleRealPart Z m).card : ℝ) + = ∑ _x ∈ multipleRealPart Z m, (2 : ℝ) := by simp [mul_comm] + _ ≤ ∑ x ∈ multipleRealPart Z m, (m x : ℝ) := by + refine Finset.sum_le_sum fun x hx => ?_ + exact_mod_cast ((Finset.mem_filter.mp hx).2).2 + have hScard : ((nonRealPart Z).card : ℝ) + ≤ ∑ z ∈ nonRealPart Z, (m z : ℝ) := by + calc + ((nonRealPart Z).card : ℝ) = ∑ _z ∈ nonRealPart Z, (1 : ℝ) := by simp + _ ≤ ∑ z ∈ nonRealPart Z, (m z : ℝ) := by + refine Finset.sum_le_sum fun z hz => ?_ + exact_mod_cast hZ.one_le z (Finset.mem_filter.mp hz).1 + linarith + +/-! ## Three-range algebra -/ + +/-- The numerical three-range estimate used for the simple-real-point bound. + +The first range uses `a² + 4 ≥ 4a`, the middle range uses `a² + 1 ≥ 2a`, and the +last range uses `a ≤ 0`. -/ +theorem three_range_simple {N dU dV R1 R2 S : ℕ} (a : Fin N → ℝ) + (hdUV : dU ≤ dV) (hdVN : dV ≤ N) + (hdU : dU ≤ R2 + S / 2) (hdGap : dV ≤ dU + R1) + (hfirst : 2 * (R2 : ℝ) + (S : ℝ) ≤ + ∑ j ∈ Finset.univ.filter (fun j : Fin N => (j : ℕ) < dU), a j) + (hthird : ∀ j : Fin N, dV ≤ (j : ℕ) → a j ≤ 0) : + 2 * ∑ j, a j - (R1 : ℝ) ≤ ∑ j, a j ^ 2 := by + classical + let A := Finset.univ.filter (fun j : Fin N => (j : ℕ) < dU) + let B := Finset.univ.filter (fun j : Fin N => dU ≤ (j : ℕ) ∧ (j : ℕ) < dV) + let C := Finset.univ.filter (fun j : Fin N => dV ≤ (j : ℕ)) + have hAB : Disjoint A B := by + refine Finset.disjoint_left.mpr ?_ + intro j hjA hjB + simp only [A, Finset.mem_filter, Finset.mem_univ, true_and] at hjA + simp only [B, Finset.mem_filter, Finset.mem_univ, true_and] at hjB + omega + have hABC : Disjoint (A ∪ B) C := by + refine Finset.disjoint_left.mpr ?_ + intro j hjAB hjC + rw [Finset.mem_union] at hjAB + simp only [C, Finset.mem_filter, Finset.mem_univ, true_and] at hjC + rcases hjAB with hjA | hjB + · simp only [A, Finset.mem_filter, Finset.mem_univ, true_and] at hjA + omega + · simp only [B, Finset.mem_filter, Finset.mem_univ, true_and] at hjB + omega + have hcover : A ∪ B ∪ C = Finset.univ := by + ext j + simp only [A, B, C, Finset.mem_union, Finset.mem_filter, Finset.mem_univ, + true_and, iff_true] + omega + have hABeq : A ∪ B = Finset.univ.filter (fun j : Fin N => (j : ℕ) < dV) := by + ext j + simp only [A, B, Finset.mem_union, Finset.mem_filter, Finset.mem_univ, true_and] + omega + have hcardA : A.card = dU := by + rw [show A = Finset.univ.filter (fun j : Fin N => (j : ℕ) < dU) from rfl, + Fin.card_filter_val_lt, Nat.min_eq_right (le_trans hdUV hdVN)] + have hcardAB : (A ∪ B).card = dV := by + rw [hABeq, Fin.card_filter_val_lt, Nat.min_eq_right hdVN] + have hcardB_le : B.card ≤ R1 := by + rw [Finset.card_union_of_disjoint hAB, hcardA] at hcardAB + omega + have htwodim_nat : 2 * dU ≤ 2 * R2 + S := by + omega + have htwodim : 2 * (dU : ℝ) ≤ 2 * (R2 : ℝ) + (S : ℝ) := by + exact_mod_cast htwodim_nat + have hsqA_base : + 4 * (∑ j ∈ A, a j) - 4 * (A.card : ℝ) ≤ ∑ j ∈ A, a j ^ 2 := by + calc + 4 * (∑ j ∈ A, a j) - 4 * (A.card : ℝ) + = ∑ j ∈ A, (4 * a j - 4) := by simp [Finset.mul_sum]; ring + _ ≤ ∑ j ∈ A, a j ^ 2 := by + refine Finset.sum_le_sum fun j hj => ?_ + nlinarith [sq_nonneg (a j - 2)] + have hsqA : 2 * (∑ j ∈ A, a j) ≤ ∑ j ∈ A, a j ^ 2 := by + rw [hcardA] at hsqA_base + have hfirstA : 2 * (R2 : ℝ) + (S : ℝ) ≤ ∑ j ∈ A, a j := by + exact hfirst + nlinarith + have hsqB_base : + 2 * (∑ j ∈ B, a j) - (B.card : ℝ) ≤ ∑ j ∈ B, a j ^ 2 := by + calc + 2 * (∑ j ∈ B, a j) - (B.card : ℝ) + = ∑ j ∈ B, (2 * a j - 1) := by simp [Finset.mul_sum] + _ ≤ ∑ j ∈ B, a j ^ 2 := by + refine Finset.sum_le_sum fun j hj => ?_ + nlinarith [sq_nonneg (a j - 1)] + have hcardB_real : (B.card : ℝ) ≤ (R1 : ℝ) := by exact_mod_cast hcardB_le + have hsqB : 2 * (∑ j ∈ B, a j) - (R1 : ℝ) ≤ ∑ j ∈ B, a j ^ 2 := by + linarith + have hsqC : 2 * (∑ j ∈ C, a j) ≤ ∑ j ∈ C, a j ^ 2 := by + calc + 2 * (∑ j ∈ C, a j) = ∑ j ∈ C, 2 * a j := by rw [Finset.mul_sum] + _ ≤ ∑ j ∈ C, a j ^ 2 := by + refine Finset.sum_le_sum fun j hj => ?_ + have hj' : dV ≤ (j : ℕ) := by simpa [C] using hj + have ha := hthird j hj' + nlinarith [sq_nonneg (a j)] + have hsum (f : Fin N → ℝ) : + ∑ j, f j = (∑ j ∈ A, f j) + (∑ j ∈ B, f j) + ∑ j ∈ C, f j := by + rw [← hcover, Finset.sum_union hABC, Finset.sum_union hAB] + rw [hsum a, hsum (fun j => a j ^ 2)] + linarith + +/-- **Lower bound for the simple real elements** (`prop_simple_real_lower`). -/ +@[zz_tag "prop_simple_real_lower"] +theorem card_simpleRealPart_lower (h : IsAdmissible lam eta) + (hZ : IsConjInvariant Z m) : + 2 * (∑ z ∈ Z, (m z : ℝ)) + - (∑ z ∈ Z, ∑ s ∈ Z, + (m z * m s : ℂ) * testKernel eta (z - s) ^ 2).re + ≤ ((simpleRealPart Z m).card : ℝ) := by + classical + obtain ⟨psi, hb, hsym⟩ := exists_symmetric_adapted_basis h Z m + let a : Fin (Module.finrank ℂ (subspaceW h Z m)) → ℝ := + fun j => (alphaOf eta lam Z m (psi j)).re + have hdUV : Module.finrank ℂ (subspaceU h Z m) ≤ + Module.finrank ℂ (subspaceV h Z m) := + Submodule.finrank_mono (subspaceU_le_subspaceV h Z m) + have hdVN : Module.finrank ℂ (subspaceV h Z m) ≤ + Module.finrank ℂ (subspaceW h Z m) := + Submodule.finrank_mono (subspaceV_le_subspaceW h Z m) + have hrange := three_range_simple a hdUV hdVN + (finrank_subspaceU_le h hZ) (finrank_subspaceV_le h Z m) + (sum_alphaOf_re_first_lower h hZ hb hsym) + (fun j hj => alphaOf_re_nonpos h hZ hb (hsym j) hj) + have htotal := sum_alphaOf_re_eq_sum_mult h hZ hb hsym + have hbessel := sum_alphaOf_re_sq_le_integral_norm_bigF_sq (Z := Z) (m := m) + h psi hb.orthonormal + have hmoment := sum_testKernel_sq_re_eq_integral_norm_bigF_sq h hZ + change 2 * (∑ j, a j) - ((simpleRealPart Z m).card : ℝ) + ≤ ∑ j, a j ^ 2 at hrange + change ∑ j, a j = ∑ z ∈ Z, (m z : ℝ) at htotal + change ∑ j, a j ^ 2 ≤ _ at hbessel + rw [← hmoment] at hbessel + linarith + +/-- The numerical three-range estimate used for the distinct-point bound. + +Compared with `three_range_simple`, all three ranges retain a factor four. On the middle range, +the upper bound on its coefficient sum supplies the extra factor two. -/ +theorem three_range_distinct {N dU dV R1 R2 S : ℕ} (a : Fin N → ℝ) + (hdUV : dU ≤ dV) (hdVN : dV ≤ N) + (hdU : dU ≤ R2 + S / 2) (hdGap : dV ≤ dU + R1) + (hsecond : + ∑ j ∈ Finset.univ.filter + (fun j : Fin N => dU ≤ (j : ℕ) ∧ (j : ℕ) < dV), a j + ≤ (R1 : ℝ)) + (hthird : ∀ j : Fin N, dV ≤ (j : ℕ) → a j ≤ 0) + (hmult : (R1 : ℝ) + 2 * (R2 : ℝ) + (S : ℝ) ≤ ∑ j, a j) : + 3 * ∑ j, a j - 2 * ((R1 : ℝ) + (R2 : ℝ) + (S : ℝ)) + ≤ ∑ j, a j ^ 2 := by + classical + let A := Finset.univ.filter (fun j : Fin N => (j : ℕ) < dU) + let B := Finset.univ.filter (fun j : Fin N => dU ≤ (j : ℕ) ∧ (j : ℕ) < dV) + let C := Finset.univ.filter (fun j : Fin N => dV ≤ (j : ℕ)) + have hAB : Disjoint A B := by + refine Finset.disjoint_left.mpr ?_ + intro j hjA hjB + simp only [A, Finset.mem_filter, Finset.mem_univ, true_and] at hjA + simp only [B, Finset.mem_filter, Finset.mem_univ, true_and] at hjB + omega + have hABC : Disjoint (A ∪ B) C := by + refine Finset.disjoint_left.mpr ?_ + intro j hjAB hjC + rw [Finset.mem_union] at hjAB + simp only [C, Finset.mem_filter, Finset.mem_univ, true_and] at hjC + rcases hjAB with hjA | hjB + · simp only [A, Finset.mem_filter, Finset.mem_univ, true_and] at hjA + omega + · simp only [B, Finset.mem_filter, Finset.mem_univ, true_and] at hjB + omega + have hcover : A ∪ B ∪ C = Finset.univ := by + ext j + simp only [A, B, C, Finset.mem_union, Finset.mem_filter, Finset.mem_univ, + true_and, iff_true] + omega + have hABeq : A ∪ B = Finset.univ.filter (fun j : Fin N => (j : ℕ) < dV) := by + ext j + simp only [A, B, Finset.mem_union, Finset.mem_filter, Finset.mem_univ, true_and] + omega + have hcardA : A.card = dU := by + rw [show A = Finset.univ.filter (fun j : Fin N => (j : ℕ) < dU) from rfl, + Fin.card_filter_val_lt, Nat.min_eq_right (le_trans hdUV hdVN)] + have hcardAB : (A ∪ B).card = dV := by + rw [hABeq, Fin.card_filter_val_lt, Nat.min_eq_right hdVN] + have hcardB_le : B.card ≤ R1 := by + rw [Finset.card_union_of_disjoint hAB, hcardA] at hcardAB + omega + have hfourdim_nat : 4 * dU ≤ 4 * R2 + 2 * S := by + omega + have hfourdim : 4 * (dU : ℝ) ≤ 4 * (R2 : ℝ) + 2 * (S : ℝ) := by + exact_mod_cast hfourdim_nat + have hsqA_base : + 4 * (∑ j ∈ A, a j) - 4 * (A.card : ℝ) ≤ ∑ j ∈ A, a j ^ 2 := by + calc + 4 * (∑ j ∈ A, a j) - 4 * (A.card : ℝ) + = ∑ j ∈ A, (4 * a j - 4) := by simp [Finset.mul_sum]; ring + _ ≤ ∑ j ∈ A, a j ^ 2 := by + refine Finset.sum_le_sum fun j hj => ?_ + nlinarith [sq_nonneg (a j - 2)] + have hsqA : + 4 * (∑ j ∈ A, a j) - 4 * (R2 : ℝ) - 2 * (S : ℝ) + ≤ ∑ j ∈ A, a j ^ 2 := by + rw [hcardA] at hsqA_base + linarith + have hsqB_base : + 2 * (∑ j ∈ B, a j) - (B.card : ℝ) ≤ ∑ j ∈ B, a j ^ 2 := by + calc + 2 * (∑ j ∈ B, a j) - (B.card : ℝ) + = ∑ j ∈ B, (2 * a j - 1) := by simp [Finset.mul_sum] + _ ≤ ∑ j ∈ B, a j ^ 2 := by + refine Finset.sum_le_sum fun j hj => ?_ + nlinarith [sq_nonneg (a j - 1)] + have hcardB_real : (B.card : ℝ) ≤ (R1 : ℝ) := by exact_mod_cast hcardB_le + have hsecondB : (∑ j ∈ B, a j) ≤ (R1 : ℝ) := by exact hsecond + have hsqB : 4 * (∑ j ∈ B, a j) - 3 * (R1 : ℝ) + ≤ ∑ j ∈ B, a j ^ 2 := by + linarith + have hsqC : 4 * (∑ j ∈ C, a j) ≤ ∑ j ∈ C, a j ^ 2 := by + calc + 4 * (∑ j ∈ C, a j) = ∑ j ∈ C, 4 * a j := by rw [Finset.mul_sum] + _ ≤ ∑ j ∈ C, a j ^ 2 := by + refine Finset.sum_le_sum fun j hj => ?_ + have hj' : dV ≤ (j : ℕ) := by simpa [C] using hj + have ha := hthird j hj' + nlinarith [sq_nonneg (a j)] + have hsum (f : Fin N → ℝ) : + ∑ j, f j = (∑ j ∈ A, f j) + (∑ j ∈ B, f j) + ∑ j ∈ C, f j := by + rw [← hcover, Finset.sum_union hABC, Finset.sum_union hAB] + rw [hsum a] at hmult ⊢ + rw [hsum (fun j => a j ^ 2)] + linarith + +/-- **Lower bound for the distinct elements** (`prop_distinct_lower`). -/ +@[zz_tag "prop_distinct_lower"] +theorem card_lower (h : IsAdmissible lam eta) + (hZ : IsConjInvariant Z m) : + (3 / 2 : ℝ) * (∑ z ∈ Z, (m z : ℝ)) + - (1 / 2 : ℝ) * + (∑ z ∈ Z, ∑ s ∈ Z, + (m z * m s : ℂ) * testKernel eta (z - s) ^ 2).re + ≤ (Z.card : ℝ) := by + classical + obtain ⟨psi, hb, hsym⟩ := exists_symmetric_adapted_basis h Z m + let a : Fin (Module.finrank ℂ (subspaceW h Z m)) → ℝ := + fun j => (alphaOf eta lam Z m (psi j)).re + have hdUV : Module.finrank ℂ (subspaceU h Z m) ≤ + Module.finrank ℂ (subspaceV h Z m) := + Submodule.finrank_mono (subspaceU_le_subspaceV h Z m) + have hdVN : Module.finrank ℂ (subspaceV h Z m) ≤ + Module.finrank ℂ (subspaceW h Z m) := + Submodule.finrank_mono (subspaceV_le_subspaceW h Z m) + have hR1mult : ((simpleRealPart Z m).card : ℝ) = + ∑ x ∈ simpleRealPart Z m, (m x : ℝ) := by + calc + ((simpleRealPart Z m).card : ℝ) = + ∑ _x ∈ simpleRealPart Z m, (1 : ℝ) := by simp + _ = ∑ x ∈ simpleRealPart Z m, (m x : ℝ) := by + refine Finset.sum_congr rfl fun x hx => ?_ + exact_mod_cast ((Finset.mem_filter.mp hx).2).2.symm + have hR2mult : 2 * ((multipleRealPart Z m).card : ℝ) ≤ + ∑ x ∈ multipleRealPart Z m, (m x : ℝ) := by + calc + 2 * ((multipleRealPart Z m).card : ℝ) = + ∑ _x ∈ multipleRealPart Z m, (2 : ℝ) := by simp [mul_comm] + _ ≤ ∑ x ∈ multipleRealPart Z m, (m x : ℝ) := by + refine Finset.sum_le_sum fun x hx => ?_ + exact_mod_cast ((Finset.mem_filter.mp hx).2).2 + have hSmult : ((nonRealPart Z).card : ℝ) ≤ + ∑ z ∈ nonRealPart Z, (m z : ℝ) := by + calc + ((nonRealPart Z).card : ℝ) = + ∑ _z ∈ nonRealPart Z, (1 : ℝ) := by simp + _ ≤ ∑ z ∈ nonRealPart Z, (m z : ℝ) := by + refine Finset.sum_le_sum fun z hz => ?_ + exact_mod_cast hZ.one_le z (Finset.mem_filter.mp hz).1 + have hmult : ((simpleRealPart Z m).card : ℝ) + + 2 * ((multipleRealPart Z m).card : ℝ) + ((nonRealPart Z).card : ℝ) + ≤ ∑ z ∈ Z, (m z : ℝ) := by + calc + ((simpleRealPart Z m).card : ℝ) + + 2 * ((multipleRealPart Z m).card : ℝ) + ((nonRealPart Z).card : ℝ) + ≤ (∑ x ∈ simpleRealPart Z m, (m x : ℝ)) + + (∑ x ∈ multipleRealPart Z m, (m x : ℝ)) + + ∑ z ∈ nonRealPart Z, (m z : ℝ) := by linarith + _ = ∑ z ∈ Z, (m z : ℝ) := by + rw [← Finset.sum_union disjoint_simpleRealPart_multipleRealPart, + ← Finset.sum_union disjoint_realPart_nonRealPart, + union_realPart_nonRealPart hZ] + have htotal := sum_alphaOf_re_eq_sum_mult h hZ hb hsym + change ∑ j, a j = ∑ z ∈ Z, (m z : ℝ) at htotal + have hmult_a : ((simpleRealPart Z m).card : ℝ) + + 2 * ((multipleRealPart Z m).card : ℝ) + ((nonRealPart Z).card : ℝ) + ≤ ∑ j, a j := by + rw [htotal] + exact hmult + have hrange := three_range_distinct a hdUV hdVN + (finrank_subspaceU_le h hZ) (finrank_subspaceV_le h Z m) + (sum_alphaOf_re_le_card_simpleRealPart h hZ hb hsym) + (fun j hj => alphaOf_re_nonpos h hZ hb (hsym j) hj) hmult_a + have hbessel := sum_alphaOf_re_sq_le_integral_norm_bigF_sq (Z := Z) (m := m) + h psi hb.orthonormal + have hmoment := sum_testKernel_sq_re_eq_integral_norm_bigF_sq h hZ + have hcardNat : (simpleRealPart Z m).card + (multipleRealPart Z m).card + + (nonRealPart Z).card = Z.card := by + rw [← Finset.card_union_of_disjoint disjoint_simpleRealPart_multipleRealPart, + ← Finset.card_union_of_disjoint disjoint_realPart_nonRealPart, + union_realPart_nonRealPart hZ] + have hcard : ((simpleRealPart Z m).card : ℝ) + + ((multipleRealPart Z m).card : ℝ) + ((nonRealPart Z).card : ℝ) + = (Z.card : ℝ) := by exact_mod_cast hcardNat + change 3 * (∑ j, a j) - + 2 * (((simpleRealPart Z m).card : ℝ) + + ((multipleRealPart Z m).card : ℝ) + ((nonRealPart Z).card : ℝ)) + ≤ ∑ j, a j ^ 2 at hrange + change ∑ j, a j ^ 2 ≤ _ at hbessel + rw [← hmoment] at hbessel + linarith + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Hilbert/Basis.lean b/LeanPool/ZetaZeros/Hilbert/Basis.lean new file mode 100644 index 0000000000..7f926eee85 --- /dev/null +++ b/LeanPool/ZetaZeros/Hilbert/Basis.lean @@ -0,0 +1,73 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public import LeanPool.ZetaZeros.Hilbert.Subspaces + +/-! +# Adapted orthonormal bases and the Bessel coefficients + +An orthonormal basis of `W` whose initial segments span `U` and `V`, and the coefficients of the +two-variable kernel against its tensor squares. + +The three subspaces are finite-dimensional — each is spanned by a finite family — which is what +makes `Module.finrank` the right index bound and Gram–Schmidt applicable. +-/ + +@[expose] public section + +namespace ZetaZeros + +open MeasureTheory + +variable {lam : ℝ} {eta : ℝ → ℝ} + +instance finiteDimensional_subspaceU (h : IsAdmissible lam eta) (Z : Finset ℂ) (m : ℂ → ℕ) : + FiniteDimensional ℂ (subspaceU h Z m) := + FiniteDimensional.span_of_finite ℂ + (((Finset.finite_toSet _).image _).union ((Finset.finite_toSet _).image _)) + +instance finiteDimensional_subspaceW (h : IsAdmissible lam eta) (Z : Finset ℂ) (m : ℂ → ℕ) : + FiniteDimensional ℂ (subspaceW h Z m) := + FiniteDimensional.span_of_finite ℂ + ((((Finset.finite_toSet _).image _).union ((Finset.finite_toSet _).image _)).union + ((Finset.finite_toSet _).image _)) + +instance finiteDimensional_subspaceV (h : IsAdmissible lam eta) (Z : Finset ℂ) (m : ℂ → ℕ) : + FiniteDimensional ℂ (subspaceV h Z m) := + FiniteDimensional.span_of_finite ℂ + (((Finset.finite_toSet _).image _).union ((Finset.finite_toSet _).image _)) + +/-- A tuple is an *adapted orthonormal basis*: orthonormal, spanning `W`, and with the initial +segments of lengths `dim U` and `dim V` spanning `U` and `V`. The nesting proved in +`subspaceU_le_subspaceV` is what makes such a tuple possible. -/ +@[zz_tag "def_adapted_basis"] +structure IsAdaptedBasis (h : IsAdmissible lam eta) (Z : Finset ℂ) (m : ℂ → ℕ) + (psi : Fin (Module.finrank ℂ (subspaceW h Z m)) → L2Interval lam) : Prop where + /-- The tuple is orthonormal. -/ + orthonormal : Orthonormal ℂ psi + /-- It spans `W`. -/ + span_W : Submodule.span ℂ (Set.range psi) = subspaceW h Z m + /-- Its first `dim U` members span `U`. -/ + span_U : Submodule.span ℂ + (psi '' {j | (j : ℕ) < Module.finrank ℂ (subspaceU h Z m)}) = subspaceU h Z m + /-- Its first `dim V` members span `V`. -/ + span_V : Submodule.span ℂ + (psi '' {j | (j : ℕ) < Module.finrank ℂ (subspaceV h Z m)}) = subspaceV h Z m + +/-- The Bessel coefficient of a single `L²` element: the coefficient of the two-variable kernel +against its tensor square. -/ +@[zz_tag "def_alpha"] +noncomputable def alphaOf (eta : ℝ → ℝ) (lam : ℝ) (Z : Finset ℂ) (m : ℂ → ℕ) + (psi : L2Interval lam) : ℂ := + ∫ u in Set.Ioo (-lam) lam, ∫ v in Set.Ioo (-lam) lam, + bigF eta Z m u v * (starRingEnd ℂ) ((psi : ℝ → ℂ) u * (psi : ℝ → ℂ) v) + +/-- The indexed form of `alphaOf`. -/ +noncomputable def alphaCoeff (eta : ℝ → ℝ) (lam : ℝ) (Z : Finset ℂ) (m : ℂ → ℕ) + (psi : ℕ → L2Interval lam) (j : ℕ) : ℂ := alphaOf eta lam Z m (psi j) + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Hilbert/Defs.lean b/LeanPool/ZetaZeros/Hilbert/Defs.lean new file mode 100644 index 0000000000..fd8734076b --- /dev/null +++ b/LeanPool/ZetaZeros/Hilbert/Defs.lean @@ -0,0 +1,87 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public import LeanPool.ZetaZeros.Defs +public import LeanPool.ZetaZeros.Meta.Attr + +/-! +# The vocabulary of the Hilbert space inequality + +The objects of the source's key proposition, which speaks only of a finite conjugation-invariant +multiset of complex numbers and a test function — no zeta function appears. + +A multiset is presented by its finite support `Z` together with a multiplicity function `m`, so +every sum is written over a finite set with explicit weights. That is what removes the source's +bookkeeping over conjugate pairs: because `gz` is conjugation-invariant and `hz` anti-invariant, a +sum over the whole non-real part equals twice a sum over representatives, with no choice of +representatives to make. +-/ + +@[expose] public section + +namespace ZetaZeros + +/-- The Fourier transform of a compactly supported real function, at a complex argument. -/ +@[zz_tag "def_f_z"] +noncomputable def fz (eta : ℝ → ℝ) (z : ℂ) (u : ℝ) : ℂ := + (eta u : ℂ) * Complex.exp (-(2 * (Real.pi : ℂ)) * Complex.I * (u : ℂ) * z) + +/-- The even part of the twisted pair, `gz = (fz z + fz (conj z)) / 2`. -/ +@[zz_tag "def_g_z"] +noncomputable def gz (eta : ℝ → ℝ) (z : ℂ) (u : ℝ) : ℂ := + (fz eta z u + fz eta ((starRingEnd ℂ) z) u) / 2 + +/-- The odd part of the twisted pair, `hz = (fz z - fz (conj z)) / (2i)`. -/ +@[zz_tag "def_h_z"] +noncomputable def hz (eta : ℝ → ℝ) (z : ℂ) (u : ℝ) : ℂ := + (fz eta z u - fz eta ((starRingEnd ℂ) z) u) / (2 * Complex.I) + +/-- A function `ℝ → ℂ` is symmetric when conjugation acts as reflection: `conj (Φ u) = Φ (-u)`. +The property is preserved by Gram–Schmidt and is what makes the Bessel coefficients real. -/ +@[zz_tag "def_symmetric"] +def IsSymmetric (Φ : ℝ → ℂ) : Prop := ∀ u : ℝ, (starRingEnd ℂ) (Φ u) = Φ (-u) + +/-- The two-variable kernel `F (u, v) = ∑ z, m z * fz z u * fz z v`, the multiset sum written with +explicit multiplicities. -/ +@[zz_tag "def_F"] +noncomputable def bigF (eta : ℝ → ℝ) (Z : Finset ℂ) (m : ℂ → ℕ) (u v : ℝ) : ℂ := + ∑ z ∈ Z, (m z : ℂ) * fz eta z u * fz eta z v + +/-- The simple real part of the support: real points of multiplicity one. -/ +@[zz_tag "def_R2"] +noncomputable def multipleRealPart (Z : Finset ℂ) (m : ℂ → ℕ) : Finset ℂ := + Z.filter fun x => x.im = 0 ∧ 2 ≤ m x + +/-- The non-real part of the support. -/ +@[zz_tag "def_S"] +noncomputable def nonRealPart (Z : Finset ℂ) : Finset ℂ := + Z.filter fun z => z.im ≠ 0 + +/-- The even part is conjugation-invariant. -/ +@[zz_tag "lem_g_conj"] +theorem gz_conj (eta : ℝ → ℝ) (z : ℂ) : gz eta ((starRingEnd ℂ) z) = gz eta z := by + funext u + simp only [gz, Complex.conj_conj] + ring + +/-- The odd part is conjugation-anti-invariant. -/ +@[zz_tag "lem_h_conj"] +theorem hz_conj (eta : ℝ → ℝ) (z : ℂ) : + hz eta ((starRingEnd ℂ) z) = fun u => -hz eta z u := by + funext u + simp only [hz, Complex.conj_conj] + ring + +/-- The twisted function splits into its even and odd parts: `fz = gz + i * hz`. -/ +@[zz_tag "lem_f_decomp"] +theorem fz_eq_gz_add_I_mul_hz (eta : ℝ → ℝ) (z : ℂ) (u : ℝ) : + fz eta z u = gz eta z u + Complex.I * hz eta z u := by + simp only [gz, hz] + field_simp + ring + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Hilbert/Dimensions.lean b/LeanPool/ZetaZeros/Hilbert/Dimensions.lean new file mode 100644 index 0000000000..eb2946d8c4 --- /dev/null +++ b/LeanPool/ZetaZeros/Hilbert/Dimensions.lean @@ -0,0 +1,83 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public import LeanPool.ZetaZeros.Hilbert.Basis + +/-! +# The dimension gap between the first two subspaces + +`dim V - dim U ≤ |R₁|`: passing from `U` to `V` adds only the twisted functions at the simple real +points, one per point, so the dimension can grow by at most their number. + +This is the bound that turns the source's second-range estimate into a statement about the count of +simple real elements — the quantity the whole proposition is about. +-/ + +@[expose] public section + +namespace ZetaZeros + +open Module + +variable {lam : ℝ} {eta : ℝ → ℝ} + +/-- `finrank (p ⊔ q) ≤ finrank p + finrank q` for finitely-generated submodules of an arbitrary +module. Mathlib's `finrank_sup_add_finrank_inf_eq` needs the *ambient* module finite-dimensional, +which `L²` is not, so the bound is built here from the surjection `p × q → p ⊔ q`. -/ +theorem finrank_sup_le {M : Type*} [AddCommGroup M] [Module ℂ M] (p q : Submodule ℂ M) + [FiniteDimensional ℂ p] [FiniteDimensional ℂ q] : + finrank ℂ (p ⊔ q : Submodule ℂ M) ≤ finrank ℂ p + finrank ℂ q := by + classical + let f : (p × q) →ₗ[ℂ] (p ⊔ q : Submodule ℂ M) := + { toFun := fun x => ⟨(x.1 : M) + (x.2 : M), + Submodule.add_mem_sup x.1.2 x.2.2⟩ + map_add' := by intro x y; ext; simp; abel + map_smul' := by intro c x; ext; simp [smul_add] } + have hsurj : Function.Surjective f := by + intro y + obtain ⟨a, ha, b, hb, hab⟩ := Submodule.mem_sup.mp y.2 + exact ⟨(⟨a, ha⟩, ⟨b, hb⟩), Subtype.ext (by simpa [f] using hab)⟩ + have := LinearMap.finrank_le_finrank_of_surjective (f := f) hsurj + simpa [Module.finrank_prod] using this + +/-- `V` is `U` together with the span of the twisted functions at the simple real points. -/ +private lemma subspaceV_eq_sup (h : IsAdmissible lam eta) (Z : Finset ℂ) (m : ℂ → ℕ) : + subspaceV h Z m + = Submodule.span ℂ (fzL2 h '' (simpleRealPart Z m : Set ℂ)) ⊔ subspaceU h Z m := by + rw [subspaceV, subspaceU, ← Submodule.span_union] + congr 1 + rw [Finset.coe_union, Set.image_union] + ext x + simp only [Set.mem_union] + tauto + +/-- **The dimension gap.** Passing from `U` to `V` costs at most one dimension per simple real +point. -/ +@[zz_tag "lem_dim_V_minus_U"] +theorem finrank_subspaceV_le (h : IsAdmissible lam eta) (Z : Finset ℂ) (m : ℂ → ℕ) : + finrank ℂ (subspaceV h Z m) + ≤ finrank ℂ (subspaceU h Z m) + (simpleRealPart Z m).card := by + classical + have hsup := subspaceV_eq_sup h Z m + have : FiniteDimensional ℂ + (Submodule.span ℂ (fzL2 h '' (simpleRealPart Z m : Set ℂ))) := + FiniteDimensional.span_of_finite ℂ ((Finset.finite_toSet _).image _) + have hle : finrank ℂ (subspaceV h Z m) + ≤ finrank ℂ (Submodule.span ℂ (fzL2 h '' (simpleRealPart Z m : Set ℂ))) + + finrank ℂ (subspaceU h Z m) := by + rw [hsup] + exact finrank_sup_le _ _ + have hcard : finrank ℂ (Submodule.span ℂ (fzL2 h '' (simpleRealPart Z m : Set ℂ))) + ≤ (simpleRealPart Z m).card := by + have himg : fzL2 h '' (simpleRealPart Z m : Set ℂ) + = (((simpleRealPart Z m).image (fzL2 h) : Finset (L2Interval lam)) : Set _) := by + rw [Finset.coe_image] + rw [himg] + exact le_trans (finrank_span_finset_le_card _) Finset.card_image_le + omega + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Hilbert/FIdentity.lean b/LeanPool/ZetaZeros/Hilbert/FIdentity.lean new file mode 100644 index 0000000000..4aeb9f4866 --- /dev/null +++ b/LeanPool/ZetaZeros/Hilbert/FIdentity.lean @@ -0,0 +1,110 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public import LeanPool.ZetaZeros.Hilbert.Defs + +/-! +# The two-variable kernel in terms of even and odd parts + +The algebraic heart of `alphaCoeff_eq`: rewriting `F` so that the non-real points contribute +`g_z(u) g_z(v) - h_z(u) h_z(v)` rather than `f_z(u) f_z(v)`. + +The rewrite is **not** termwise — `f_z(u) f_z(v)` exceeds `g_z(u)g_z(v) - h_z(u)h_z(v)` by the cross +term `i(g_z(u)h_z(v) + h_z(u)g_z(v))`. What makes it true is that conjugation is a fixed-point-free +involution of the non-real part under which the cross term is *odd*, since `gz` is +conjugation-invariant and `hz` anti-invariant. So the cross terms cancel in pairs and +`Finset.sum_involution` kills the whole sum at once. + +This is the step that lets the source's factor of two over conjugate pairs disappear entirely. +-/ + +@[expose] public section + +namespace ZetaZeros + +variable {lam : ℝ} {eta : ℝ → ℝ} {Z : Finset ℂ} {m : ℂ → ℕ} + +/-- The support splits into its real and non-real parts. -/ +theorem union_realPart_nonRealPart (hZ : IsConjInvariant Z m) : + (simpleRealPart Z m ∪ multipleRealPart Z m) ∪ nonRealPart Z = Z := by + ext x + simp only [Finset.mem_union, simpleRealPart, multipleRealPart, nonRealPart, + Finset.mem_filter] + constructor + · rintro ((⟨hx, -⟩ | ⟨hx, -⟩) | ⟨hx, -⟩) <;> exact hx + · intro hx + by_cases him : x.im = 0 + · have h1 := hZ.one_le x hx + rcases eq_or_lt_of_le h1 with heq | hlt + · exact Or.inl (Or.inl ⟨hx, him, heq.symm⟩) + · exact Or.inl (Or.inr ⟨hx, him, hlt⟩) + · exact Or.inr ⟨hx, him⟩ + +/-- The real and non-real parts are disjoint. -/ +theorem disjoint_realPart_nonRealPart : + Disjoint (simpleRealPart Z m ∪ multipleRealPart Z m) (nonRealPart Z) := by + rw [Finset.disjoint_left] + intro x hx hx' + simp only [Finset.mem_union, simpleRealPart, multipleRealPart, Finset.mem_filter] at hx + simp only [nonRealPart, Finset.mem_filter] at hx' + rcases hx with ⟨-, him, -⟩ | ⟨-, him, -⟩ <;> exact hx'.2 him + +/-- The cross terms cancel over the non-real part: conjugation is a fixed-point-free involution +there, and the cross term is odd under it. -/ +theorem sum_cross_eq_zero (hZ : IsConjInvariant Z m) (u v : ℝ) : + ∑ z ∈ nonRealPart Z, (m z : ℂ) * + (Complex.I * (gz eta z u * hz eta z v + hz eta z u * gz eta z v)) = 0 := by + refine Finset.sum_involution (fun z _ => (starRingEnd ℂ) z) ?_ ?_ ?_ ?_ + · intro z hz + simp only [nonRealPart, Finset.mem_filter] at hz + have hmem := hZ.conj_mem z hz.1 + have hmult := hZ.mult_conj z hz.1 + rw [hmult, gz_conj, hz_conj] + ring + · intro z hz _ + simp only [nonRealPart, Finset.mem_filter] at hz + intro hcon + apply hz.2 + have := Complex.conj_eq_iff_im.mp hcon + exact this + · intro z hz + simp only [nonRealPart, Finset.mem_filter] at hz ⊢ + exact ⟨hZ.conj_mem z hz.1, by simpa using hz.2⟩ + · intro z _ + exact Complex.conj_conj z + +/-- **The two-variable kernel in terms of even and odd parts.** -/ +theorem bigF_eq (hZ : IsConjInvariant Z m) (u v : ℝ) : + bigF eta Z m u v + = (∑ x ∈ simpleRealPart Z m ∪ multipleRealPart Z m, + (m x : ℂ) * fz eta x u * fz eta x v) + + ∑ z ∈ nonRealPart Z, + (m z : ℂ) * (gz eta z u * gz eta z v - hz eta z u * hz eta z v) := by + have hsplit : ∑ z ∈ Z, (m z : ℂ) * fz eta z u * fz eta z v + = (∑ x ∈ simpleRealPart Z m ∪ multipleRealPart Z m, + (m x : ℂ) * fz eta x u * fz eta x v) + + ∑ z ∈ nonRealPart Z, (m z : ℂ) * fz eta z u * fz eta z v := by + rw [← Finset.sum_union disjoint_realPart_nonRealPart, union_realPart_nonRealPart hZ] + rw [bigF, hsplit] + congr 1 + have hpt : ∀ z : ℂ, (m z : ℂ) * fz eta z u * fz eta z v + = (m z : ℂ) * (gz eta z u * gz eta z v - hz eta z u * hz eta z v) + + (m z : ℂ) * + (Complex.I * (gz eta z u * hz eta z v + hz eta z u * gz eta z v)) := by + intro z + rw [fz_eq_gz_add_I_mul_hz eta z u, fz_eq_gz_add_I_mul_hz eta z v] + linear_combination ((m z : ℂ) * hz eta z u * hz eta z v) * Complex.I_sq + calc ∑ z ∈ nonRealPart Z, (m z : ℂ) * fz eta z u * fz eta z v + = ∑ z ∈ nonRealPart Z, + ((m z : ℂ) * (gz eta z u * gz eta z v - hz eta z u * hz eta z v) + + (m z : ℂ) * + (Complex.I * (gz eta z u * hz eta z v + hz eta z u * gz eta z v))) := + Finset.sum_congr rfl fun z _ => hpt z + _ = _ := by + rw [Finset.sum_add_distrib, sum_cross_eq_zero hZ u v, add_zero] + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Hilbert/InnerReal.lean b/LeanPool/ZetaZeros/Hilbert/InnerReal.lean new file mode 100644 index 0000000000..8ac2171a1b --- /dev/null +++ b/LeanPool/ZetaZeros/Hilbert/InnerReal.lean @@ -0,0 +1,50 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public import LeanPool.ZetaZeros.Hilbert.Defs + +/-! +# Inner products of symmetric functions are real + +The reason the source may apply real inequalities such as `a² + 1 ≥ 2a` to the Bessel coefficients: +every inner product formed from symmetric functions is real, because conjugating it is the same as +reflecting the interval, and the interval `(-lam, lam)` is reflection-invariant. +-/ + +@[expose] public section + +namespace ZetaZeros + +/-- **Inner products of symmetric functions are self-conjugate.** -/ +theorem conj_inner_symmetric {Φ₁ Φ₂ : ℝ → ℂ} (h1 : IsSymmetric Φ₁) (h2 : IsSymmetric Φ₂) + (lam : ℝ) : + (starRingEnd ℂ) (∫ u in (-lam)..lam, Φ₁ u * (starRingEnd ℂ) (Φ₂ u)) + = ∫ u in (-lam)..lam, Φ₁ u * (starRingEnd ℂ) (Φ₂ u) := by + rw [← intervalIntegral.intervalIntegral_conj] + have hpt : ∀ u : ℝ, (starRingEnd ℂ) (Φ₁ u * (starRingEnd ℂ) (Φ₂ u)) + = Φ₁ (-u) * (starRingEnd ℂ) (Φ₂ (-u)) := by + intro u + rw [map_mul, Complex.conj_conj, h1 u] + congr 1 + rw [h2 (-u), neg_neg] + calc ∫ u in (-lam)..lam, (starRingEnd ℂ) (Φ₁ u * (starRingEnd ℂ) (Φ₂ u)) + = ∫ u in (-lam)..lam, Φ₁ (-u) * (starRingEnd ℂ) (Φ₂ (-u)) := by + exact intervalIntegral.integral_congr fun u _ => hpt u + _ = ∫ u in (-lam)..lam, Φ₁ u * (starRingEnd ℂ) (Φ₂ u) := by + rw [intervalIntegral.integral_comp_neg fun v => Φ₁ v * (starRingEnd ℂ) (Φ₂ v)] + norm_num + +/-- **Inner products of symmetric functions are real.** -/ +@[zz_tag "lem_symmetric_inner_real"] +theorem inner_symmetric_im_eq_zero {Φ₁ Φ₂ : ℝ → ℂ} (h1 : IsSymmetric Φ₁) (h2 : IsSymmetric Φ₂) + (lam : ℝ) : + (∫ u in (-lam)..lam, Φ₁ u * (starRingEnd ℂ) (Φ₂ u)).im = 0 := by + have h := conj_inner_symmetric h1 h2 lam + have := Complex.conj_eq_iff_im.mp h + exact this + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Hilbert/InnerRealL2.lean b/LeanPool/ZetaZeros/Hilbert/InnerRealL2.lean new file mode 100644 index 0000000000..6aa95d7b06 --- /dev/null +++ b/LeanPool/ZetaZeros/Hilbert/InnerRealL2.lean @@ -0,0 +1,51 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public import LeanPool.ZetaZeros.Hilbert.SymmetryL2 + +/-! +# The `L²` inner product of symmetric elements is real + +`inner_symmetric_im_eq_zero` says this for *functions*; Gram–Schmidt runs on `L²`, so the same fact +is needed for the inner product of `Lp` elements. The upgrade needs genuine measure-preservation of +`u ↦ -u` on the restricted measure, rather than an a.e. transport, because a change of variables +inside an integral is involved rather than a null-set argument. + +With this, "the Gram–Schmidt coefficients are real" becomes a fact about the ambient space, which is +what lets the process stay inside `symmetricSubspace`. +-/ + +@[expose] public section + +namespace ZetaZeros + +open MeasureTheory + +variable {lam : ℝ} + +/-- Negation preserves Lebesgue measure restricted to the symmetric interval. -/ +theorem measurePreserving_neg_Ioo (lam : ℝ) : + MeasurePreserving (fun u : ℝ => -u) (volume.restrict (Set.Ioo (-lam) lam)) + (volume.restrict (Set.Ioo (-lam) lam)) := by + have hpre : (fun u : ℝ => -u) ⁻¹' Set.Ioo (-lam) lam = Set.Ioo (-lam) lam := by + ext u + simp only [Set.mem_preimage, Set.mem_Ioo] + constructor + · exact fun ⟨h1, h2⟩ => ⟨by linarith, by linarith⟩ + · exact fun ⟨h1, h2⟩ => ⟨by linarith, by linarith⟩ + have h := (Measure.measurePreserving_neg (volume : Measure ℝ)).restrict_preimage + (measurableSet_Ioo (a := -lam) (b := lam)) + rwa [hpre] at h + +/-- A reflection-invariant integral over the symmetric interval. -/ +theorem integral_comp_neg_Ioo {E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E] + (g : ℝ → E) (lam : ℝ) : + ∫ u in Set.Ioo (-lam) lam, g (-u) = ∫ u in Set.Ioo (-lam) lam, g u := + (measurePreserving_neg_Ioo lam).integral_comp + (MeasurableEquiv.neg ℝ).measurableEmbedding g + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Hilbert/Integrals.lean b/LeanPool/ZetaZeros/Hilbert/Integrals.lean new file mode 100644 index 0000000000..17946a8d16 --- /dev/null +++ b/LeanPool/ZetaZeros/Hilbert/Integrals.lean @@ -0,0 +1,101 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public import LeanPool.ZetaZeros.Hilbert.Defs + +/-! +# The kernel as an inner product + +The identity that turns the kernel into a Gram matrix on `L²((-lam, lam))`, and its two immediate +consequences. + +Every integral here is taken over `Set.Ioo (-lam) lam` rather than over `ℝ`, so that it is +literally the inner product of the ambient Hilbert space; `fourierC` integrates over the line, and +the two agree because an admissible test function vanishes off the interval. +-/ + +@[expose] public section + +namespace ZetaZeros + +open MeasureTheory + +variable {lam : ℝ} {eta : ℝ → ℝ} + +/-- An admissible test function vanishes off `Ioo (-lam) lam`. -/ +private lemma eta_eq_zero_of_notMem (h : IsAdmissible lam eta) {u : ℝ} + (hu : u ∉ Set.Ioo (-lam) lam) : eta u = 0 := by + simp only [Set.mem_Ioo, not_and_or, not_lt] at hu + rcases hu with h1 | h2 + · exact h.support u (le_trans (by linarith) (neg_le_abs u)) + · exact h.support u (le_trans h2 (le_abs_self u)) + +/-- The twisted function vanishes off `Ioo (-lam) lam`. -/ +private lemma fz_eq_zero_of_notMem (h : IsAdmissible lam eta) {z : ℂ} {u : ℝ} + (hu : u ∉ Set.Ioo (-lam) lam) : fz eta z u = 0 := by + simp [fz, eta_eq_zero_of_notMem h hu] + +/-- **The kernel is a Gram matrix.** `K_eta (z - conj s)` is the inner product of the twisted +functions attached to `z` and `s`. -/ +@[zz_tag "lem_kernel_factorization"] +theorem testKernel_sub_conj (h : IsAdmissible lam eta) (z s : ℂ) : + testKernel eta (z - (starRingEnd ℂ) s) + = ∫ u in Set.Ioo (-lam) lam, fz eta z u * (starRingEnd ℂ) (fz eta s u) := by + have hpt : ∀ u : ℝ, + ((eta u : ℂ) ^ 2) * + Complex.exp (-(2 * (Real.pi : ℂ)) * Complex.I * (z - (starRingEnd ℂ) s) * (u : ℂ)) + = fz eta z u * (starRingEnd ℂ) (fz eta s u) := by + intro u + simp only [fz, map_mul, Complex.conj_ofReal, ← Complex.exp_conj, map_neg, map_mul, + Complex.conj_I, Complex.conj_ofReal, map_ofNat] + have hexp : -(2 * (Real.pi : ℂ)) * Complex.I * (u : ℂ) * z + + -(2 * (Real.pi : ℂ)) * -Complex.I * (u : ℂ) * (starRingEnd ℂ) s + = -(2 * (Real.pi : ℂ)) * Complex.I * (z - (starRingEnd ℂ) s) * (u : ℂ) := by + ring + rw [mul_mul_mul_comm, ← Complex.exp_add, hexp, sq] + rw [setIntegral_eq_integral_of_forall_compl_eq_zero fun u hu => by + simp [fz_eq_zero_of_notMem h hu]] + simp only [testKernel, fourierC, Pi.pow_apply, Complex.ofReal_pow] + exact integral_congr_ae (Filter.Eventually.of_forall hpt) + +/-- At a real point the twisted function has unit `L²` norm. -/ +@[zz_tag "lem_f_real_norm"] +theorem integral_norm_fz_sq (h : IsAdmissible lam eta) {x : ℂ} (hx : x.im = 0) : + ∫ u in Set.Ioo (-lam) lam, ‖fz eta x u‖ ^ 2 = 1 := by + obtain ⟨r, hr⟩ : ∃ r : ℝ, x = (r : ℂ) := ⟨x.re, by apply Complex.ext <;> simp [hx]⟩ + have hnorm : ∀ u : ℝ, ‖fz eta x u‖ ^ 2 = eta u ^ 2 := by + intro u + have hexp : -(2 * (Real.pi : ℂ)) * Complex.I * (u : ℂ) * x + = ((-(2 * Real.pi) * u * r : ℝ) : ℂ) * Complex.I := by + rw [hr]; push_cast; ring + rw [fz, norm_mul, hexp, Complex.norm_exp_ofReal_mul_I, mul_one] + simp [sq_abs] + simp only [hnorm] + rw [setIntegral_eq_integral_of_forall_compl_eq_zero fun u hu => by + simp [eta_eq_zero_of_notMem h hu]] + have hk := h.fourier_sq_zero + simp only [fourierC, Pi.pow_apply, mul_zero, zero_mul, Complex.exp_zero, mul_one] at hk + have hcast : ((∫ u : ℝ, eta u ^ 2 : ℝ) : ℂ) = 1 := by + rw [← integral_complex_ofReal] + exact hk + exact_mod_cast hcast + +/-- Conjugating the second argument of the kernel does not change the second moment: conjugation +permutes the support and preserves multiplicity. -/ +@[zz_tag "lem_sum_shift_conj"] +theorem sum_testKernel_sq_eq_sum_conj {Z : Finset ℂ} {m : ℂ → ℕ} + (h : IsConjInvariant Z m) : + ∑ z ∈ Z, ∑ s ∈ Z, (m z * m s : ℂ) * testKernel eta (z - s) ^ 2 + = ∑ z ∈ Z, ∑ s ∈ Z, (m z * m s : ℂ) * testKernel eta (z - (starRingEnd ℂ) s) ^ 2 := by + refine Finset.sum_congr rfl fun z _ => ?_ + refine (Finset.sum_nbij' (fun s => (starRingEnd ℂ) s) (fun s => (starRingEnd ℂ) s) + (fun s hs => h.conj_mem s hs) (fun s hs => h.conj_mem s hs) + (fun s _ => Complex.conj_conj s) (fun s _ => Complex.conj_conj s) ?_).symm + intro s hs + rw [h.mult_conj s hs] + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Hilbert/L2.lean b/LeanPool/ZetaZeros/Hilbert/L2.lean new file mode 100644 index 0000000000..e31c44702f --- /dev/null +++ b/LeanPool/ZetaZeros/Hilbert/L2.lean @@ -0,0 +1,115 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public import LeanPool.ZetaZeros.Hilbert.Defs +public import LeanPool.ZetaZeros.Zeta.Finite + +/-! +# The twisted functions live in `L²((-lam, lam))` + +The twisted functions and their even and odd parts are square-integrable on the interval, so they +are genuine elements of the ambient Hilbert space and `Submodule.span` may be taken of them. + +The bound is the only analytic content. `‖fz eta z u‖ = |eta u| · exp(2π u · im z)`, and on a +bounded interval the exponential is bounded by `exp(2π · lam · |im z|)`, so `fz` is dominated by a +constant multiple of `eta`, which is square-integrable by admissibility. Note the twist is *not* +bounded by `1` unless `z` is real — that is exactly why the interval has to be bounded. + +Also here: the passage from the rescaled zeros as a `Set` to the `Finset` that +`IsConjInvariant` requires. +-/ + +@[expose] public section + +namespace ZetaZeros + +open MeasureTheory + +variable {lam : ℝ} {eta : ℝ → ℝ} + +/-- The real part of the twist exponent is `2π u · im z`. -/ +private lemma twist_exponent_re (z : ℂ) (u : ℝ) : + (-(2 * (Real.pi : ℂ)) * Complex.I * (u : ℂ) * z).re = 2 * Real.pi * u * z.im := by + simp only [Complex.mul_re, Complex.mul_im, Complex.I_re, Complex.I_im, Complex.neg_re, + Complex.neg_im, Complex.ofReal_re, Complex.ofReal_im, Complex.re_ofNat, Complex.im_ofNat] + ring + +/-- The twist factor has norm `exp(2π u · im z)`; in particular it is not bounded by one unless +`z` is real. -/ +private lemma norm_twist (z : ℂ) (u : ℝ) : + ‖Complex.exp (-(2 * (Real.pi : ℂ)) * Complex.I * (u : ℂ) * z)‖ + = Real.exp (2 * Real.pi * u * z.im) := by + rw [Complex.norm_exp, twist_exponent_re] + +/-- The twist factor is continuous. -/ +private lemma continuous_twist (z : ℂ) : + Continuous fun u : ℝ => Complex.exp (-(2 * (Real.pi : ℂ)) * Complex.I * (u : ℂ) * z) := by + fun_prop + +/-- On the interval, `fz` is dominated by a constant multiple of `eta`. -/ +private lemma norm_fz_le (z : ℂ) {u : ℝ} (hu : u ∈ Set.Ioo (-lam) lam) : + ‖fz eta z u‖ ≤ Real.exp (2 * Real.pi * lam * |z.im|) * ‖eta u‖ := by + have hu' : |u| ≤ lam := abs_le.mpr ⟨le_of_lt hu.1, le_of_lt hu.2⟩ + have hprod : u * z.im ≤ |u| * |z.im| := + le_trans (le_abs_self _) (le_of_eq (abs_mul u z.im)) + have hle : u * z.im ≤ lam * |z.im| := + le_trans hprod (mul_le_mul_of_nonneg_right hu' (abs_nonneg _)) + have hexp : 2 * Real.pi * u * z.im ≤ 2 * Real.pi * lam * |z.im| := by + calc 2 * Real.pi * u * z.im = 2 * Real.pi * (u * z.im) := by ring + _ ≤ 2 * Real.pi * (lam * |z.im|) := + mul_le_mul_of_nonneg_left hle (by positivity) + _ = 2 * Real.pi * lam * |z.im| := by ring + rw [fz, norm_mul, norm_twist, Complex.norm_real, mul_comm] + exact mul_le_mul_of_nonneg_right (Real.exp_le_exp.mpr hexp) (abs_nonneg _) + +/-- `fz` is measurable on the interval. -/ +private lemma aestronglyMeasurable_fz (h : IsAdmissible lam eta) (z : ℂ) (s : Set ℝ) : + AEStronglyMeasurable (fz eta z) (volume.restrict s) := by + have he : AEStronglyMeasurable (fun u : ℝ => ((eta u : ℝ) : ℂ)) (volume.restrict s) := + Complex.continuous_ofReal.comp_aestronglyMeasurable (h.memLp.1.restrict) + exact he.mul (continuous_twist z).aestronglyMeasurable + +/-- **The twisted function is square-integrable on the interval.** -/ +theorem memLp_fz (h : IsAdmissible lam eta) (z : ℂ) : + MemLp (fz eta z) 2 (volume.restrict (Set.Ioo (-lam) lam)) := by + refine MemLp.of_le_mul (c := Real.exp (2 * Real.pi * lam * |z.im|)) + ((h.memLp.restrict _)) (aestronglyMeasurable_fz h z _) ?_ + filter_upwards [ae_restrict_mem measurableSet_Ioo] with u hu + exact norm_fz_le z hu + +/-- The even part is square-integrable on the interval. -/ +theorem memLp_gz (h : IsAdmissible lam eta) (z : ℂ) : + MemLp (gz eta z) 2 (volume.restrict (Set.Ioo (-lam) lam)) := by + have hg : gz eta z = (2 : ℂ)⁻¹ • (fz eta z + fz eta ((starRingEnd ℂ) z)) := by + funext u; simp [gz, Pi.add_apply, smul_eq_mul]; ring + rw [hg] + exact ((memLp_fz h z).add (memLp_fz h ((starRingEnd ℂ) z))).const_smul _ + +/-- The odd part is square-integrable on the interval. -/ +theorem memLp_hz (h : IsAdmissible lam eta) (z : ℂ) : + MemLp (hz eta z) 2 (volume.restrict (Set.Ioo (-lam) lam)) := by + have hh : hz eta z = (2 * Complex.I)⁻¹ • (fz eta z - fz eta ((starRingEnd ℂ) z)) := by + funext u + simp only [hz, Pi.sub_apply, Pi.smul_apply, smul_eq_mul] + rw [div_eq_inv_mul] + rw [hh] + exact ((memLp_fz h z).sub (memLp_fz h ((starRingEnd ℂ) z))).const_smul _ + +/-! ## From the rescaled zeros as a set to the finite set the propositions need -/ + +/-- The rescaled zeros as a `Finset`, which is what `IsConjInvariant` and the key +proposition take. The rescaled zeros are defined as a `Set`; finiteness of the zero set is what +bridges the two. -/ +noncomputable def rescaledZerosFinset (T : ℝ) : Finset ℂ := + (nontrivialZeros_finite T).toFinset.image (rescale T) + +@[simp] +theorem coe_rescaledZerosFinset (T : ℝ) : + ((rescaledZerosFinset T : Finset ℂ) : Set ℂ) = rescaledZeros T := by + simp [rescaledZerosFinset, rescaledZeros, Set.Finite.coe_toFinset] + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Hilbert/Subspaces.lean b/LeanPool/ZetaZeros/Hilbert/Subspaces.lean new file mode 100644 index 0000000000..737e99088f --- /dev/null +++ b/LeanPool/ZetaZeros/Hilbert/Subspaces.lean @@ -0,0 +1,84 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public import LeanPool.ZetaZeros.Hilbert.L2 + +/-! +# The three nested subspaces + +`U ⊆ V ⊆ W` inside `L²((-lam, lam))`, spanned by the twisted functions attached to the three parts +of the support. These are the spaces the Gram–Schmidt process is run on, and the nesting is what +makes an *adapted* orthonormal basis possible. + +Each family is indexed by the whole of `nonRealPart Z` rather than by a choice of one point from +each conjugate pair. That is legitimate because `gz` is conjugation-invariant and `hz` is +conjugation-anti-invariant, so the span is unchanged — and it removes the enumeration +`z₁, conj z₁, …, z_k, conj z_k` that the source has to carry. +-/ + +@[expose] public section + +namespace ZetaZeros + +open MeasureTheory + +variable {lam : ℝ} {eta : ℝ → ℝ} + +/-- The ambient Hilbert space: square-integrable functions on the interval. -/ +noncomputable abbrev L2Interval (lam : ℝ) := Lp ℂ 2 (volume.restrict (Set.Ioo (-lam) lam)) + +/-- The twisted function as an element of `L²`. -/ +noncomputable def fzL2 (h : IsAdmissible lam eta) (z : ℂ) : L2Interval lam := + MemLp.toLp _ (memLp_fz h z) + +/-- The even part as an element of `L²`. -/ +noncomputable def gzL2 (h : IsAdmissible lam eta) (z : ℂ) : L2Interval lam := + MemLp.toLp _ (memLp_gz h z) + +/-- The odd part as an element of `L²`. -/ +noncomputable def hzL2 (h : IsAdmissible lam eta) (z : ℂ) : L2Interval lam := + MemLp.toLp _ (memLp_hz h z) + +/-- The first subspace, spanned by the twisted functions at the multiple real points together with +the even parts at the non-real points. -/ +@[zz_tag "def_U"] +noncomputable def subspaceU (h : IsAdmissible lam eta) (Z : Finset ℂ) (m : ℂ → ℕ) : + Submodule ℂ (L2Interval lam) := + Submodule.span ℂ + ((fzL2 h '' (multipleRealPart Z m : Set ℂ)) ∪ (gzL2 h '' (nonRealPart Z : Set ℂ))) + +/-- The second subspace, adding the twisted functions at the simple real points. -/ +@[zz_tag "def_V"] +noncomputable def subspaceV (h : IsAdmissible lam eta) (Z : Finset ℂ) (m : ℂ → ℕ) : + Submodule ℂ (L2Interval lam) := + Submodule.span ℂ + ((fzL2 h '' ((simpleRealPart Z m ∪ multipleRealPart Z m : Finset ℂ) : Set ℂ)) + ∪ (gzL2 h '' (nonRealPart Z : Set ℂ))) + +/-- The third subspace, adding the odd parts at the non-real points. -/ +@[zz_tag "def_W"] +noncomputable def subspaceW (h : IsAdmissible lam eta) (Z : Finset ℂ) (m : ℂ → ℕ) : + Submodule ℂ (L2Interval lam) := + Submodule.span ℂ + ((fzL2 h '' ((simpleRealPart Z m ∪ multipleRealPart Z m : Finset ℂ) : Set ℂ)) + ∪ (gzL2 h '' (nonRealPart Z : Set ℂ)) + ∪ (hzL2 h '' (nonRealPart Z : Set ℂ))) + +/-- The subspaces are nested: `U ≤ V`. -/ +theorem subspaceU_le_subspaceV (h : IsAdmissible lam eta) (Z : Finset ℂ) (m : ℂ → ℕ) : + subspaceU h Z m ≤ subspaceV h Z m := by + refine Submodule.span_mono (Set.union_subset_union_left _ (Set.image_mono ?_)) + intro x hx + simp only [Finset.coe_union, Set.mem_union] + exact Or.inr hx + +/-- The subspaces are nested: `V ≤ W`. -/ +theorem subspaceV_le_subspaceW (h : IsAdmissible lam eta) (Z : Finset ℂ) (m : ℂ → ℕ) : + subspaceV h Z m ≤ subspaceW h Z m := + Submodule.span_mono Set.subset_union_left + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Hilbert/Symmetry.lean b/LeanPool/ZetaZeros/Hilbert/Symmetry.lean new file mode 100644 index 0000000000..2136771680 --- /dev/null +++ b/LeanPool/ZetaZeros/Hilbert/Symmetry.lean @@ -0,0 +1,61 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public import LeanPool.ZetaZeros.Hilbert.Defs + +/-! +# Symmetry of the twisted functions + +Conjugation acts on the twisted functions as reflection in the origin. This is the property that +survives Gram–Schmidt and makes every Bessel coefficient real, which is what lets the source treat +them with real inequalities rather than complex ones. + +Everything here follows from one computation, `conj_fz`: conjugating `fz eta z` reflects the +argument and conjugates the twist. +-/ + +@[expose] public section + +namespace ZetaZeros + +variable {lam : ℝ} {eta : ℝ → ℝ} + +/-- Conjugation reflects the argument and conjugates the twist. The single computation the three +symmetry lemmas below rest on. -/ +theorem conj_fz (he : ∀ x, eta (-x) = eta x) (z : ℂ) (u : ℝ) : + (starRingEnd ℂ) (fz eta z u) = fz eta ((starRingEnd ℂ) z) (-u) := by + simp only [fz, map_mul, Complex.conj_ofReal, ← Complex.exp_conj, map_neg, map_mul, + Complex.conj_I, Complex.conj_ofReal, map_ofNat, he u, Complex.ofReal_neg] + ring_nf + +/-- At a real point the twisted function is symmetric. -/ +@[zz_tag "lem_f_symmetric"] +theorem isSymmetric_fz (h : IsAdmissible lam eta) {x : ℂ} (hx : x.im = 0) : + IsSymmetric (fz eta x) := by + intro u + rw [conj_fz h.even x u] + congr 1 + exact Complex.ext (by simp) (by simp [hx]) + +/-- The even part is symmetric. -/ +@[zz_tag "lem_g_symmetric"] +theorem isSymmetric_gz (h : IsAdmissible lam eta) (z : ℂ) : IsSymmetric (gz eta z) := by + intro u + simp only [gz, map_div₀, map_add, map_ofNat] + rw [conj_fz h.even z u, conj_fz h.even ((starRingEnd ℂ) z) u, Complex.conj_conj] + ring + +/-- The odd part is symmetric. -/ +@[zz_tag "lem_h_symmetric"] +theorem isSymmetric_hz (h : IsAdmissible lam eta) (z : ℂ) : IsSymmetric (hz eta z) := by + intro u + simp only [hz, map_div₀, map_sub, map_mul, map_ofNat, Complex.conj_I] + rw [conj_fz h.even z u, conj_fz h.even ((starRingEnd ℂ) z) u, Complex.conj_conj] + field_simp + ring + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Hilbert/SymmetryL2.lean b/LeanPool/ZetaZeros/Hilbert/SymmetryL2.lean new file mode 100644 index 0000000000..8bc4dcfc0c --- /dev/null +++ b/LeanPool/ZetaZeros/Hilbert/SymmetryL2.lean @@ -0,0 +1,72 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public import LeanPool.ZetaZeros.Hilbert.Subspaces +public import LeanPool.ZetaZeros.Hilbert.Symmetry + +/-! +# Symmetry on `L²`, as a real subspace + +Pointwise symmetry cannot be carried into `L²` unchanged: elements of `Lp` are equivalence classes, +so the property must be stated almost everywhere. Doing so needs one genuinely new ingredient — an +a.e. statement has to be transportable through `u ↦ -u`, which holds because negation preserves +Lebesgue measure and `Ioo (-lam) lam` is reflection-invariant. + +Once that is available the symmetric elements form an **ℝ**-subspace. Not a `ℂ`-one: multiplying by +`i` sends `conj (f u) = f (-u)` to `conj (i · f u) = -i · conj (f u)`, which fails. That is exactly +the structure the Gram–Schmidt argument needs, since its coefficients are real by +`inner_symmetric_im_eq_zero` and so it never leaves the subspace. +-/ + +@[expose] public section + +namespace ZetaZeros + +open MeasureTheory + +variable {lam : ℝ} + +/-- An almost-everywhere statement on the symmetric interval survives reflection. Negation +preserves Lebesgue measure and `Ioo (-lam) lam` is reflection-invariant, so a null set is carried +to a null set. -/ +theorem ae_restrict_Ioo_neg {P : ℝ → Prop} + (h : ∀ᵐ u ∂(volume.restrict (Set.Ioo (-lam) lam)), P u) : + ∀ᵐ u ∂(volume.restrict (Set.Ioo (-lam) lam)), P (-u) := by + rw [ae_restrict_iff' measurableSet_Ioo] at h ⊢ + have hneg : MeasurePreserving (fun u : ℝ => -u) volume volume := + Measure.measurePreserving_neg volume + filter_upwards [hneg.quasiMeasurePreserving.ae h] with u hu hmem + refine hu ?_ + obtain ⟨h1, h2⟩ := hmem + exact ⟨by linarith, by linarith⟩ + +/-- Almost-everywhere symmetry for an element of `L²`. -/ +def IsSymmetricL2 (f : L2Interval lam) : Prop := + ∀ᵐ u ∂(volume.restrict (Set.Ioo (-lam) lam)), (starRingEnd ℂ) (f u) = f (-u) + +/-- The symmetric elements form an `ℝ`-subspace of `L²`. -/ +noncomputable def symmetricSubspace (lam : ℝ) : Submodule ℝ (L2Interval lam) where + carrier := {f | IsSymmetricL2 f} + zero_mem' := by + have hz := Lp.coeFn_zero ℂ 2 (volume.restrict (Set.Ioo (-lam) lam)) + filter_upwards [hz, ae_restrict_Ioo_neg hz] with u h1 h2 + simp only [h1, h2, Pi.zero_apply, map_zero] + add_mem' := by + intro f g hf hg + have hadd := Lp.coeFn_add f g + filter_upwards [hf, hg, ae_restrict_Ioo_neg hf, ae_restrict_Ioo_neg hg, + hadd, ae_restrict_Ioo_neg hadd] with u hf1 hg1 _ _ ha1 ha2 + simp only [ha1, ha2, Pi.add_apply, map_add, hf1, hg1] + smul_mem' := by + intro c f hf + have hsmul := Lp.coeFn_smul c f + filter_upwards [hf, ae_restrict_Ioo_neg hf, hsmul, ae_restrict_Ioo_neg hsmul] + with u hf1 _ hs1 hs2 + simp only [hs1, hs2, Pi.smul_apply, RCLike.real_smul_eq_coe_mul, map_mul, hf1] + simp + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Main.lean b/LeanPool/ZetaZeros/Main.lean new file mode 100644 index 0000000000..dd29ed7878 --- /dev/null +++ b/LeanPool/ZetaZeros/Main.lean @@ -0,0 +1,141 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Axiom Math +-/ +module + +public import LeanPool.ZetaZeros.Zeta.Proportion +public import LeanPool.ZetaZeros.Zeta.Transfer +public import LeanPool.ZetaZeros.Numeric.MontgomeryTaylor + +/-! +# Proportion bounds for zeta zeros + +This combines the kernel construction, the finite-set bounds transferred to zeta zeros, and the +Riemann--von Mangoldt asymptotic to obtain the two asymptotic proportion bounds. +-/ + +@[expose] public section + +namespace ZetaZeros + +open Filter Topology + +@[zz_tag "thm_simple"] +theorem simple_proportion_lower (hRvM : RiemannVonMangoldt) (hPC : PairCorrelation) + (ε : ℝ) (hε : 0 < ε) : + ∃ T₀ : ℝ, ∀ T ≥ T₀, + 3 / 2 - (1 / Real.sqrt 2) * Real.cot (1 / Real.sqrt 2) - ε < + (simpleOnLineCount T : ℝ) / (zeroCount T : ℝ) := by + obtain ⟨eta, C, heta, hC, hsum⟩ := kernelConstruction hPC (ε / 2) (by positivity) + have hNscale := hRvM.tendsto + have hscale : ∀ᶠ T : ℝ in atTop, zeroScale T ≠ 0 := + zeroScale_pos_eventually.mono fun _ h => ne_of_gt h + have hratio : Tendsto + (fun T => (unweightedKernelSum eta T).re / (zeroCount T : ℝ)) + atTop (nhds C) := + tendsto_ratio_of_tendsto_div_scale _ _ _ _ hNscale hsum hscale + have hNpos := hRvM.zeroCount_pos_eventually + have hbound : ∀ᶠ T : ℝ in atTop, + 2 * (zeroCount T : ℝ) - (unweightedKernelSum eta T).re ≤ + (simpleOnLineCount T : ℝ) := by + filter_upwards [eventually_gt_atTop (1 : ℝ)] with T hT + exact simpleOnLineCount_lower hT heta + have hprop := eventually_simple_proportion + (fun T => (zeroCount T : ℝ)) (fun T => (simpleOnLineCount T : ℝ)) + (fun T => (unweightedKernelSum eta T).re) C (ε / 2) (by positivity) + hNpos hratio hbound + have hconst : + 3 / 2 - (1 / Real.sqrt 2) * Real.cot (1 / Real.sqrt 2) - ε < + 2 - C - ε / 2 := by + rw [show 3 / 2 - (1 / Real.sqrt 2) * Real.cot (1 / Real.sqrt 2) = + 2 - montgomeryTaylorConst by + rw [montgomeryTaylorConst] + ring] + rw [abs_lt] at hC + linarith + have hfinal : ∀ᶠ T : ℝ in atTop, + 3 / 2 - (1 / Real.sqrt 2) * Real.cot (1 / Real.sqrt 2) - ε < + (simpleOnLineCount T : ℝ) / (zeroCount T : ℝ) := + hprop.mono fun T hT => hconst.trans hT + exact eventually_atTop.1 hfinal + +@[zz_tag "thm_distinct"] +theorem distinct_proportion_lower (hRvM : RiemannVonMangoldt) (hPC : PairCorrelation) + (ε : ℝ) (hε : 0 < ε) : + ∃ T₀ : ℝ, ∀ T ≥ T₀, + 5 / 4 - (1 / (2 * Real.sqrt 2)) * Real.cot (1 / Real.sqrt 2) - ε < + (distinctZeroCount T : ℝ) / (zeroCount T : ℝ) := by + obtain ⟨eta, C, heta, hC, hsum⟩ := kernelConstruction hPC (ε / 2) (by positivity) + have hNscale := hRvM.tendsto + have hscale : ∀ᶠ T : ℝ in atTop, zeroScale T ≠ 0 := + zeroScale_pos_eventually.mono fun _ h => ne_of_gt h + have hratio : Tendsto + (fun T => (unweightedKernelSum eta T).re / (zeroCount T : ℝ)) + atTop (nhds C) := + tendsto_ratio_of_tendsto_div_scale _ _ _ _ hNscale hsum hscale + have hNpos := hRvM.zeroCount_pos_eventually + have hbound : ∀ᶠ T : ℝ in atTop, + (3 / 2 : ℝ) * (zeroCount T : ℝ) - (unweightedKernelSum eta T).re / 2 ≤ + (distinctZeroCount T : ℝ) := by + filter_upwards [eventually_gt_atTop (1 : ℝ)] with T hT + exact distinctZeroCount_lower hT heta + have hprop := eventually_distinct_proportion + (fun T => (zeroCount T : ℝ)) (fun T => (distinctZeroCount T : ℝ)) + (fun T => (unweightedKernelSum eta T).re) C (ε / 2) (by positivity) + hNpos hratio hbound + have hconst : + 5 / 4 - (1 / (2 * Real.sqrt 2)) * Real.cot (1 / Real.sqrt 2) - ε < + 3 / 2 - C / 2 - ε / 2 := by + rw [show 5 / 4 - (1 / (2 * Real.sqrt 2)) * Real.cot (1 / Real.sqrt 2) = + 3 / 2 - montgomeryTaylorConst / 2 by + rw [montgomeryTaylorConst] + ring] + rw [abs_lt] at hC + linarith + have hfinal : ∀ᶠ T : ℝ in atTop, + 5 / 4 - (1 / (2 * Real.sqrt 2)) * Real.cot (1 / Real.sqrt 2) - ε < + (distinctZeroCount T : ℝ) / (zeroCount T : ℝ) := + hprop.mono fun T hT => hconst.trans hT + exact eventually_atTop.1 hfinal + +/-- **`thm_simple_numeric`.** Beyond some height, more than `67.25%` of the non-trivial zeros of +the Riemann zeta function are simple and lie on the critical line. -/ +@[zz_tag "thm_simple_numeric"] +theorem simple_proportion_d4 (hRvM : RiemannVonMangoldt) (hPC : PairCorrelation) : + ∃ T₀ : ℝ, ∀ T ≥ T₀, 0.6725 < (simpleOnLineCount T : ℝ) / (zeroCount T : ℝ) := by + have hconst : (0.6725 : ℝ) < + 3 / 2 - (1 / Real.sqrt 2) * Real.cot (1 / Real.sqrt 2) := by + rw [show 3 / 2 - (1 / Real.sqrt 2) * Real.cot (1 / Real.sqrt 2) = + 2 - montgomeryTaylorConst by + rw [montgomeryTaylorConst] + ring] + linarith [montgomeryTaylorConst_lt] + obtain ⟨T₀, hT₀⟩ := simple_proportion_lower hRvM hPC + (3 / 2 - (1 / Real.sqrt 2) * Real.cot (1 / Real.sqrt 2) - 0.6725) + (sub_pos.mpr hconst) + refine ⟨T₀, fun T hT => ?_⟩ + convert hT₀ T hT using 1 + ring + +/-- **`thm_distinct_numeric`.** Beyond some height, more than `83.625%` of the non-trivial zeros +of the Riemann zeta function are distinct. -/ +@[zz_tag "thm_distinct_numeric"] +theorem distinct_proportion_d5 (hRvM : RiemannVonMangoldt) (hPC : PairCorrelation) : + ∃ T₀ : ℝ, ∀ T ≥ T₀, 0.83625 < (distinctZeroCount T : ℝ) / (zeroCount T : ℝ) := by + have hconst : (0.83625 : ℝ) < + 5 / 4 - (1 / (2 * Real.sqrt 2)) * Real.cot (1 / Real.sqrt 2) := by + rw [show 5 / 4 - (1 / (2 * Real.sqrt 2)) * Real.cot (1 / Real.sqrt 2) = + 3 / 2 - montgomeryTaylorConst / 2 by + rw [montgomeryTaylorConst] + ring] + linarith [montgomeryTaylorConst_lt] + obtain ⟨T₀, hT₀⟩ := distinct_proportion_lower hRvM hPC + (5 / 4 - (1 / (2 * Real.sqrt 2)) * Real.cot (1 / Real.sqrt 2) - 0.83625) + (sub_pos.mpr hconst) + refine ⟨T₀, fun T hT => ?_⟩ + convert hT₀ T hT using 1 + ring + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Meta/Attr.lean b/LeanPool/ZetaZeros/Meta/Attr.lean new file mode 100644 index 0000000000..00408205e3 --- /dev/null +++ b/LeanPool/ZetaZeros/Meta/Attr.lean @@ -0,0 +1,42 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public meta import Lean + +/-! +# The `zz_tag` attribute + +`@[zz_tag "TAG"]` records which numbered result of the source paper a declaration formalizes, `TAG` +being that result's label (`def_f_z`, `lem_bessel_F`, `prop_simple_real_lower`, `thm_simple`, ...). +It is the analogue of Mathlib's `@[stacks TAG]`. + +The tag goes on **every** declaration whose union states the result's content, not merely the first; +scaffolding — helper definitions, hypothesis bundles, proof-local lemmas — stays untagged. +-/ + +public meta section + +open Lean + +/-- `@[zz_tag "TAG"]` records which result of the source paper a declaration formalizes. + +The syntax node is named `zzTag`, not `zz_tag`: the user-facing *token* keeps the underscore, to +match `@[stacks …]`, but Mathlib's `defsWithUnderscore` linter inspects the declaration name, so the +internal name must be lowerCamelCase. -/ +syntax (name := zzTag) "zz_tag " str : attr + +/-- Records which result of the source paper a declaration formalizes. -/ +initialize zzTagAttr : ParametricAttribute String ← + registerParametricAttribute { + name := `zzTag + descr := "Records which result of the source paper a declaration formalizes." + getParam := fun _ stx => do + match stx with + | `(attr| zz_tag $s:str) => return s.getString + | _ => throwError + "expected zz_tag applied to a single string literal, as in zz_tag \"lem_bessel_F\"" + } diff --git a/LeanPool/ZetaZeros/MontgomeryTaylor/AffineKernel.lean b/LeanPool/ZetaZeros/MontgomeryTaylor/AffineKernel.lean new file mode 100644 index 0000000000..a765db040b --- /dev/null +++ b/LeanPool/ZetaZeros/MontgomeryTaylor/AffineKernel.lean @@ -0,0 +1,250 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Axiom Math +-/ +module + +public import LeanPool.ZetaZeros.Zeta.Basic + +/-! +# The auxiliary kernel `G`, and why it is constant + +`G u = f_0 u + integral over [-1/2, 1/2] of |u - v| f_0 v`. + +The heart of the computation: the modulus kernel differentiates twice to `2 f_0 u` while +`f_0'' = -2 f_0`, so `G'' = 0` and `G` is affine; being even, it is constant. +-/ + +@[expose] public section + +open MeasureTheory + +namespace ZetaZeros + +/-- The auxiliary kernel `G(u) = f₀(u) + ∫_{-1/2}^{1/2} |u - v| f₀(v) dv`, which is constant on +`[-1/2, 1/2]`. -/ +noncomputable def extremalG (u : ℝ) : ℝ := + extremalTest u + ∫ v in (-(1:ℝ)/2)..(1/2), |u - v| * extremalTest v + +/-! +## Antiderivatives for the modulus kernel +-/ + +/-- Antiderivative fact: `d/dv [v sin(√2 v)/√2 + cos(√2 v)/2] = v cos(√2 v)`. -/ +lemma hasDerivAt_vcos (v : ℝ) : + HasDerivAt + (fun v => v * Real.sin (Real.sqrt 2 * v) / Real.sqrt 2 + Real.cos (Real.sqrt 2 * v) / 2) + (v * Real.cos (Real.sqrt 2 * v)) v := by + have h2 : (0:ℝ) < Real.sqrt 2 := Real.sqrt_pos.mpr (by norm_num) + have h2ne : Real.sqrt 2 ≠ 0 := ne_of_gt h2 + have hmul : Real.sqrt 2 * Real.sqrt 2 = 2 := Real.mul_self_sqrt (by norm_num) + -- derivative of sin(√2 v) + have hlin : HasDerivAt (fun v => Real.sqrt 2 * v) (Real.sqrt 2) v := by + simpa using (hasDerivAt_id v).const_mul (Real.sqrt 2) + have hsin : HasDerivAt (fun v => Real.sin (Real.sqrt 2 * v)) + (Real.sqrt 2 * Real.cos (Real.sqrt 2 * v)) v := by + have := (Real.hasDerivAt_sin (Real.sqrt 2 * v)).comp v hlin + simp only [Function.comp_def] at this + rw [mul_comm] at this + exact this + have hcos : HasDerivAt (fun v => Real.cos (Real.sqrt 2 * v)) + (-(Real.sqrt 2 * Real.sin (Real.sqrt 2 * v))) v := by + have := (Real.hasDerivAt_cos (Real.sqrt 2 * v)).comp v hlin + simp only [Function.comp_def] at this + rw [show -Real.sin (Real.sqrt 2 * v) * Real.sqrt 2 + = -(Real.sqrt 2 * Real.sin (Real.sqrt 2 * v)) by ring] at this + exact this + -- g1 = v * sin(√2 v) / √2 + have hg1 : HasDerivAt (fun v => v * Real.sin (Real.sqrt 2 * v) / Real.sqrt 2) + ((Real.sin (Real.sqrt 2 * v) + v * (Real.sqrt 2 * Real.cos (Real.sqrt 2 * v))) / Real.sqrt 2) + v := by + have hp : HasDerivAt (fun v => v * Real.sin (Real.sqrt 2 * v)) + (1 * Real.sin (Real.sqrt 2 * v) + v * (Real.sqrt 2 * Real.cos (Real.sqrt 2 * v))) v := + (hasDerivAt_id v).mul hsin + have := hp.div_const (Real.sqrt 2) + simpa [one_mul] using this + have hg2 : HasDerivAt (fun v => Real.cos (Real.sqrt 2 * v) / 2) + (-(Real.sqrt 2 * Real.sin (Real.sqrt 2 * v)) / 2) v := hcos.div_const 2 + have hsum := hg1.add hg2 + have hval : (Real.sin (Real.sqrt 2 * v) + + v * (Real.sqrt 2 * Real.cos (Real.sqrt 2 * v))) / Real.sqrt 2 + + -(Real.sqrt 2 * Real.sin (Real.sqrt 2 * v)) / 2 + = v * Real.cos (Real.sqrt 2 * v) := by + rw [div_add_div _ _ h2ne (by norm_num : (2:ℝ) ≠ 0), div_eq_iff (by positivity)] + linear_combination (-Real.sin (Real.sqrt 2 * v)) * hmul + have hfun : (fun v => v * Real.sin (Real.sqrt 2 * v) / Real.sqrt 2 + + Real.cos (Real.sqrt 2 * v) / 2) + = (fun v => v * Real.sin (Real.sqrt 2 * v) / Real.sqrt 2) + + (fun v => Real.cos (Real.sqrt 2 * v) / 2) := rfl + rw [hfun, ← hval] + exact hsum + +/-- Antiderivative for the shifted kernel: for fixed `u`, + `d/dv [u·sin(√2 v)/√2 − (v·sin(√2 v)/√2 + cos(√2 v)/2)] = (u − v)·cos(√2 v)`. -/ +lemma hasDerivAt_shift (u v : ℝ) : + HasDerivAt (fun v => u * Real.sin (Real.sqrt 2 * v) / Real.sqrt 2 + - (v * Real.sin (Real.sqrt 2 * v) / Real.sqrt 2 + Real.cos (Real.sqrt 2 * v) / 2)) + ((u - v) * Real.cos (Real.sqrt 2 * v)) v := by + have h2 : (0:ℝ) < Real.sqrt 2 := Real.sqrt_pos.mpr (by norm_num) + have h2ne : Real.sqrt 2 ≠ 0 := ne_of_gt h2 + have hmul : Real.sqrt 2 * Real.sqrt 2 = 2 := Real.mul_self_sqrt (by norm_num) + have hlin : HasDerivAt (fun v => Real.sqrt 2 * v) (Real.sqrt 2) v := by + simpa using (hasDerivAt_id v).const_mul (Real.sqrt 2) + have hsin : HasDerivAt (fun v => Real.sin (Real.sqrt 2 * v)) + (Real.sqrt 2 * Real.cos (Real.sqrt 2 * v)) v := by + have := (Real.hasDerivAt_sin (Real.sqrt 2 * v)).comp v hlin + simp only [Function.comp_def] at this + rw [mul_comm] at this + exact this + -- derivative of u * sin(√2 v)/√2 is u cos(√2 v) + have hA1 : HasDerivAt (fun v => u * Real.sin (Real.sqrt 2 * v) / Real.sqrt 2) + (u * Real.cos (Real.sqrt 2 * v)) v := by + have hd := (hsin.const_mul u).div_const (Real.sqrt 2) + have hval1 : u * (Real.sqrt 2 * Real.cos (Real.sqrt 2 * v)) / Real.sqrt 2 + = u * Real.cos (Real.sqrt 2 * v) := by + field_simp + rw [hval1] at hd + exact hd + have hA2 := hasDerivAt_vcos v + have hsub := hA1.sub hA2 + have hfun : (fun v => u * Real.sin (Real.sqrt 2 * v) / Real.sqrt 2 + - (v * Real.sin (Real.sqrt 2 * v) / Real.sqrt 2 + Real.cos (Real.sqrt 2 * v) / 2)) + = (fun v => u * Real.sin (Real.sqrt 2 * v) / Real.sqrt 2) + - (fun v => v * Real.sin (Real.sqrt 2 * v) / Real.sqrt 2 + + Real.cos (Real.sqrt 2 * v) / 2) := rfl + have hval2 : u * Real.cos (Real.sqrt 2 * v) - v * Real.cos (Real.sqrt 2 * v) + = (u - v) * Real.cos (Real.sqrt 2 * v) := by ring + rw [hfun, ← hval2] + exact hsub + +lemma extremalG_const {u : ℝ} (hu : |u| ≤ 1 / 2) : extremalG u = extremalG 0 := by + -- Direct evaluation: for |u| ≤ 1/2, extremalG u = c·(sin(1/√2)/√2 + cos(1/√2)), independent of u. + suffices h : ∀ w : ℝ, |w| ≤ 1/2 → extremalG w + = (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2))⁻¹ + * (Real.sin (1 / Real.sqrt 2) / Real.sqrt 2 + Real.cos (1 / Real.sqrt 2)) by + rw [h u hu, h 0 (by norm_num)] + intro w hw + have h2 : (0:ℝ) < Real.sqrt 2 := Real.sqrt_pos.mpr (by norm_num) + have h2ne : Real.sqrt 2 ≠ 0 := ne_of_gt h2 + have hmul : Real.sqrt 2 * Real.sqrt 2 = 2 := Real.mul_self_sqrt (by norm_num) + have hkey : Real.sqrt 2 * (1/2) = 1 / Real.sqrt 2 := by + rw [eq_div_iff h2ne]; nlinarith [hmul] + have hwabs : -(1:ℝ)/2 ≤ w ∧ w ≤ 1/2 := by + rw [abs_le] at hw; constructor <;> linarith [hw.1, hw.2] + unfold extremalG + -- extremalTest w = c cos(√2 w) + rw [extremalTest, ite_eq_left hw] + -- replace extremalTest v by c cos(√2 v) in the kernel + have hcongr : (∫ v in (-(1:ℝ)/2)..(1/2), |w - v| * extremalTest v) + = ∫ v in (-(1:ℝ)/2)..(1/2), + |w - v| * (Real.cos (Real.sqrt 2 * v) / (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2))) := by + apply intervalIntegral.integral_congr + intro v hv + rw [Set.uIcc_of_le (by norm_num : (-(1:ℝ)/2) ≤ 1/2), Set.mem_Icc] at hv + have hvabs : |v| ≤ 1 / 2 := by rw [abs_le]; constructor <;> linarith [hv.1, hv.2] + simp only + rw [extremalTest, ite_eq_left hvabs] + rw [hcongr] + -- pull constant c out + set c : ℝ := (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2))⁻¹ with hc + have hcne : Real.sqrt 2 * Real.sin (1 / Real.sqrt 2) ≠ 0 := by + -- shown nonzero from positivity below; here just need it for field_simp + have hsqrt2_lt : Real.sqrt 2 < 2 := by + have h := Real.sqrt_lt_sqrt (by norm_num : (0:ℝ) ≤ 2) (by norm_num : (2:ℝ) < 4) + rwa [show (4:ℝ) = 2^2 by norm_num, Real.sqrt_sq (by norm_num : (0:ℝ) ≤ 2)] at h + have hinv_pos : (0:ℝ) < 1 / Real.sqrt 2 := by positivity + have hinv_lt_pi : 1 / Real.sqrt 2 < Real.pi := by + have hlt1 : 1 / Real.sqrt 2 < 1 := by + rw [div_lt_one h2] + have : Real.sqrt 1 < Real.sqrt 2 := by apply Real.sqrt_lt_sqrt <;> norm_num + simpa using this + linarith [Real.pi_gt_three] + have hsin_pos : 0 < Real.sin (1 / Real.sqrt 2) := + Real.sin_pos_of_pos_of_lt_pi hinv_pos hinv_lt_pi + exact ne_of_gt (mul_pos h2 hsin_pos) + have hrw : (∫ v in (-(1:ℝ)/2)..(1/2), + |w - v| * (Real.cos (Real.sqrt 2 * v) / (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2)))) + = c * ∫ v in (-(1:ℝ)/2)..(1/2), |w - v| * Real.cos (Real.sqrt 2 * v) := by + rw [← intervalIntegral.integral_const_mul] + apply intervalIntegral.integral_congr + intro v _ + simp only [hc] + field_simp + rw [hrw] + -- compute the kernel integral K := ∫ |w-v| cos(√2 v) + -- = -cos(√2 w) + sin(1/√2)/√2 + cos(1/√2) + have hcontK : Continuous (fun v : ℝ => |w - v| * Real.cos (Real.sqrt 2 * v)) := by + exact ((continuous_const.sub continuous_id').abs).mul (by continuity) + have hcontvcos : Continuous (fun v : ℝ => (w - v) * Real.cos (Real.sqrt 2 * v)) := by + continuity + have hK : (∫ v in (-(1:ℝ)/2)..(1/2), |w - v| * Real.cos (Real.sqrt 2 * v)) + = -Real.cos (Real.sqrt 2 * w) + + Real.sin (1 / Real.sqrt 2) / Real.sqrt 2 + Real.cos (1 / Real.sqrt 2) := by + -- split at w + rw [← intervalIntegral.integral_add_adjacent_intervals + (a := -(1:ℝ)/2) (b := w) (c := 1/2) + (hcontK.intervalIntegrable _ _) (hcontK.intervalIntegrable _ _)] + -- on [-1/2,w], w - v ≥ 0 so |w-v| = w - v + have hleft : (∫ v in (-(1:ℝ)/2)..w, |w - v| * Real.cos (Real.sqrt 2 * v)) + = ∫ v in (-(1:ℝ)/2)..w, (w - v) * Real.cos (Real.sqrt 2 * v) := by + apply intervalIntegral.integral_congr + intro v hv + rw [Set.uIcc_of_le hwabs.1, Set.mem_Icc] at hv + simp only + rw [abs_of_nonneg (show (0:ℝ) ≤ w - v by linarith [hv.2])] + -- on [w,1/2], w - v ≤ 0 so |w-v| = -(w-v) = v - w + have hright : (∫ v in w..(1/2), |w - v| * Real.cos (Real.sqrt 2 * v)) + = ∫ v in w..(1/2), (-(w - v)) * Real.cos (Real.sqrt 2 * v) := by + apply intervalIntegral.integral_congr + intro v hv + rw [Set.uIcc_of_le hwabs.2, Set.mem_Icc] at hv + simp only + rw [abs_of_nonpos (show w - v ≤ 0 by linarith [hv.1])] + rw [hleft, hright] + -- antiderivative A v = w sin(√2 v)/√2 - (v sin(√2 v)/√2 + cos(√2 v)/2) + set A : ℝ → ℝ := fun v => w * Real.sin (Real.sqrt 2 * v) / Real.sqrt 2 + - (v * Real.sin (Real.sqrt 2 * v) / Real.sqrt 2 + Real.cos (Real.sqrt 2 * v) / 2) with hA + have hAderiv : ∀ x : ℝ, HasDerivAt A ((w - x) * Real.cos (Real.sqrt 2 * x)) x := + fun x => hasDerivAt_shift w x + -- left integral = A w - A (-1/2) + have hleftval : (∫ v in (-(1:ℝ)/2)..w, (w - v) * Real.cos (Real.sqrt 2 * v)) + = A w - A (-(1:ℝ)/2) := by + rw [intervalIntegral.integral_eq_sub_of_hasDerivAt (fun x _ => hAderiv x) + (hcontvcos.intervalIntegrable _ _)] + -- right integral of -(w-v)cos = A w - A(1/2) + have hrightval : (∫ v in w..(1/2), (-(w - v)) * Real.cos (Real.sqrt 2 * v)) + = A w - A (1/2) := by + have hneg : (∫ v in w..(1/2), (-(w - v)) * Real.cos (Real.sqrt 2 * v)) + = -(∫ v in w..(1/2), (w - v) * Real.cos (Real.sqrt 2 * v)) := by + rw [← intervalIntegral.integral_neg] + apply intervalIntegral.integral_congr + intro v _; ring + rw [hneg, intervalIntegral.integral_eq_sub_of_hasDerivAt (fun x _ => hAderiv x) + (hcontvcos.intervalIntegrable _ _)] + ring + rw [hleftval, hrightval] + -- A w = -cos(√2 w)/2 + have hAw : A w = -Real.cos (Real.sqrt 2 * w) / 2 := by + simp only [hA]; ring + -- A(1/2) : √2*(1/2) = 1/√2 + have hAhalf : A (1/2) = w * Real.sin (1 / Real.sqrt 2) / Real.sqrt 2 + - ((1/2) * Real.sin (1 / Real.sqrt 2) / Real.sqrt 2 + Real.cos (1 / Real.sqrt 2) / 2) := by + simp only [hA] + rw [hkey] + -- A(-1/2) : √2*(-1/2) = -(1/√2) + have hAmhalf : A (-(1:ℝ)/2) = -(w * Real.sin (1 / Real.sqrt 2)) / Real.sqrt 2 + - (-(1:ℝ)/2 * -Real.sin (1 / Real.sqrt 2) / Real.sqrt 2 + + Real.cos (1 / Real.sqrt 2) / 2) := by + simp only [hA] + have he : Real.sqrt 2 * (-(1:ℝ)/2) = -(1 / Real.sqrt 2) := by + rw [show (-(1:ℝ)/2) = -(1/2) by ring, mul_neg, hkey] + rw [he, Real.sin_neg, Real.cos_neg] + ring + rw [hAw, hAhalf, hAmhalf] + ring + rw [hK] + simp only [hc] + ring + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/MontgomeryTaylor/Basic.lean b/LeanPool/ZetaZeros/MontgomeryTaylor/Basic.lean new file mode 100644 index 0000000000..049f3f71ed --- /dev/null +++ b/LeanPool/ZetaZeros/MontgomeryTaylor/Basic.lean @@ -0,0 +1,71 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Axiom Math +-/ +module + +public import LeanPool.ZetaZeros.MontgomeryTaylor.Evaluation +public import LeanPool.ZetaZeros.MontgomeryTaylor.Reduction + +/-! +# The Montgomery--Taylor computation + +`A(Q_0) = C_MT`: the pair-correlation functional at the self-convolution of the extremal test +function is the Montgomery--Taylor constant. It is proved here rather than assumed. + +The three preceding files supply the pieces: the functional is the integral of `f_0` against `G` +(`Reduction`), `G` is constant (`AffineKernel`), and that constant is `C_MT` (`Evaluation`). With +`f_0` of total mass one, the integral collapses to the constant. +-/ + +@[expose] public section + +open MeasureTheory + +namespace ZetaZeros + +/-- **The Montgomery--Taylor computation** (`lem_montgomery_taylor`). See Montgomery, +*Distribution of the zeros of the Riemann zeta function*, Proc. ICM (Vancouver, 1974), Vol. 1, +379--381. -/ +@[zz_tag "lem_montgomery_taylor"] +theorem montgomeryTaylor : + extremalSelfConv 0 + 2 * ∫ α in (0:ℝ)..1, α * extremalSelfConv α = montgomeryTaylorConst := by + -- Step 1: reduce the LHS to a single interval integral of f₀·G. + rw [montgomeryTaylor_step1] + -- Step 2: G is constant on [-1/2,1/2], equal to G 0, so we may pull it out of the integral: + -- ∫_{-1/2}^{1/2} f₀(u) G(u) du = ∫_{-1/2}^{1/2} f₀(u) G(0) du = G(0) · ∫_{-1/2}^{1/2} f₀. + -- Since f₀ vanishes off [-1/2,1/2], ∫_{-1/2}^{1/2} f₀ = ∫_ℝ f₀ = 1 (integral_extremalTest). + -- Step 3: G(0) = montgomeryTaylorConst (extremalG_zero). + have hpull : + (∫ u in (-(1:ℝ)/2)..(1/2), extremalTest u * extremalG u) + = extremalG 0 * ∫ u in (-(1:ℝ)/2)..(1/2), extremalTest u := by + -- Rewrite the integrand using extremalG_const on [-1/2,1/2], then factor the constant out. + rw [show (fun u => extremalTest u * extremalG u) + = (fun u => extremalTest u * extremalG u) from rfl] + rw [intervalIntegral.integral_congr + (g := fun u => extremalG 0 * extremalTest u) + (f := fun u => extremalTest u * extremalG u) ?_] + · rw [intervalIntegral.integral_const_mul] + · intro u hu + rw [Set.uIcc_of_le (by norm_num : (-(1:ℝ)/2) ≤ 1/2), Set.mem_Icc] at hu + have : |u| ≤ 1 / 2 := by rw [abs_le]; constructor <;> linarith [hu.1, hu.2] + simp only + rw [extremalG_const this, mul_comm] + have hmass : (∫ u in (-(1:ℝ)/2)..(1/2), extremalTest u) = 1 := by + -- Off `[-1/2,1/2]` the integrand vanishes, so the interval integral equals the line + -- integral, which is one. + rw [intervalIntegral.integral_of_le (by norm_num : (-(1:ℝ)/2) ≤ 1/2), + ← MeasureTheory.integral_Icc_eq_integral_Ioc, + MeasureTheory.setIntegral_eq_integral_of_forall_compl_eq_zero, + integral_extremalTest] + intro x hx + apply extremalTest_eq_zero + rw [Set.mem_Icc, not_and_or] at hx + rw [lt_abs] + rcases hx with h | h + · right; push Not at h; linarith + · left; push Not at h; linarith + rw [hpull, hmass, mul_one, extremalG_zero] + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/MontgomeryTaylor/Evaluation.lean b/LeanPool/ZetaZeros/MontgomeryTaylor/Evaluation.lean new file mode 100644 index 0000000000..ad893d7b67 --- /dev/null +++ b/LeanPool/ZetaZeros/MontgomeryTaylor/Evaluation.lean @@ -0,0 +1,138 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Axiom Math +-/ +module + +public import LeanPool.ZetaZeros.MontgomeryTaylor.AffineKernel + +/-! +# The value of the constant + +`G 0 = C_MT`. With `G` constant this single evaluation pins the functional: +`G 0 = f_0 0 + integral of |v| f_0 v`, elementary by parts, and the trigonometry collapses. +-/ + +@[expose] public section + +open MeasureTheory + +namespace ZetaZeros + +/-- `G 0` is the Montgomery--Taylor constant. -/ +lemma extremalG_zero : extremalG 0 = montgomeryTaylorConst := by + have h2 : (0:ℝ) < Real.sqrt 2 := Real.sqrt_pos.mpr (by norm_num) + have h2ne : Real.sqrt 2 ≠ 0 := ne_of_gt h2 + have hmul : Real.sqrt 2 * Real.sqrt 2 = 2 := Real.mul_self_sqrt (by norm_num) + -- sin(1/√2) positivity + have hsqrt2_lt : Real.sqrt 2 < 2 := by + have h := Real.sqrt_lt_sqrt (by norm_num : (0:ℝ) ≤ 2) (by norm_num : (2:ℝ) < 4) + rwa [show (4:ℝ) = 2^2 by norm_num, Real.sqrt_sq (by norm_num : (0:ℝ) ≤ 2)] at h + have hinv_pos : (0:ℝ) < 1 / Real.sqrt 2 := by positivity + have hinv_lt_pi : 1 / Real.sqrt 2 < Real.pi := by + have hlt1 : 1 / Real.sqrt 2 < 1 := by + rw [div_lt_one h2] + have : Real.sqrt 1 < Real.sqrt 2 := by apply Real.sqrt_lt_sqrt <;> norm_num + simpa using this + linarith [Real.pi_gt_three] + have hsin_pos : 0 < Real.sin (1 / Real.sqrt 2) := + Real.sin_pos_of_pos_of_lt_pi hinv_pos hinv_lt_pi + have hsin_ne : Real.sin (1 / Real.sqrt 2) ≠ 0 := ne_of_gt hsin_pos + -- key: √2 * (1/2) = 1/√2 + have hkey : Real.sqrt 2 * (1/2) = 1 / Real.sqrt 2 := by + rw [eq_div_iff h2ne]; nlinarith [hmul] + -- abbreviation for the antiderivative + set F : ℝ → ℝ := + fun v => v * Real.sin (Real.sqrt 2 * v) / Real.sqrt 2 + Real.cos (Real.sqrt 2 * v) / 2 with hF + -- integrability of v cos(√2 v) + have hcont : Continuous (fun v => v * Real.cos (Real.sqrt 2 * v)) := by + continuity + unfold extremalG + -- the kernel integral: replace extremalTest v by cos(√2 v)/(√2 sin(1/√2)) on the interval + have hcongr : (∫ v in (-(1:ℝ)/2)..(1/2), |0 - v| * extremalTest v) + = ∫ v in (-(1:ℝ)/2)..(1/2), + |v| * (Real.cos (Real.sqrt 2 * v) / (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2))) := by + apply intervalIntegral.integral_congr + intro v hv + rw [Set.uIcc_of_le (by norm_num : (-(1:ℝ)/2) ≤ 1/2), Set.mem_Icc] at hv + have hvabs : |v| ≤ 1 / 2 := by rw [abs_le]; constructor <;> linarith [hv.1, hv.2] + simp only [zero_sub, abs_neg] + rw [extremalTest, ite_eq_left hvabs] + rw [hcongr] + -- pull constant c out + set c : ℝ := (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2))⁻¹ with hc + have hcpos : 0 < Real.sqrt 2 * Real.sin (1 / Real.sqrt 2) := mul_pos h2 hsin_pos + have hcne : Real.sqrt 2 * Real.sin (1 / Real.sqrt 2) ≠ 0 := ne_of_gt hcpos + have hrw : (∫ v in (-(1:ℝ)/2)..(1/2), + |v| * (Real.cos (Real.sqrt 2 * v) / (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2)))) + = c * ∫ v in (-(1:ℝ)/2)..(1/2), |v| * Real.cos (Real.sqrt 2 * v) := by + rw [← intervalIntegral.integral_const_mul] + apply intervalIntegral.integral_congr + intro v _ + simp only [hc] + field_simp + rw [hrw] + -- integrability of |v| cos(√2 v) + have hcontabs : Continuous (fun v : ℝ => |v| * Real.cos (Real.sqrt 2 * v)) := by + exact (continuous_abs.mul (by continuity)) + -- compute the elementary integral I := ∫ |v| cos(√2 v) = sin(1/√2)/√2 + cos(1/√2) - 1 + have hI : (∫ v in (-(1:ℝ)/2)..(1/2), |v| * Real.cos (Real.sqrt 2 * v)) + = Real.sin (1 / Real.sqrt 2) / Real.sqrt 2 + Real.cos (1 / Real.sqrt 2) - 1 := by + -- split at 0 + rw [← intervalIntegral.integral_add_adjacent_intervals + (a := -(1:ℝ)/2) (b := 0) (c := 1/2) + (hcontabs.intervalIntegrable _ _) (hcontabs.intervalIntegrable _ _)] + -- left piece: on [-1/2,0], |v| = -v + have hleft : (∫ v in (-(1:ℝ)/2)..(0:ℝ), |v| * Real.cos (Real.sqrt 2 * v)) + = ∫ v in (-(1:ℝ)/2)..(0:ℝ), (-v) * Real.cos (Real.sqrt 2 * v) := by + apply intervalIntegral.integral_congr + intro v hv + rw [Set.uIcc_of_le (by norm_num : (-(1:ℝ)/2) ≤ 0), Set.mem_Icc] at hv + simp only + rw [abs_of_nonpos (show v ≤ 0 by linarith [hv.2])] + have hright : (∫ v in (0:ℝ)..(1/2), |v| * Real.cos (Real.sqrt 2 * v)) + = ∫ v in (0:ℝ)..(1/2), v * Real.cos (Real.sqrt 2 * v) := by + apply intervalIntegral.integral_congr + intro v hv + rw [Set.uIcc_of_le (by norm_num : (0:ℝ) ≤ 1/2), Set.mem_Icc] at hv + simp only + rw [abs_of_nonneg (show (0:ℝ) ≤ v by linarith [hv.1])] + rw [hleft, hright] + -- ∫ v cos = F b - F a + have hFderiv : ∀ x ∈ Set.uIcc (0:ℝ) (1/2), HasDerivAt F (x * Real.cos (Real.sqrt 2 * x)) x := + fun x _ => hasDerivAt_vcos x + have hFderiv2 : ∀ x ∈ Set.uIcc (-(1:ℝ)/2) (0:ℝ), + HasDerivAt F (x * Real.cos (Real.sqrt 2 * x)) x := + fun x _ => hasDerivAt_vcos x + have hcont_vcos : Continuous (fun v : ℝ => v * Real.cos (Real.sqrt 2 * v)) := hcont + have hint_right : (∫ v in (0:ℝ)..(1/2), v * Real.cos (Real.sqrt 2 * v)) = F (1/2) - F 0 := + intervalIntegral.integral_eq_sub_of_hasDerivAt hFderiv (hcont_vcos.intervalIntegrable _ _) + have hint_left0 : (∫ v in (-(1:ℝ)/2)..(0:ℝ), v * Real.cos (Real.sqrt 2 * v)) + = F 0 - F (-(1:ℝ)/2) := + intervalIntegral.integral_eq_sub_of_hasDerivAt hFderiv2 (hcont_vcos.intervalIntegrable _ _) + have hint_left : (∫ v in (-(1:ℝ)/2)..(0:ℝ), (-v) * Real.cos (Real.sqrt 2 * v)) + = -(F 0 - F (-(1:ℝ)/2)) := by + rw [← hint_left0, ← intervalIntegral.integral_neg] + apply intervalIntegral.integral_congr + intro v _; ring + rw [hint_left, hint_right] + -- now evaluate F at endpoints + simp only [hF] + rw [show Real.sqrt 2 * (1/2 : ℝ) = 1 / Real.sqrt 2 from hkey] + rw [show Real.sqrt 2 * (-(1:ℝ)/2) = -(1 / Real.sqrt 2) by rw [← hkey]; ring] + rw [show Real.sqrt 2 * (0:ℝ) = 0 by ring] + rw [Real.sin_neg, Real.cos_neg, Real.sin_zero, Real.cos_zero] + field_simp + ring + rw [hI] + -- extremalTest 0 = 1 / (√2 sin(1/√2)) + rw [extremalTest, ite_eq_left (by norm_num : |(0:ℝ)| ≤ 1 / 2)] + rw [show Real.sqrt 2 * (0:ℝ) = 0 by ring, Real.cos_zero] + -- assemble + rw [montgomeryTaylorConst, Real.cot_eq_cos_div_sin] + simp only [hc] + field_simp + linear_combination (-Real.sin (1 / Real.sqrt 2)) * hmul + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/MontgomeryTaylor/Integrability.lean b/LeanPool/ZetaZeros/MontgomeryTaylor/Integrability.lean new file mode 100644 index 0000000000..85b436c908 --- /dev/null +++ b/LeanPool/ZetaZeros/MontgomeryTaylor/Integrability.lean @@ -0,0 +1,69 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Axiom Math +-/ +module + +public import LeanPool.ZetaZeros.MontgomeryTaylor.TestFunction + +/-! +# Interval-integrability of the integrands + +Side conditions only: `f_0` against the linear, `max` and shifted-`max` kernels must be +interval-integrable, and the outer integral needs a continuous integrand. +-/ + +@[expose] public section + +open MeasureTheory + +namespace ZetaZeros + +/-- `extremalTest` is interval-integrable on any interval. -/ +lemma extremalTest_intervalIntegrable_gen (p q : ℝ) : + IntervalIntegrable extremalTest MeasureTheory.volume p q := by + apply MeasureTheory.IntegrableOn.intervalIntegrable + apply extremalTest_integrableOn + rw [Set.uIcc_eq_union] + exact lt_of_le_of_lt (measure_union_le _ _) + (by exact ENNReal.add_lt_top.mpr ⟨measure_Icc_lt_top, measure_Icc_lt_top⟩) + +/-- `(β + t) · f₀ β` and `max(β+t, 0) · f₀ β` are interval-integrable on any interval. -/ +lemma linTest_intervalIntegrable (t p q : ℝ) : + IntervalIntegrable (fun β => (β + t) * extremalTest β) MeasureTheory.volume p q := + (extremalTest_intervalIntegrable_gen p q).continuousOn_mul + (by fun_prop : ContinuousOn (fun β => β + t) (Set.uIcc p q)) + +lemma maxTest_intervalIntegrable (t p q : ℝ) : + IntervalIntegrable (fun β => max (β + t) 0 * extremalTest β) MeasureTheory.volume p q := + (extremalTest_intervalIntegrable_gen p q).continuousOn_mul + (by fun_prop : ContinuousOn (fun β => max (β + t) 0) (Set.uIcc p q)) + +/-- `max(c - v, 0) · f₀ v` is interval-integrable in `v` on any interval. -/ +lemma maxSubTest_intervalIntegrable (c p q : ℝ) : + IntervalIntegrable (fun v => max (c - v) 0 * extremalTest v) MeasureTheory.volume p q := + (extremalTest_intervalIntegrable_gen p q).continuousOn_mul + (by fun_prop : ContinuousOn (fun v => max (c - v) 0) (Set.uIcc p q)) + +/-- For a jointly continuous kernel `K`, the outer integrand + `t ↦ f₀ t · ∫ v in -1/2..1/2, K t v · f₀ v` is interval-integrable on `[-1/2,1/2]`. -/ +lemma outer_intervalIntegrable (K : ℝ → ℝ → ℝ) (hK : Continuous (Function.uncurry K)) : + IntervalIntegrable + (fun t => extremalTest t * ∫ v in (-(1:ℝ)/2)..(1/2), K t v * extremalTest v) + MeasureTheory.volume (-(1:ℝ)/2) (1/2) := by + apply ContinuousOn.intervalIntegrable + apply extremalTest_continuousOn.mul + -- rewrite the inner kernel to the continuous cos-form on [-1/2,1/2] + have hcont : Continuous (fun t => ∫ v in (-(1:ℝ)/2)..(1/2), + K t v * (Real.cos (Real.sqrt 2 * v) / (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2)))) := by + apply intervalIntegral.continuous_parametric_intervalIntegral_of_continuous' + exact (hK.comp (continuous_fst.prodMk continuous_snd)).mul (by fun_prop) + apply ContinuousOn.congr hcont.continuousOn + intro t _ + apply intervalIntegral.integral_congr + intro v hv + simp only + rw [extremalTest_eqOn_uIcc hv] + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/MontgomeryTaylor/Reduction.lean b/LeanPool/ZetaZeros/MontgomeryTaylor/Reduction.lean new file mode 100644 index 0000000000..278ebd9290 --- /dev/null +++ b/LeanPool/ZetaZeros/MontgomeryTaylor/Reduction.lean @@ -0,0 +1,378 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Axiom Math +-/ +module + +public import LeanPool.ZetaZeros.MontgomeryTaylor.AffineKernel +public import LeanPool.ZetaZeros.MontgomeryTaylor.Integrability + +/-! +# Reducing the functional to a single integral against `G` + +`Q_0 0` is the integral of `f_0` squared and the moment term is the double integral of +`|u - v| f_0 u f_0 v`; added, they are the integral of `f_0` against `G`. +-/ + +@[expose] public section + +open MeasureTheory + +namespace ZetaZeros + +/-- The self-convolution restricted to the support interval. -/ +lemma extremalSelfConv_eq_interval (α : ℝ) : + extremalSelfConv α = ∫ t in (-(1:ℝ)/2)..(1/2), extremalTest t * extremalTest (α - t) := by + rw [extremalSelfConv] + rw [intervalIntegral.integral_of_le (by norm_num : (-(1:ℝ)/2) ≤ 1/2), + ← MeasureTheory.integral_Icc_eq_integral_Ioc, + MeasureTheory.setIntegral_eq_integral_of_forall_compl_eq_zero] + intro x hx + rw [Set.mem_Icc, not_and_or] at hx + have hxout : 1 / 2 < |x| := by + rw [lt_abs] + rcases hx with h | h + · right; push Not at h; linarith + · left; push Not at h; linarith + rw [extremalTest_eq_zero hxout, zero_mul] + +/-- Part (I): `Q₀(0) = ∫_{-1/2}^{1/2} f₀(u)² du`. -/ +lemma step1_selfconv0 : + extremalSelfConv 0 = ∫ u in (-(1:ℝ)/2)..(1/2), extremalTest u * extremalTest u := by + rw [extremalSelfConv] + -- extremalTest (0 - t) = extremalTest (-t) = extremalTest t + have hev : (fun t => extremalTest t * extremalTest (0 - t)) + = (fun t => extremalTest t * extremalTest t) := by + funext t + rw [zero_sub, extremalTest_even] + rw [hev] + -- convert full-line integral to the interval via support + rw [intervalIntegral.integral_of_le (by norm_num : (-(1:ℝ)/2) ≤ 1/2), + ← MeasureTheory.integral_Icc_eq_integral_Ioc, + MeasureTheory.setIntegral_eq_integral_of_forall_compl_eq_zero] + intro x hx + rw [Set.mem_Icc, not_and_or] at hx + have hxout : 1 / 2 < |x| := by + rw [lt_abs] + rcases hx with h | h + · right; push Not at h; linarith + · left; push Not at h; linarith + rw [extremalTest_eq_zero hxout, mul_zero] + +/-- Inner reduction: substituting `β = α - t` and using the support of `extremalTest`, + `∫ α in 0..1, α · f₀(α - t) = ∫ β in -1/2..1/2, max(β+t, 0) · f₀(β)` for `|t| ≤ 1/2`. -/ +lemma inner_reduce {t : ℝ} (ht : |t| ≤ 1 / 2) : + (∫ α in (0:ℝ)..1, α * extremalTest (α - t)) + = ∫ β in (-(1:ℝ)/2)..(1/2), max (β + t) 0 * extremalTest β := by + have htle : -(1:ℝ)/2 ≤ t ∧ t ≤ 1/2 := by + rw [abs_le] at ht; constructor <;> linarith [ht.1, ht.2] + -- substitute β = α - t + have hsub : (∫ α in (0:ℝ)..1, α * extremalTest (α - t)) + = ∫ β in (0 - t)..(1 - t), (β + t) * extremalTest β := by + have h := intervalIntegral.integral_comp_sub_right + (fun β => (β + t) * extremalTest β) t (a := (0:ℝ)) (b := 1) + simp only [sub_add_cancel] at h + rw [← h] + rw [hsub, show (0:ℝ) - t = -t from by ring] + -- LHS: ∫ β in -t..(1-t), (β+t)·g β. Split at 1/2 and drop [1/2, 1-t] where g = 0. + have hmt_le_half : -t ≤ (1:ℝ)/2 := by linarith [htle.1] + have hhalf_le : (1:ℝ)/2 ≤ 1 - t := by linarith [htle.2] + rw [← intervalIntegral.integral_add_adjacent_intervals + (a := -t) (b := (1:ℝ)/2) (c := 1 - t) + (linTest_intervalIntegrable t _ _) (linTest_intervalIntegrable t _ _)] + -- the [1/2, 1-t] piece vanishes: g β = 0 there + have hdrop : (∫ β in ((1:ℝ)/2)..(1 - t), (β + t) * extremalTest β) = 0 := by + rw [intervalIntegral.integral_of_le hhalf_le, + MeasureTheory.integral_eq_zero_of_ae] + rw [Filter.eventuallyEq_iff_exists_mem] + refine ⟨Set.Ioc ((1:ℝ)/2) (1-t), ?_, ?_⟩ + · exact MeasureTheory.self_mem_ae_restrict measurableSet_Ioc + · intro β hβ + rw [Set.mem_Ioc] at hβ + have hb0 : (1:ℝ)/2 < |β| := by rw [lt_abs]; left; linarith [hβ.1] + change (β + t) * extremalTest β = 0 + rw [extremalTest_eq_zero hb0, mul_zero] + rw [hdrop, add_zero] + -- RHS: ∫ β in -1/2..1/2, max(β+t,0)·g β. Split at -t; drop [-1/2, -t] where max = 0. + rw [← intervalIntegral.integral_add_adjacent_intervals + (a := -(1:ℝ)/2) (b := -t) (c := (1:ℝ)/2) + (maxTest_intervalIntegrable t _ _) (maxTest_intervalIntegrable t _ _)] + have hdrop2 : (∫ β in (-(1:ℝ)/2)..(-t), max (β + t) 0 * extremalTest β) = 0 := by + rw [intervalIntegral.integral_of_le (by linarith [htle.1] : -(1:ℝ)/2 ≤ -t), + MeasureTheory.integral_eq_zero_of_ae] + rw [Filter.eventuallyEq_iff_exists_mem] + refine ⟨Set.Ioc (-(1:ℝ)/2) (-t), ?_, ?_⟩ + · exact MeasureTheory.self_mem_ae_restrict measurableSet_Ioc + · intro β hβ + rw [Set.mem_Ioc] at hβ + have hbt : β + t ≤ 0 := by linarith [hβ.2] + change max (β + t) 0 * extremalTest β = 0 + rw [max_eq_right hbt, zero_mul] + rw [hdrop2, zero_add] + -- the two middle pieces on [-t, 1/2] agree: max(β+t,0) = β+t since β+t ≥ 0. + apply intervalIntegral.integral_congr + intro β hβ + rw [Set.uIcc_of_le hmt_le_half, Set.mem_Icc] at hβ + simp only + rw [max_eq_left (by linarith [hβ.1] : (0:ℝ) ≤ β + t)] + +/-- Part (II): `2 ∫₀¹ α Q₀(α) dα = ∫∫ |u-v| f₀(u) f₀(v)`. -/ +lemma step1_double : + 2 * ∫ α in (0:ℝ)..1, α * extremalSelfConv α + = ∫ u in (-(1:ℝ)/2)..(1/2), + extremalTest u * ∫ v in (-(1:ℝ)/2)..(1/2), |u - v| * extremalTest v := by + set g := extremalTest with hg + -- The common middle expression: `∫∫ max(t+β,0) g(t) g(β)`. + set a : ℝ := -(1:ℝ)/2 with ha + set b : ℝ := (1:ℝ)/2 with hb + have hab : a ≤ b := by rw [ha, hb]; norm_num + -- Continuity/integrability facts on the fixed interval. + have hgIntOn : ∀ (s : Set ℝ), volume s < ⊤ → IntegrableOn g s volume := + fun s hs => extremalTest_integrableOn hs + have h2 : (0:ℝ) < Real.sqrt 2 := Real.sqrt_pos.mpr (by norm_num) + have hsqrt2_lt : Real.sqrt 2 < 2 := by + have h := Real.sqrt_lt_sqrt (by norm_num : (0:ℝ) ≤ 2) (by norm_num : (2:ℝ) < 4) + rwa [show (4:ℝ) = 2^2 by norm_num, Real.sqrt_sq (by norm_num : (0:ℝ) ≤ 2)] at h + have hinv_pos : (0:ℝ) < 1 / Real.sqrt 2 := by positivity + have hinv_lt_pi : 1 / Real.sqrt 2 < Real.pi := by + have hlt1 : 1 / Real.sqrt 2 < 1 := by + rw [div_lt_one h2] + have : Real.sqrt 1 < Real.sqrt 2 := by apply Real.sqrt_lt_sqrt <;> norm_num + simpa using this + linarith [Real.pi_gt_three] + have hsin_pos : 0 < Real.sin (1 / Real.sqrt 2) := + Real.sin_pos_of_pos_of_lt_pi hinv_pos hinv_lt_pi + have hden_pos : 0 < Real.sqrt 2 * Real.sin (1 / Real.sqrt 2) := mul_pos h2 hsin_pos + -- Step 1: restrict inner conv and pull α inside. + have hL1 : (∫ α in (0:ℝ)..1, α * extremalSelfConv α) + = ∫ α in (0:ℝ)..1, ∫ t in a..b, α * (g t * g (α - t)) := by + apply intervalIntegral.integral_congr + intro α _ + simp only + rw [extremalSelfConv_eq_interval, ← ha, ← hb, hg] + rw [intervalIntegral.integral_const_mul] + -- integrability of the joint integrand on the rectangle + have hMeas : Measurable (Function.uncurry (fun α t => α * (g t * g (α - t)))) := by + unfold Function.uncurry + apply Measurable.mul measurable_fst + apply Measurable.mul + · exact extremalTest_measurable.comp measurable_snd + · exact extremalTest_measurable.comp (measurable_fst.sub measurable_snd) + set C : ℝ := (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2))⁻¹ with hC + have hCnn : 0 ≤ C := by rw [hC]; exact le_of_lt (inv_pos.mpr hden_pos) + have hIntegrRect : IntegrableOn (Function.uncurry (fun α t => α * (g t * g (α - t)))) + (Set.uIoc (0:ℝ) 1 ×ˢ Set.uIoc a b) volume := by + apply MeasureTheory.IntegrableOn.of_bound _ hMeas.aestronglyMeasurable (1 * (C * C)) + · filter_upwards [MeasureTheory.ae_restrict_mem + ((measurableSet_uIoc).prod (measurableSet_uIoc))] with p hp + rw [Set.mem_prod, Set.uIoc, Set.uIoc, Set.mem_Ioc, Set.mem_Ioc] at hp + simp only [min_eq_left (by norm_num : (0:ℝ) ≤ 1), + max_eq_right (by norm_num : (0:ℝ) ≤ 1)] at hp + simp only [Function.uncurry, Real.norm_eq_abs, abs_mul] + apply mul_le_mul _ _ (by positivity) (by norm_num) + · rw [abs_of_pos hp.1.1] + exact hp.1.2.trans (by norm_num) + · exact mul_le_mul (extremalTest_abs_le _) (extremalTest_abs_le _) (abs_nonneg _) hCnn + · rw [Measure.volume_eq_prod] + refine lt_of_le_of_lt (Measure.prod_prod_le _ _) ?_ + apply ENNReal.mul_lt_top + · rw [Set.uIoc]; exact measure_Ioc_lt_top + · rw [Set.uIoc]; exact measure_Ioc_lt_top + -- Step 2 (Fubini): swap the two interval integrals. + have hL2 : (∫ α in (0:ℝ)..1, ∫ t in a..b, α * (g t * g (α - t))) + = ∫ t in a..b, ∫ α in (0:ℝ)..1, α * (g t * g (α - t)) := by + -- convert interval integrals to set integrals over Ioc, then apply the product swap. + rw [intervalIntegral.integral_of_le (by norm_num : (0:ℝ) ≤ 1)] + have hcongr1 : ∀ α, (∫ t in a..b, α * (g t * g (α - t))) + = ∫ t in Set.Ioc a b, α * (g t * g (α - t)) := + fun α => intervalIntegral.integral_of_le hab + have hcongr2 : ∀ t, (∫ α in (0:ℝ)..1, α * (g t * g (α - t))) + = ∫ α in Set.Ioc (0:ℝ) 1, α * (g t * g (α - t)) := + fun t => intervalIntegral.integral_of_le (by norm_num) + simp_rw [hcongr1, hcongr2] + rw [intervalIntegral.integral_of_le hab] + -- rephrase as integral against restricted measures + have hInt : Integrable (Function.uncurry (fun α t => α * (g t * g (α - t)))) + ((volume.restrict (Set.Ioc (0:ℝ) 1)).prod (volume.restrict (Set.Ioc a b))) := by + rw [Measure.prod_restrict] + have : Set.uIoc (0:ℝ) 1 = Set.Ioc (0:ℝ) 1 := Set.uIoc_of_le (by norm_num) + have hb2 : Set.uIoc a b = Set.Ioc a b := Set.uIoc_of_le hab + rw [this, hb2] at hIntegrRect + exact hIntegrRect + have := MeasureTheory.integral_integral_swap (μ := volume.restrict (Set.Ioc (0:ℝ) 1)) + (ν := volume.restrict (Set.Ioc a b)) hInt + simpa only [Function.uncurry] using this + -- Step 3: `∫ α, α Q₀ α = ∫ t, g t · ∫ v, max(t - v, 0) · g v`. + have hM : (∫ α in (0:ℝ)..1, α * extremalSelfConv α) + = ∫ t in a..b, g t * ∫ v in a..b, max (t - v) 0 * g v := by + rw [hL1, hL2] + apply intervalIntegral.integral_congr + intro t ht + rw [Set.uIcc_of_le hab, ha, hb, Set.mem_Icc] at ht + have htabs : |t| ≤ 1/2 := by rw [abs_le]; constructor <;> linarith [ht.1, ht.2] + -- pull g t out of the inner integral + have hpull : (∫ α in (0:ℝ)..1, α * (g t * g (α - t))) + = g t * ∫ α in (0:ℝ)..1, α * g (α - t) := by + rw [← intervalIntegral.integral_const_mul] + apply intervalIntegral.integral_congr + intro α _; ring + simp only + rw [hpull, hg, inner_reduce htabs] + -- reflect v → -v : ∫ β, max(β+t,0) g β = ∫ v, max(t-v,0) g v + congr 1 + -- apply integral_comp_neg to `f v = max (t - v) 0 * extremalTest v` + have hrefl := intervalIntegral.integral_comp_neg + (fun v => max (t - v) 0 * extremalTest v) (a := -(1:ℝ)/2) (b := (1:ℝ)/2) + rw [show (-(-(1:ℝ)/2)) = (1:ℝ)/2 from by norm_num, + show (-((1:ℝ)/2)) = -(1:ℝ)/2 from by norm_num] at hrefl + rw [ha, hb, ← hrefl] + apply intervalIntegral.integral_congr + intro β _ + simp only + rw [extremalTest_even, sub_neg_eq_add, add_comm] + -- Step 4 (symmetry): swapping `t ↔ v` in the double integral of `K t v = g t g v max(t-v,0)`. + have hsymm : (∫ t in a..b, g t * ∫ v in a..b, max (t - v) 0 * g v) + = ∫ t in a..b, g t * ∫ v in a..b, max (v - t) 0 * g v := by + -- pull `g t` inside on both sides to get the double integral of `φ t v = g t g v max(t-v,0)` + have hpullL : (∫ t in a..b, g t * ∫ v in a..b, max (t - v) 0 * g v) + = ∫ t in a..b, ∫ v in a..b, g t * g v * max (t - v) 0 := by + apply intervalIntegral.integral_congr; intro t _ + simp only + rw [← intervalIntegral.integral_const_mul] + apply intervalIntegral.integral_congr; intro v _; simp only; ring + have hpullR : (∫ t in a..b, g t * ∫ v in a..b, max (v - t) 0 * g v) + = ∫ t in a..b, ∫ v in a..b, g v * g t * max (v - t) 0 := by + apply intervalIntegral.integral_congr; intro t _ + simp only + rw [← intervalIntegral.integral_const_mul] + apply intervalIntegral.integral_congr; intro v _; simp only; ring + rw [hpullL, hpullR] + -- Fubini swap `∫ t ∫ v φ t v = ∫ v ∫ t φ t v`, then rename dummy variables. + set φ : ℝ → ℝ → ℝ := fun t v => g t * g v * max (t - v) 0 with hφ + -- measurability of the uncurried kernel + have hMeasφ : Measurable (Function.uncurry φ) := by + unfold Function.uncurry + simp only [hφ] + apply Measurable.mul + · exact (extremalTest_measurable.comp measurable_fst).mul + (extremalTest_measurable.comp measurable_snd) + · exact (measurable_fst.sub measurable_snd).max measurable_const + -- integrability on the square + have hIntφ : IntegrableOn (Function.uncurry φ) + (Set.uIoc a b ×ˢ Set.uIoc a b) volume := by + apply MeasureTheory.IntegrableOn.of_bound _ hMeasφ.aestronglyMeasurable (C * C * 1) + · filter_upwards [MeasureTheory.ae_restrict_mem + ((measurableSet_uIoc).prod (measurableSet_uIoc))] with p hp + rw [Set.mem_prod] at hp + rw [show Set.uIoc a b = Set.Ioc a b from Set.uIoc_of_le hab] at hp + rw [Set.mem_Ioc, Set.mem_Ioc, ha, hb] at hp + simp only [hφ, Function.uncurry, Real.norm_eq_abs, abs_mul] + apply mul_le_mul + · exact mul_le_mul (extremalTest_abs_le _) (extremalTest_abs_le _) + (abs_nonneg _) hCnn + · rw [abs_of_nonneg (le_max_right _ _)] + rw [max_le_iff] + exact ⟨by linarith [hp.1.2, hp.2.1], by norm_num⟩ + · positivity + · positivity + · rw [Measure.volume_eq_prod] + refine lt_of_le_of_lt (Measure.prod_prod_le _ _) ?_ + apply ENNReal.mul_lt_top + · rw [Set.uIoc]; exact measure_Ioc_lt_top + · rw [Set.uIoc]; exact measure_Ioc_lt_top + -- fold both integrands into `φ` + have hLHS : (∫ t in a..b, ∫ v in a..b, g t * g v * max (t - v) 0) + = ∫ t in a..b, ∫ v in a..b, φ t v := by + apply intervalIntegral.integral_congr; intro t _ + apply intervalIntegral.integral_congr; intro v _ + simp only [hφ] + have hRHS : (∫ t in a..b, ∫ v in a..b, g v * g t * max (v - t) 0) + = ∫ t in a..b, ∫ v in a..b, φ v t := by + apply intervalIntegral.integral_congr; intro t _ + apply intervalIntegral.integral_congr; intro v _ + simp only [hφ] + rw [hLHS, hRHS] + -- convert both nested interval integrals over a..b to set integrals over Ioc a b + have hc1 : ∀ t, (∫ v in a..b, φ t v) = ∫ v in Set.Ioc a b, φ t v := + fun t => intervalIntegral.integral_of_le hab + have hc2 : ∀ t, (∫ v in a..b, φ v t) = ∫ v in Set.Ioc a b, φ v t := + fun t => intervalIntegral.integral_of_le hab + have hL : (∫ t in a..b, ∫ v in a..b, φ t v) + = ∫ t in Set.Ioc a b, ∫ v in Set.Ioc a b, φ t v := by + rw [intervalIntegral.integral_of_le hab]; simp_rw [hc1] + have hR : (∫ t in a..b, ∫ v in a..b, φ v t) + = ∫ t in Set.Ioc a b, ∫ v in Set.Ioc a b, φ v t := by + rw [intervalIntegral.integral_of_le hab]; simp_rw [hc2] + rw [hL, hR] + -- now goal: ∫ t in Ioc, ∫ v in Ioc, φ t v = ∫ t in Ioc, ∫ v in Ioc, φ v t + rw [show Set.uIoc a b = Set.Ioc a b from Set.uIoc_of_le hab] at hIntφ + have hInt : Integrable (Function.uncurry φ) + ((volume.restrict (Set.Ioc a b)).prod (volume.restrict (Set.Ioc a b))) := by + rw [Measure.prod_restrict] + exact hIntφ + have hswap := MeasureTheory.integral_integral_swap + (μ := volume.restrict (Set.Ioc a b)) (ν := volume.restrict (Set.Ioc a b)) hInt + rw [show (∫ t in Set.Ioc a b, ∫ v in Set.Ioc a b, φ t v) + = ∫ t, ∫ v, φ t v ∂(volume.restrict (Set.Ioc a b)) + ∂(volume.restrict (Set.Ioc a b)) from rfl] + rw [hswap] + -- Step 5: `2·(∫ α Q₀) = ∫∫ (max(t-v,0)+max(v-t,0)) g t g v = ∫∫ |t-v| g t g v = RHS`. + rw [hM, two_mul] + nth_rewrite 2 [hsymm] + rw [← intervalIntegral.integral_add ?hi1 ?hi2] + · apply intervalIntegral.integral_congr + intro t _ + simp only + rw [← mul_add, ← intervalIntegral.integral_add + (maxSubTest_intervalIntegrable t a b) ?_] + · congr 1 + apply intervalIntegral.integral_congr + intro v _ + simp only + rw [← add_mul, ← max_zero_add_max_neg_zero_eq_abs_self (t - v), + show -(t - v) = v - t from by ring] + · exact (extremalTest_intervalIntegrable_gen a b).continuousOn_mul + (by fun_prop : ContinuousOn (fun v => max (v - t) 0) (Set.uIcc a b)) + case hi1 => + rw [ha, hb, hg] + exact outer_intervalIntegrable (fun t v => max (t - v) 0) (by fun_prop) + case hi2 => + rw [ha, hb, hg] + exact outer_intervalIntegrable (fun t v => max (v - t) 0) (by fun_prop) + +lemma montgomeryTaylor_step1 : + extremalSelfConv 0 + 2 * ∫ α in (0:ℝ)..1, α * extremalSelfConv α + = ∫ u in (-(1:ℝ)/2)..(1/2), extremalTest u * extremalG u := by + rw [step1_selfconv0, step1_double] + -- Integrability of the two pieces of `f₀·G`. + have hint1 : IntervalIntegrable (fun u => extremalTest u * extremalTest u) + MeasureTheory.volume (-(1:ℝ)/2) (1/2) := + (extremalTest_continuousOn.mul extremalTest_continuousOn).intervalIntegrable + have hint2 : IntervalIntegrable + (fun u => extremalTest u * ∫ v in (-(1:ℝ)/2)..(1/2), |u - v| * extremalTest v) + MeasureTheory.volume (-(1:ℝ)/2) (1/2) := by + -- rewrite the inner kernel to its continuous form on the interval, giving a jointly + -- continuous parametric integral, then multiply by the continuous `extremalTest`. + have hcont : Continuous (fun u => ∫ v in (-(1:ℝ)/2)..(1/2), + |u - v| * (Real.cos (Real.sqrt 2 * v) / (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2)))) := by + apply intervalIntegral.continuous_parametric_intervalIntegral_of_continuous' + (f := fun u v => + |u - v| * (Real.cos (Real.sqrt 2 * v) / (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2)))) + fun_prop + apply ContinuousOn.intervalIntegrable + apply extremalTest_continuousOn.mul + apply ContinuousOn.congr (hcont.continuousOn) + intro u _ + apply intervalIntegral.integral_congr + intro v hv + simp only + rw [extremalTest_eqOn_uIcc hv] + -- Now split the integral of the sum on the RHS. + rw [show (fun u => extremalTest u * extremalG u) + = (fun u => extremalTest u * extremalTest u + + extremalTest u * ∫ v in (-(1:ℝ)/2)..(1/2), |u - v| * extremalTest v) from by + funext u; rw [extremalG, mul_add]] + rw [intervalIntegral.integral_add hint1 hint2] + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/MontgomeryTaylor/TestFunction.lean b/LeanPool/ZetaZeros/MontgomeryTaylor/TestFunction.lean new file mode 100644 index 0000000000..120ffe2a5c --- /dev/null +++ b/LeanPool/ZetaZeros/MontgomeryTaylor/TestFunction.lean @@ -0,0 +1,102 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Axiom Math +-/ +module + +public import LeanPool.ZetaZeros.Zeta.Basic +public import LeanPool.ZetaZeros.Zeta.Mass + +/-! +# Properties of the extremal test function + +`f_0` is even, vanishes off `[-1/2, 1/2]`, has total mass one, and is measurable, bounded and +continuous on its support. Every later step rests on these, and none mentions the functional. +-/ + +@[expose] public section + +open MeasureTheory + +namespace ZetaZeros + +/-- `f₀` is even. Immediate from `cos` being even and `|-x| = |x|`. -/ +lemma extremalTest_even (x : ℝ) : extremalTest (-x) = extremalTest x := by + simp [extremalTest, abs_neg, mul_neg, Real.cos_neg] + +/-- `f₀` is supported in `[-1/2, 1/2]`: it vanishes outside. -/ +lemma extremalTest_eq_zero {x : ℝ} (hx : 1 / 2 < |x|) : extremalTest x = 0 := by + rw [extremalTest, ite_eq_right (not_le.mpr hx)] + + +/-- On the closed interval `[-1/2,1/2]`, `extremalTest` agrees with the continuous + `cos(√2 ·)/(√2 sin(1/√2))` (the jump of the indicator is strictly outside). -/ +lemma extremalTest_eqOn_uIcc : + Set.EqOn extremalTest + (fun x => Real.cos (Real.sqrt 2 * x) / (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2))) + (Set.uIcc (-(1:ℝ)/2) (1/2)) := by + intro x hx + rw [Set.uIcc_of_le (by norm_num : (-(1:ℝ)/2) ≤ 1/2), Set.mem_Icc] at hx + have hxabs : |x| ≤ 1 / 2 := by rw [abs_le]; constructor <;> linarith [hx.1, hx.2] + rw [extremalTest, ite_eq_left hxabs] + +/-- `extremalTest` is continuous on `[-1/2,1/2]`. -/ +lemma extremalTest_continuousOn : + ContinuousOn extremalTest (Set.uIcc (-(1:ℝ)/2) (1/2)) := by + have hc : Continuous + (fun x => Real.cos (Real.sqrt 2 * x) / (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2))) := by + fun_prop + exact hc.continuousOn.congr extremalTest_eqOn_uIcc + +/-- `extremalTest` is interval-integrable on `[-1/2,1/2]`. -/ +lemma extremalTest_intervalIntegrable : + IntervalIntegrable extremalTest MeasureTheory.volume (-(1:ℝ)/2) (1/2) := + extremalTest_continuousOn.intervalIntegrable + +/-- `extremalTest` is measurable. -/ +lemma extremalTest_measurable : Measurable extremalTest := by + have hcont : Continuous + (fun x => Real.cos (Real.sqrt 2 * x) / (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2))) := by + fun_prop + have hset : MeasurableSet {x : ℝ | |x| ≤ 1 / 2} := by + have : {x : ℝ | |x| ≤ 1 / 2} = {x : ℝ | |x| ≤ 1 / 2} := rfl + exact measurableSet_le (by fun_prop) measurable_const + unfold extremalTest + exact Measurable.ite hset hcont.measurable measurable_const + +/-- `extremalTest` is bounded: `|extremalTest x| ≤ C` with + `C = (√2 sin(1/√2))⁻¹`. -/ +lemma extremalTest_abs_le (x : ℝ) : + |extremalTest x| ≤ (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2))⁻¹ := by + have h2 : (0:ℝ) < Real.sqrt 2 := Real.sqrt_pos.mpr (by norm_num) + have hsqrt2_lt : Real.sqrt 2 < 2 := by + have h := Real.sqrt_lt_sqrt (by norm_num : (0:ℝ) ≤ 2) (by norm_num : (2:ℝ) < 4) + rwa [show (4:ℝ) = 2^2 by norm_num, Real.sqrt_sq (by norm_num : (0:ℝ) ≤ 2)] at h + have hinv_pos : (0:ℝ) < 1 / Real.sqrt 2 := by positivity + have hinv_lt_pi : 1 / Real.sqrt 2 < Real.pi := by + have hlt1 : 1 / Real.sqrt 2 < 1 := by + rw [div_lt_one h2] + have : Real.sqrt 1 < Real.sqrt 2 := by apply Real.sqrt_lt_sqrt <;> norm_num + simpa using this + linarith [Real.pi_gt_three] + have hsin_pos : 0 < Real.sin (1 / Real.sqrt 2) := + Real.sin_pos_of_pos_of_lt_pi hinv_pos hinv_lt_pi + have hden : 0 < Real.sqrt 2 * Real.sin (1 / Real.sqrt 2) := mul_pos h2 hsin_pos + unfold extremalTest + by_cases hx : |x| ≤ 1 / 2 + · rw [ite_eq_left hx, abs_div, abs_of_pos hden, ← one_div, div_le_div_iff_of_pos_right hden] + exact Real.abs_cos_le_one _ + · rw [ite_eq_right hx, abs_zero] + positivity + +/-- `extremalTest` is `IntegrableOn` any measurable set of finite measure. -/ +lemma extremalTest_integrableOn {s : Set ℝ} (hs : volume s < ⊤) : + IntegrableOn extremalTest s volume := by + apply MeasureTheory.IntegrableOn.of_bound hs + (extremalTest_measurable.aestronglyMeasurable) + ((Real.sqrt 2 * Real.sin (1 / Real.sqrt 2))⁻¹) + filter_upwards with x + simpa [Real.norm_eq_abs] using extremalTest_abs_le x + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Numeric/MontgomeryTaylor.lean b/LeanPool/ZetaZeros/Numeric/MontgomeryTaylor.lean new file mode 100644 index 0000000000..ca831d0b05 --- /dev/null +++ b/LeanPool/ZetaZeros/Numeric/MontgomeryTaylor.lean @@ -0,0 +1,181 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public import Mathlib.Analysis.SpecialFunctions.Trigonometric.Series +public import LeanPool.ZetaZeros.Meta.Attr + +/-! +# The Montgomery–Taylor constant is less than `1.3275` + +The single numerical input to both headline bounds. Since `(1/√2)² = 1/2` exactly, the values +`cos(1/√2)` and `√2 sin(1/√2)` are alternating series with *rational* terms, so bounding them needs +no estimate at an irrational argument: five terms bound the first from above, four bound the second +from below, and the gap to `1.3275` is about `7 · 10⁻⁷`. +-/ + +@[expose] public section + +namespace ZetaZeros + +open Filter Finset + +-- The factorial notation `n !` is scoped to `Nat`. +open scoped Nat + +/-- The Montgomery–Taylor constant `1/2 + (1/√2) cot(1/√2) = 1.3274992963…`. -/ +@[zz_tag "def_C_MT"] +noncomputable def montgomeryTaylorConst : ℝ := + 1 / 2 + (1 / Real.sqrt 2) * Real.cot (1 / Real.sqrt 2) + +/-- The proportion of zeros shown simple and on the critical line, +`3/2 - (1/√2) cot(1/√2) = 0.6725007037…`. -/ +@[zz_tag "def_C0"] +noncomputable def simpleProportion : ℝ := + 3 / 2 - (1 / Real.sqrt 2) * Real.cot (1 / Real.sqrt 2) + +/-- The proportion of zeros shown distinct, `5/4 - (1/(2√2)) cot(1/√2) = 0.8362503518…`. -/ +@[zz_tag "def_C1"] +noncomputable def distinctProportion : ℝ := + 5 / 4 - (1 / (2 * Real.sqrt 2)) * Real.cot (1 / Real.sqrt 2) + +section Elementary + +private lemma sqrt_two_pos : 0 < Real.sqrt 2 := Real.sqrt_pos.mpr (by norm_num) + +/-- The whole point: `(1/√2)² = 1/2` on the nose, which is what makes the two series rational. -/ +private lemma inv_sqrt_two_sq : (1 / Real.sqrt 2) ^ 2 = 1 / 2 := by + rw [div_pow, one_pow, Real.sq_sqrt (by norm_num : (0:ℝ) ≤ 2)] + +private lemma inv_sqrt_two_lt_one : 1 / Real.sqrt 2 < 1 := by + rw [div_lt_one sqrt_two_pos, ← Real.sqrt_one] + exact Real.sqrt_lt_sqrt (by norm_num) (by norm_num) + +end Elementary + +/-- `sin(1/√2) > 0`. -/ +@[zz_tag "lem_sin_pos"] +theorem sin_inv_sqrt_two_pos : 0 < Real.sin (1 / Real.sqrt 2) := + Real.sin_pos_of_pos_of_le_one (by positivity) (le_of_lt inv_sqrt_two_lt_one) + +/-- The `k`-th term of the rational series for `cos(1/√2)`. -/ +noncomputable def cosTerm (k : ℕ) : ℝ := 1 / (2 ^ k * ((2 * k)! : ℝ)) + +/-- The `k`-th term of the rational series for `√2 sin(1/√2)`. -/ +noncomputable def sinTerm (k : ℕ) : ℝ := 1 / (2 ^ k * ((2 * k + 1)! : ℝ)) + +/-- `cos(1/√2)` is the alternating sum of `1 / (2ᵏ (2k)!)`. -/ +@[zz_tag "lem_cos_series"] +theorem hasSum_cosTerm : + HasSum (fun k : ℕ => (-1 : ℝ) ^ k * cosTerm k) (Real.cos (1 / Real.sqrt 2)) := by + have h := Real.hasSum_cos (1 / Real.sqrt 2) + have hfun : (fun n : ℕ => (-1 : ℝ) ^ n * (1 / Real.sqrt 2) ^ (2 * n) / ((2 * n)! : ℝ)) + = fun k : ℕ => (-1 : ℝ) ^ k * cosTerm k := by + funext k + rw [cosTerm, pow_mul, inv_sqrt_two_sq, div_pow, one_pow] + have : ((2 * k)! : ℝ) ≠ 0 := Nat.cast_ne_zero.mpr (Nat.factorial_ne_zero _) + field_simp + rwa [hfun] at h + +/-- `√2 sin(1/√2)` is the alternating sum of `1 / (2ᵏ (2k+1)!)`. -/ +@[zz_tag "lem_sin_series"] +theorem hasSum_sinTerm : + HasSum (fun k : ℕ => (-1 : ℝ) ^ k * sinTerm k) + (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2)) := by + have h := (Real.hasSum_sin (1 / Real.sqrt 2)).mul_left (Real.sqrt 2) + have hfun : (fun n : ℕ => Real.sqrt 2 * + ((-1 : ℝ) ^ n * (1 / Real.sqrt 2) ^ (2 * n + 1) / ((2 * n + 1)! : ℝ))) + = fun k : ℕ => (-1 : ℝ) ^ k * sinTerm k := by + funext k + rw [sinTerm, pow_succ, pow_mul, inv_sqrt_two_sq, div_pow, one_pow] + have hf : ((2 * k + 1)! : ℝ) ≠ 0 := Nat.cast_ne_zero.mpr (Nat.factorial_ne_zero _) + have hs : Real.sqrt 2 ≠ 0 := ne_of_gt sqrt_two_pos + field_simp + rwa [hfun] at h + +private lemma cosTerm_antitone : Antitone cosTerm := by + refine antitone_nat_of_succ_le fun n => ?_ + have hpos : (0:ℝ) < 2 ^ n * ((2 * n)! : ℝ) := by + have : (0:ℝ) < ((2 * n)! : ℝ) := Nat.cast_pos.mpr (Nat.factorial_pos _) + positivity + have hle : (2:ℝ) ^ n * ((2 * n)! : ℝ) ≤ 2 ^ (n + 1) * ((2 * (n + 1))! : ℝ) := by + have hf : ((2 * n)! : ℝ) ≤ ((2 * (n + 1))! : ℝ) := + Nat.cast_le.mpr (Nat.factorial_le (by omega)) + have hp : (2:ℝ) ^ n ≤ 2 ^ (n + 1) := by + apply pow_le_pow_right₀ (by norm_num) (by omega) + have h1 : (0:ℝ) < (2:ℝ) ^ n := by positivity + have h2 : (0:ℝ) < ((2 * n)! : ℝ) := Nat.cast_pos.mpr (Nat.factorial_pos _) + nlinarith + simpa [cosTerm] using one_div_le_one_div_of_le hpos hle + +private lemma sinTerm_antitone : Antitone sinTerm := by + refine antitone_nat_of_succ_le fun n => ?_ + have hpos : (0:ℝ) < 2 ^ n * ((2 * n + 1)! : ℝ) := by + have : (0:ℝ) < ((2 * n + 1)! : ℝ) := Nat.cast_pos.mpr (Nat.factorial_pos _) + positivity + have hle : (2:ℝ) ^ n * ((2 * n + 1)! : ℝ) ≤ 2 ^ (n + 1) * ((2 * (n + 1) + 1)! : ℝ) := by + have hf : ((2 * n + 1)! : ℝ) ≤ ((2 * (n + 1) + 1)! : ℝ) := + Nat.cast_le.mpr (Nat.factorial_le (by omega)) + have hp : (2:ℝ) ^ n ≤ 2 ^ (n + 1) := by + apply pow_le_pow_right₀ (by norm_num) (by omega) + have h1 : (0:ℝ) < (2:ℝ) ^ n := by positivity + have h2 : (0:ℝ) < ((2 * n + 1)! : ℝ) := Nat.cast_pos.mpr (Nat.factorial_pos _) + nlinarith + simpa [sinTerm] using one_div_le_one_div_of_le hpos hle + +/-- Five terms bound `cos(1/√2)` from above. The partial sum is `0.760244605…`. -/ +@[zz_tag "lem_cos_upper"] +theorem cos_inv_sqrt_two_lt : Real.cos (1 / Real.sqrt 2) < 0.7602447 := by + have h := Antitone.tendsto_le_alternating_series + hasSum_cosTerm.tendsto_sum_nat cosTerm_antitone 2 + have hsum : ∑ i ∈ Finset.range (2 * 2 + 1), (-1 : ℝ) ^ i * cosTerm i < 0.7602447 := by + norm_num [Finset.sum_range_succ, cosTerm, Nat.factorial] + linarith + +/-- Four terms bound `√2 sin(1/√2)` from below. The partial sum is `0.918725198…`. -/ +@[zz_tag "lem_sin_lower"] +theorem sqrt_two_mul_sin_inv_sqrt_two_gt : + 0.9187251 < Real.sqrt 2 * Real.sin (1 / Real.sqrt 2) := by + have h := Antitone.alternating_series_le_tendsto + hasSum_sinTerm.tendsto_sum_nat sinTerm_antitone 2 + have hsum : (0.9187251 : ℝ) < ∑ i ∈ Finset.range (2 * 2), (-1 : ℝ) ^ i * sinTerm i := by + norm_num [Finset.sum_range_succ, sinTerm, Nat.factorial] + linarith + +/-- `(1/√2) cot(1/√2) < 0.8275`. -/ +@[zz_tag "lem_cot_upper"] +theorem inv_sqrt_two_mul_cot_lt : (1 / Real.sqrt 2) * Real.cot (1 / Real.sqrt 2) < 0.8275 := by + have hs : 0 < Real.sin (1 / Real.sqrt 2) := sin_inv_sqrt_two_pos + have hden : 0 < Real.sqrt 2 * Real.sin (1 / Real.sqrt 2) := by positivity + have hcot : (1 / Real.sqrt 2) * Real.cot (1 / Real.sqrt 2) + = Real.cos (1 / Real.sqrt 2) / (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2)) := by + rw [Real.cot_eq_cos_div_sin] + field_simp + rw [hcot, div_lt_iff₀ hden] + linarith [cos_inv_sqrt_two_lt, sqrt_two_mul_sin_inv_sqrt_two_gt] + +/-- **The numerical input.** `C_MT < 1.3275`, which is exactly what both headline bounds need: +`2 - 1.3275 = 0.6725` and `3/2 - 1.3275/2 = 0.83625`. -/ +@[zz_tag "lem_cmt_upper"] +theorem montgomeryTaylorConst_lt : montgomeryTaylorConst < 1.3275 := by + have := inv_sqrt_two_mul_cot_lt + rw [montgomeryTaylorConst] + linarith + +/-- `C₀ = 2 - C_MT`. -/ +@[zz_tag "lem_C0_eq"] +theorem simpleProportion_eq : simpleProportion = 2 - montgomeryTaylorConst := by + rw [simpleProportion, montgomeryTaylorConst]; ring + +/-- `C₁ = 3/2 - C_MT/2`. -/ +@[zz_tag "lem_C1_eq"] +theorem distinctProportion_eq : distinctProportion = 3 / 2 - montgomeryTaylorConst / 2 := by + rw [distinctProportion, montgomeryTaylorConst] + have hs : Real.sqrt 2 ≠ 0 := ne_of_gt sqrt_two_pos + field_simp + ring + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Zeta/Asymptotics.lean b/LeanPool/ZetaZeros/Zeta/Asymptotics.lean new file mode 100644 index 0000000000..1e6e024a28 --- /dev/null +++ b/LeanPool/ZetaZeros/Zeta/Asymptotics.lean @@ -0,0 +1,56 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public import LeanPool.ZetaZeros.Defs + +/-! +# Elementary asymptotic consequences of Riemann--von Mangoldt + +This file turns the epsilon-form external input into the filter form used by the final proportion +argument and records the eventual positivity needed to divide by the zero count. +-/ + +@[expose] public section + +namespace ZetaZeros + +open Filter Topology + +/-- The main scale in the Riemann--von Mangoldt and pair-correlation formulae. -/ +noncomputable def zeroScale (T : ℝ) : ℝ := T / (2 * Real.pi) * Real.log T + +/-- The Riemann--von Mangoldt scale is positive at all sufficiently large heights. -/ +theorem zeroScale_pos_eventually : ∀ᶠ T in atTop, 0 < zeroScale T := by + filter_upwards [eventually_ge_atTop (2 : ℝ)] with T hT + have hT0 : 0 < T := by linarith + have hlog : 0 < Real.log T := Real.log_pos (by linarith) + exact mul_pos (div_pos hT0 (mul_pos (by norm_num) Real.pi_pos)) hlog + +/-- Epsilon-form Riemann--von Mangoldt is convergence of the normalized count to one. -/ +theorem RiemannVonMangoldt.tendsto (hRvM : RiemannVonMangoldt) : + Tendsto (fun T : ℝ => (zeroCount T : ℝ) / zeroScale T) atTop (nhds 1) := by + rw [Metric.tendsto_nhds] + intro ε hε + obtain ⟨T₀, hT₀⟩ := hRvM ε hε + filter_upwards [eventually_ge_atTop T₀] with T hT + simpa only [zeroScale, Real.dist_eq] using hT₀ T hT + +/-- Riemann--von Mangoldt in particular makes the zero count positive eventually. -/ +theorem RiemannVonMangoldt.zeroCount_pos_eventually (hRvM : RiemannVonMangoldt) : + ∀ᶠ T in atTop, 0 < (zeroCount T : ℝ) := by + obtain ⟨T₀, hT₀⟩ := hRvM (1 / 2) (by norm_num) + filter_upwards [eventually_ge_atTop T₀, zeroScale_pos_eventually] with T hT hscale + have hratio : 0 < (zeroCount T : ℝ) / zeroScale T := by + have habs := hT₀ T hT + rw [abs_lt] at habs + dsimp [zeroScale] at habs ⊢ + linarith + rcases (div_pos_iff.mp hratio) with h | h + · exact h.1 + · exact (not_lt_of_ge (le_of_lt hscale) h.2).elim + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Zeta/Basic.lean b/LeanPool/ZetaZeros/Zeta/Basic.lean new file mode 100644 index 0000000000..d7ec5c541e --- /dev/null +++ b/LeanPool/ZetaZeros/Zeta/Basic.lean @@ -0,0 +1,302 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public import Mathlib.Analysis.Calculus.ContDiff.Convolution +public import Mathlib.Analysis.Real.Pi.Bounds +public import LeanPool.ZetaZeros.Numeric.MontgomeryTaylor +public import LeanPool.ZetaZeros.Zeta.Defs + +/-! +# First properties of the extremal test function and the rescaling + +The extremal test function is strictly positive on its support — which is what makes its square +root smooth, and hence the whole kernel construction possible — and the rescaling sends a zero to +a real point exactly when that zero lies on the critical line. + +From those, the normalised cutoff test function `η_ψ` and its square `f_ψ` are shown to be smooth, +compactly supported in `(-1/2, 1/2)` and even — and then so is the self-convolution `Q_ψ`, on +`(-1, 1)`, together with its second derivative. +-/ + +@[expose] public section + +namespace ZetaZeros + +open MeasureTheory + +/-- `√2 < 3/2`, which places `√2 x` inside `(-π/2, π/2)` for `|x| ≤ 1/2`. -/ +private lemma sqrt_two_lt_three_halves : Real.sqrt 2 < 3 / 2 := by + have h : Real.sqrt 2 < Real.sqrt (9 / 4) := Real.sqrt_lt_sqrt (by norm_num) (by norm_num) + rwa [show (9 / 4 : ℝ) = (3 / 2) ^ 2 by norm_num, Real.sqrt_sq (by norm_num)] at h + +/-- The extremal test function is strictly positive on `[-1/2, 1/2]`. -/ +@[zz_tag "lem_f0_pos"] +theorem extremalTest_pos {x : ℝ} (hx : |x| ≤ 1 / 2) : 0 < extremalTest x := by + rw [extremalTest, ite_eq_left hx] + have hs2 : 0 < Real.sqrt 2 := Real.sqrt_pos.mpr (by norm_num) + have hden : 0 < Real.sqrt 2 * Real.sin (1 / Real.sqrt 2) := + mul_pos hs2 sin_inv_sqrt_two_pos + have hb : |Real.sqrt 2 * x| < Real.pi / 2 := by + rw [abs_mul, abs_of_pos hs2] + nlinarith [sqrt_two_lt_three_halves, abs_nonneg x, hx, Real.pi_gt_three] + obtain ⟨h1, h2⟩ := abs_lt.mp hb + exact div_pos (Real.cos_pos_of_mem_Ioo ⟨h1, h2⟩) hden + +/-- The rescaling sends a zero to a real point exactly when the zero is on the critical line. -/ +@[zz_tag "lem_Z_T_real"] +theorem rescale_im_eq_zero_iff {T : ℝ} (hT : 1 < T) (ρ : ℂ) : + (rescale T ρ).im = 0 ↔ ρ.re = 1 / 2 := by + have hlog : 0 < Real.log T := Real.log_pos hT + have hc : Real.log T / (2 * Real.pi) ≠ 0 := by positivity + have h12 : (1 : ℂ) / 2 = ((1 / 2 : ℝ) : ℂ) := by norm_num + have him : (rescale T ρ).im = (ρ.re - 1 / 2) * (Real.log T / (2 * Real.pi)) := by + simp only [rescale, h12, Complex.mul_im, Complex.mul_re, Complex.I_re, Complex.I_im, + Complex.ofReal_re, Complex.ofReal_im, Complex.sub_re, Complex.sub_im] + ring + rw [him, mul_eq_zero] + simp [hc, sub_eq_zero] + +/-! ### The normalised cutoff test function is smooth, compactly supported and even + +The delicate point is `±1/2`: there `√f₀` is not smooth, because `f₀` jumps +from a positive value down to `0`. What saves it is that a `δ`-cutoff VANISHES on a neighbourhood of +every such point, so the product is locally constant there. + +Stated without `0 < δ < 1/4` and without positivity of `A_ψ`: neither is needed. When `A_ψ = 0` the +division gives `η_ψ = 0`, which is smooth, supported anywhere and even. -/ + +/-- Positivity of the normalising denominator of `f₀`, read off from `extremalTest_pos` at `0` +rather than re-proved, so the library holds only one proof of it. -/ +theorem sqrt_two_mul_sin_pos : 0 < Real.sqrt 2 * Real.sin (1 / Real.sqrt 2) := by + have h := extremalTest_pos (x := 0) (by norm_num) + rw [extremalTest, ite_eq_left (by norm_num : |(0 : ℝ)| ≤ 1 / 2), mul_zero, Real.cos_zero] at h + exact one_div_pos.mp h + +/-- Off `[-1/2, 1/2]` the cutoff kills the product, so on ALL of `ℝ` the test function agrees with +the expression that has `f₀`'s `if` removed. That representative is smooth wherever +`cos (√2 x) > 0`, and the cutoff handles everywhere else. -/ +theorem cutoffTest_eq_unfolded {delta : ℝ} {psi : ℝ → ℝ} (h : IsCutoff delta psi) : + cutoffTest psi = fun x => psi x * Real.sqrt (Real.cos (Real.sqrt 2 * x) / + (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2))) / Real.sqrt (cutoffNormaliser psi) := by + funext x + by_cases hx : |x| ≤ 1 / 2 + · simp only [cutoffTest, extremalTest, ite_eq_left hx] + · have hz : psi x = 0 := h.support x (not_le.mp hx).le + simp only [cutoffTest, extremalTest, ite_eq_right hx, hz, zero_mul, zero_div] + +/-- **`η_ψ` is smooth.** -/ +@[zz_tag "lem_eta_psi_smooth"] +theorem cutoffTest_contDiff {delta : ℝ} {psi : ℝ → ℝ} (h : IsCutoff delta psi) : + ContDiff ℝ (⊤ : ℕ∞) (cutoffTest psi) := by + have hk := sqrt_two_mul_sin_pos + have hs2pos : 0 < Real.sqrt 2 := Real.sqrt_pos.mpr (by norm_num) + rw [cutoffTest_eq_unfolded h, contDiff_iff_contDiffAt] + intro x + by_cases hx : |x| ≤ 1 / 2 + · have hcos : 0 < Real.cos (Real.sqrt 2 * x) := by + apply Real.cos_pos_of_mem_Ioo + have habs : |Real.sqrt 2 * x| < Real.pi / 2 := by + have hrw : |Real.sqrt 2 * x| = Real.sqrt 2 * |x| := by + rw [abs_mul, abs_of_pos hs2pos] + rw [hrw] + nlinarith [sqrt_two_lt_three_halves, abs_nonneg x, Real.pi_gt_three] + rw [abs_lt] at habs + exact ⟨habs.1, habs.2⟩ + have hne : Real.cos (Real.sqrt 2 * x) / (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2)) ≠ 0 := + ne_of_gt (div_pos hcos hk) + have hinner : ContDiffAt ℝ (⊤ : ℕ∞) (fun y : ℝ => Real.cos (Real.sqrt 2 * y) / + (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2))) x := + ((Real.contDiff_cos.comp (contDiff_const.mul contDiff_id)).contDiffAt).div_const _ + exact ((h.smooth.contDiffAt).mul (hinner.sqrt hne)).div_const _ + · have hopen : IsOpen {y : ℝ | 1 / 2 < |y|} := isOpen_lt continuous_const continuous_abs + have hev : (fun y : ℝ => psi y * Real.sqrt (Real.cos (Real.sqrt 2 * y) / + (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2))) / Real.sqrt (cutoffNormaliser psi)) + =ᶠ[nhds x] fun _ => (0 : ℝ) := by + filter_upwards [hopen.mem_nhds (not_le.mp hx)] with y hy + simp [h.support y (le_of_lt hy)] + exact (contDiffAt_const (c := (0 : ℝ))).congr_of_eventuallyEq hev + +/-- **`η_ψ` vanishes off `(-1/2, 1/2)`.** -/ +@[zz_tag "lem_eta_psi_smooth"] +theorem cutoffTest_eq_zero_of_half_le_abs {delta : ℝ} {psi : ℝ → ℝ} (h : IsCutoff delta psi) + {x : ℝ} (hx : 1 / 2 ≤ |x|) : cutoffTest psi x = 0 := by + simp [cutoffTest, h.support x hx] + +/-- **`η_ψ` has compact support** -- the `C_c` half of `C_c^∞`. -/ +@[zz_tag "lem_eta_psi_smooth"] +theorem cutoffTest_hasCompactSupport {delta : ℝ} {psi : ℝ → ℝ} (h : IsCutoff delta psi) : + HasCompactSupport (cutoffTest psi) := by + refine HasCompactSupport.intro (isCompact_Icc (a := -(1 / 2 : ℝ)) (b := 1 / 2)) ?_ + intro x hx + refine cutoffTest_eq_zero_of_half_le_abs h ?_ + by_contra hlt + exact hx (Set.mem_Icc.mpr (abs_le.mp (le_of_lt (not_le.mp hlt)))) + +/-- **`η_ψ` is even.** -/ +@[zz_tag "lem_eta_psi_smooth"] +theorem cutoffTest_neg {delta : ℝ} {psi : ℝ → ℝ} (h : IsCutoff delta psi) (x : ℝ) : + cutoffTest psi (-x) = cutoffTest psi x := by + have hf : extremalTest (-x) = extremalTest x := by + simp [extremalTest, abs_neg, mul_neg, Real.cos_neg] + simp [cutoffTest, hf, h.even x] + +/-- **`f_ψ` is smooth.** -/ +@[zz_tag "lem_eta_psi_smooth"] +theorem cutoffTestSq_contDiff {delta : ℝ} {psi : ℝ → ℝ} (h : IsCutoff delta psi) : + ContDiff ℝ (⊤ : ℕ∞) (cutoffTestSq psi) := by + have hpow : cutoffTestSq psi = fun x => cutoffTest psi x ^ 2 := rfl + rw [hpow] + exact (cutoffTest_contDiff h).pow 2 + +/-- **`f_ψ` vanishes off `(-1/2, 1/2)`.** -/ +@[zz_tag "lem_eta_psi_smooth"] +theorem cutoffTestSq_eq_zero_of_half_le_abs {delta : ℝ} {psi : ℝ → ℝ} (h : IsCutoff delta psi) + {x : ℝ} (hx : 1 / 2 ≤ |x|) : cutoffTestSq psi x = 0 := by + simp [cutoffTestSq, Pi.pow_apply, cutoffTest_eq_zero_of_half_le_abs h hx] + +/-- **`f_ψ` has compact support.** -/ +@[zz_tag "lem_eta_psi_smooth"] +theorem cutoffTestSq_hasCompactSupport {delta : ℝ} {psi : ℝ → ℝ} (h : IsCutoff delta psi) : + HasCompactSupport (cutoffTestSq psi) := by + refine HasCompactSupport.intro (isCompact_Icc (a := -(1 / 2 : ℝ)) (b := 1 / 2)) ?_ + intro x hx + refine cutoffTestSq_eq_zero_of_half_le_abs h ?_ + by_contra hlt + exact hx (Set.mem_Icc.mpr (abs_le.mp (le_of_lt (not_le.mp hlt)))) + +/-- **`f_ψ` is even.** -/ +@[zz_tag "lem_eta_psi_smooth"] +theorem cutoffTestSq_neg {delta : ℝ} {psi : ℝ → ℝ} (h : IsCutoff delta psi) (x : ℝ) : + cutoffTestSq psi (-x) = cutoffTestSq psi x := by + simp [cutoffTestSq, Pi.pow_apply, cutoffTest_neg h x] + +/-! ### The self-convolution `Q_psi`: smoothness, support, parity + +`Q_psi` is `f_psi` convolved with itself, so Mathlib's convolution theory applies once `f_psi` is +known smooth with compact support, which is `cutoffTestSq_contDiff` and +`cutoffTestSq_hasCompactSupport`. + +The support claim doubles: `f_psi` lives in `(-1/2, 1/2)`, so the convolution lives in `(-1, 1)`. +The elementary reason is that the integrand `f_psi t * f_psi (x - t)` is identically zero when +`1 <= |x|`: either `|t| >= 1/2`, killing the first factor, or `|t| < 1/2` and then +`|x - t| >= |x| - |t| > 1/2`, killing the second. + +For `Q_psi''` the support and parity are inherited rather than recomputed: derivatives cannot +enlarge the support (`tsupport_deriv_subset`), and `iteratedDeriv_comp_neg` turns evenness of +`Q_psi` into evenness of an even-order derivative. -/ + +section QPsi + +variable {delta : ℝ} {psi : ℝ → ℝ} + +/-- Outside `[-c, c]` the absolute value is at least `c`. Used repeatedly to turn a +`HasCompactSupport.intro` obligation into the pointwise vanishing statement. -/ +theorem le_abs_of_notMem_Icc {c x : ℝ} (hx : x ∉ Set.Icc (-c) c) : c ≤ |x| := by + rcases le_or_gt c |x| with hle | hlt + · exact hle + · exact absurd (Set.mem_Icc.mpr (abs_le.mp hlt.le)) hx + +/-- `Q_psi` is Mathlib's convolution of `f_psi` with itself. Definitional, but it is what makes the +convolution smoothness theorem applicable. -/ +theorem cutoffSelfConv_eq_convolution (psi : ℝ → ℝ) : + cutoffSelfConv psi = + convolution (cutoffTestSq psi) (cutoffTestSq psi) (ContinuousLinearMap.mul ℝ ℝ) volume := by + funext x + rfl + +/-- **`Q_psi` is smooth.** -/ +@[zz_tag "lem_Q_psi_support"] +theorem contDiff_cutoffSelfConv (h : IsCutoff delta psi) : + ContDiff ℝ (⊤ : ℕ∞) (cutoffSelfConv psi) := by + rw [cutoffSelfConv_eq_convolution] + exact HasCompactSupport.contDiff_convolution_right + (L := ContinuousLinearMap.mul ℝ ℝ) (μ := volume) + (hcg := cutoffTestSq_hasCompactSupport h) + (hf := (cutoffTestSq_contDiff h).continuous.locallyIntegrable (μ := volume)) + (hg := cutoffTestSq_contDiff h) + +/-- **`Q_psi` vanishes off `(-1, 1)`.** -/ +@[zz_tag "lem_Q_psi_support"] +theorem cutoffSelfConv_eq_zero_of_one_le_abs (h : IsCutoff delta psi) {x : ℝ} (hx : 1 ≤ |x|) : + cutoffSelfConv psi x = 0 := by + have key : ∀ t : ℝ, cutoffTestSq psi t * cutoffTestSq psi (x - t) = 0 := by + intro t + rcases le_or_gt (1 / 2 : ℝ) |t| with ht | ht + · rw [cutoffTestSq_eq_zero_of_half_le_abs h ht, zero_mul] + · have h2 : 1 / 2 ≤ |x - t| := by + have := abs_sub_abs_le_abs_sub x t + linarith + rw [cutoffTestSq_eq_zero_of_half_le_abs h h2, mul_zero] + simp [cutoffSelfConv, key] + +/-- **`Q_psi` has compact support.** -/ +@[zz_tag "lem_Q_psi_support"] +theorem hasCompactSupport_cutoffSelfConv (h : IsCutoff delta psi) : + HasCompactSupport (cutoffSelfConv psi) := + HasCompactSupport.intro (isCompact_Icc (a := (-1 : ℝ)) (b := 1)) fun _ hx => + cutoffSelfConv_eq_zero_of_one_le_abs h (le_abs_of_notMem_Icc (by simpa using hx)) + +/-- **`Q_psi` is even.** -/ +@[zz_tag "lem_Q_psi_support"] +theorem cutoffSelfConv_neg (h : IsCutoff delta psi) (x : ℝ) : + cutoffSelfConv psi (-x) = cutoffSelfConv psi x := by + have hneg := integral_neg_eq_self + (fun t : ℝ => cutoffTestSq psi t * cutoffTestSq psi (x - t)) volume + rw [cutoffSelfConv, cutoffSelfConv, ← hneg] + refine integral_congr_ae (Filter.Eventually.of_forall fun t => ?_) + change cutoffTestSq psi t * cutoffTestSq psi (-x - t) + = cutoffTestSq psi (-t) * cutoffTestSq psi (x - -t) + rw [cutoffTestSq_neg h t, show (-x - t) = -(x - -t) by ring, cutoffTestSq_neg h] + +/-- **`Q_psi` is Lipschitz continuous at `0`** -- Lipschitz with some constant on some +neighbourhood of `0`, which is what "Lipschitz continuous at `0`" means here. -/ +@[zz_tag "lem_Q_psi_support"] +theorem exists_lipschitzOnWith_cutoffSelfConv (h : IsCutoff delta psi) : + ∃ K, ∃ t ∈ nhds (0 : ℝ), LipschitzOnWith K (cutoffSelfConv psi) t := + ((contDiff_cutoffSelfConv h).of_le (by exact_mod_cast le_top)).contDiffAt.exists_lipschitzOnWith + +/-- **`Q_psi''` is smooth.** -/ +@[zz_tag "lem_Q_psi_support"] +theorem contDiff_iteratedDeriv_two_cutoffSelfConv (h : IsCutoff delta psi) : + ContDiff ℝ (⊤ : ℕ∞) (iteratedDeriv 2 (cutoffSelfConv psi)) := by + rw [iteratedDeriv_eq_iterate] + exact ContDiff.iterate_deriv 2 (contDiff_cutoffSelfConv h) + +/-- **`Q_psi''` vanishes off `[-1, 1]`.** A derivative cannot enlarge the support. -/ +@[zz_tag "lem_Q_psi_support"] +theorem iteratedDeriv_two_cutoffSelfConv_eq_zero (h : IsCutoff delta psi) {x : ℝ} (hx : 1 < |x|) : + iteratedDeriv 2 (cutoffSelfConv psi) x = 0 := by + have hsupp : Function.support (cutoffSelfConv psi) ⊆ Set.Icc (-1 : ℝ) 1 := fun y hy => by + by_contra hmem + exact hy (cutoffSelfConv_eq_zero_of_one_le_abs h (le_abs_of_notMem_Icc (by simpa using hmem))) + have hout : x ∉ tsupport (cutoffSelfConv psi) := fun hmem => by + have hle : |x| ≤ 1 := abs_le.mpr (Set.mem_Icc.mp (closure_minimal hsupp isClosed_Icc hmem)) + linarith + have hchain : x ∉ Function.support (deriv (deriv (cutoffSelfConv psi))) := fun hmem => + hout (tsupport_deriv_subset (support_deriv_subset hmem)) + simpa [iteratedDeriv_succ, iteratedDeriv_one, Function.notMem_support] using hchain + +/-- **`Q_psi''` is even**, being an even-order derivative of an even function. -/ +@[zz_tag "lem_Q_psi_support"] +theorem iteratedDeriv_two_cutoffSelfConv_neg (h : IsCutoff delta psi) (x : ℝ) : + iteratedDeriv 2 (cutoffSelfConv psi) (-x) = iteratedDeriv 2 (cutoffSelfConv psi) x := by + have heven : (fun y : ℝ => cutoffSelfConv psi (-y)) = cutoffSelfConv psi := + funext fun y => cutoffSelfConv_neg h y + have hcn := iteratedDeriv_comp_neg 2 (cutoffSelfConv psi) x + rw [heven] at hcn + simpa using hcn.symm + +/-- **`Q_psi''` is Lipschitz continuous at `0`.** -/ +@[zz_tag "lem_Q_psi_support"] +theorem exists_lipschitzOnWith_iteratedDeriv_two_cutoffSelfConv (h : IsCutoff delta psi) : + ∃ K, ∃ t ∈ nhds (0 : ℝ), LipschitzOnWith K (iteratedDeriv 2 (cutoffSelfConv psi)) t := + ((contDiff_iteratedDeriv_two_cutoffSelfConv h).of_le + (by exact_mod_cast le_top)).contDiffAt.exists_lipschitzOnWith + +end QPsi + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Zeta/Cutoff.lean b/LeanPool/ZetaZeros/Zeta/Cutoff.lean new file mode 100644 index 0000000000..771a151926 --- /dev/null +++ b/LeanPool/ZetaZeros/Zeta/Cutoff.lean @@ -0,0 +1,49 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public import Mathlib.Analysis.Calculus.BumpFunction.FiniteDimension +public import LeanPool.ZetaZeros.Zeta.Defs + +/-! +# Cutoffs exist + +The source asks for a smooth even cutoff, valued in `[0,1]`, supported in `(-1/2, 1/2)` and +identically one on `|x| ≤ 1/2 - delta`. That is exactly Mathlib's `ContDiffBump` centred at the +origin with inner radius `1/2 - delta` and outer radius `1/2`: the bump is radial, so evenness +comes free from `ContDiffBump.neg`, and the two radius conditions are what `0 < delta < 1/4` +supplies. +-/ + +@[expose] public section + +namespace ZetaZeros + +open Metric + +/-- **Cutoffs exist.** -/ +@[zz_tag "lem_cutoff_exists"] +theorem exists_isCutoff {delta : ℝ} (h0 : 0 < delta) (h4 : delta < 1 / 4) : + ∃ psi : ℝ → ℝ, IsCutoff delta psi := by + have hrIn : (0 : ℝ) < 1 / 2 - delta := by linarith + have hlt : (1 : ℝ) / 2 - delta < 1 / 2 := by linarith + let f : ContDiffBump (0 : ℝ) := ⟨1 / 2 - delta, 1 / 2, hrIn, hlt⟩ + refine ⟨fun x => f x, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · exact f.contDiff + · intro x hx + refine f.zero_of_le_dist ?_ + simpa [f, Real.dist_eq] using hx + · intro x + exact f.neg x + · intro x + exact f.nonneg + · intro x + exact f.le_one + · intro x hx + refine f.one_of_mem_closedBall ?_ + simpa [f, mem_closedBall, Real.dist_eq] using hx + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Zeta/Defs.lean b/LeanPool/ZetaZeros/Zeta/Defs.lean new file mode 100644 index 0000000000..6d31a1c0b8 --- /dev/null +++ b/LeanPool/ZetaZeros/Zeta/Defs.lean @@ -0,0 +1,95 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public import LeanPool.ZetaZeros.Hilbert.Defs + +/-! +# The zeta-side vocabulary + +The zero counts, the pair-correlation apparatus, and the kernel construction of the source's +Section 3. + +Every definition that also appears in `Challenge/Basic.lean` is spelled **identically** here. That +is a requirement rather than a courtesy: the comparator compares elaborated types, so a development +and a challenge that spell one notion two ways name two constants, and the pair then verifies +nothing while every other gate stays green. +-/ + +@[expose] public section + +namespace ZetaZeros + +open MeasureTheory + +/-- The non-trivial zeros of the Riemann zeta function with imaginary part in `(0, T]`: the +zeros lying in the critical strip `0 < re s < 1`, as a set, so without multiplicity. -/ +@[zz_tag "def_Z_T"] +noncomputable def rescale (T : ℝ) (ρ : ℂ) : ℂ := + Complex.I * (ρ - 1 / 2) * ((Real.log T / (2 * Real.pi) : ℝ) : ℂ) + +/-- The rescaled zeros: the image of the non-trivial zeros up to height `T`. -/ +@[zz_tag "def_Z_T"] +noncomputable def rescaledZeros (T : ℝ) : Set ℂ := rescale T '' nontrivialZeros T + +/-- The multiplicity transported along the rescaling: the rescaling is injective, so a rescaled +point inherits the multiplicity of the zero it came from. -/ +@[zz_tag "def_Z_T"] +noncomputable def rescaledMult (T : ℝ) (w : ℂ) : ℕ := + zeroMultiplicity (w / (Complex.I * ((Real.log T / (2 * Real.pi) : ℝ) : ℂ)) + 1 / 2) + +/-- The extremal test function `cos(√2 x) / (√2 sin(1/√2))` on `[-1/2, 1/2]`, zero elsewhere. -/ +@[zz_tag "def_f0"] +noncomputable def extremalTest (x : ℝ) : ℝ := + if |x| ≤ 1 / 2 then Real.cos (Real.sqrt 2 * x) / (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2)) + else 0 + +/-- The self-convolution of the extremal test function. -/ +@[zz_tag "def_Q0"] +noncomputable def extremalSelfConv (x : ℝ) : ℝ := ∫ t : ℝ, extremalTest t * extremalTest (x - t) + +/-- `psi` is a `delta`-cutoff: smooth, even, supported in `(-1/2, 1/2)`, valued in `[0, 1]`, and +identically `1` on `|x| ≤ 1/2 - delta`. -/ +@[zz_tag "def_cutoff"] +structure IsCutoff (delta : ℝ) (psi : ℝ → ℝ) : Prop where + /-- `psi` is smooth. -/ + smooth : ContDiff ℝ (⊤ : ℕ∞) psi + /-- `psi` vanishes off `(-1/2, 1/2)`. -/ + support : ∀ x, 1 / 2 ≤ |x| → psi x = 0 + /-- `psi` is even. -/ + even : ∀ x, psi (-x) = psi x + /-- `psi` is non-negative. -/ + nonneg : ∀ x, 0 ≤ psi x + /-- `psi` is at most one. -/ + le_one : ∀ x, psi x ≤ 1 + /-- `psi` is identically one on the shrunken interval. -/ + eq_one : ∀ x, |x| ≤ 1 / 2 - delta → psi x = 1 + +/-- The normalising constant `A_psi = ∫ psi² f₀`. -/ +@[zz_tag "def_normaliser"] +noncomputable def cutoffNormaliser (psi : ℝ → ℝ) : ℝ := ∫ x : ℝ, psi x ^ 2 * extremalTest x + +/-- The normalised test function `eta_psi = psi √f₀ / √A_psi`. -/ +@[zz_tag "def_eta_psi"] +noncomputable def cutoffTest (psi : ℝ → ℝ) (x : ℝ) : ℝ := + psi x * Real.sqrt (extremalTest x) / Real.sqrt (cutoffNormaliser psi) + +/-- Its square, `f_psi = eta_psi²`. -/ +@[zz_tag "def_f_psi"] +noncomputable def cutoffTestSq (psi : ℝ → ℝ) : ℝ → ℝ := cutoffTest psi ^ 2 + +/-- Its self-convolution, `Q_psi = f_psi ⋆ f_psi`. -/ +@[zz_tag "def_Q_psi"] +noncomputable def cutoffSelfConv (psi : ℝ → ℝ) (x : ℝ) : ℝ := + ∫ t : ℝ, cutoffTestSq psi t * cutoffTestSq psi (x - t) + +/-- The corrected test function `r_{psi,T} = Q_psi - Q_psi'' / (4 (log T)²)`, whose Fourier +transform carries the factor that cancels the pair-correlation weight. -/ +@[zz_tag "def_r_psi_T"] +noncomputable def correctedTest (psi : ℝ → ℝ) (T : ℝ) (x : ℝ) : ℝ := + cutoffSelfConv psi x - iteratedDeriv 2 (cutoffSelfConv psi) x / (4 * Real.log T ^ 2) + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Zeta/Finite.lean b/LeanPool/ZetaZeros/Zeta/Finite.lean new file mode 100644 index 0000000000..3287734b95 --- /dev/null +++ b/LeanPool/ZetaZeros/Zeta/Finite.lean @@ -0,0 +1,45 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public import Mathlib.NumberTheory.LSeries.ZetaZeros +public import LeanPool.ZetaZeros.Zeta.Defs + +/-! +# Finiteness of the zero set up to a height + +The non-trivial zeros with imaginary part in `(0, T]` form a finite set, and each has positive +multiplicity. This is what lets the rescaled zeros be a finite multiset, which every statement of +the key proposition requires. + +It is discharged from Mathlib's `IsCompact.inter_riemannZetaZeros_finite`, since the region is +bounded. +-/ + +@[expose] public section + +namespace ZetaZeros + +/-- The non-trivial zeros up to height `T` lie in a ball of radius `1 + |T|`. -/ +private lemma nontrivialZeros_subset_closedBall (T : ℝ) : + nontrivialZeros T ⊆ Metric.closedBall (0 : ℂ) (1 + |T|) := by + intro ρ hρ + obtain ⟨-, h1, h2, h3, h4⟩ := hρ + have hre : |ρ.re| ≤ 1 := by rw [abs_of_pos h1]; linarith + have him : |ρ.im| ≤ |T| := by + rw [abs_of_pos h3]; exact le_trans h4 (le_abs_self T) + simp only [Metric.mem_closedBall, dist_zero_right] + calc ‖ρ‖ ≤ |ρ.re| + |ρ.im| := Complex.norm_le_abs_re_add_abs_im ρ + _ ≤ 1 + |T| := by linarith + +/-- **The zero set up to a height is finite.** -/ +@[zz_tag "lem_zeros_finite"] +theorem nontrivialZeros_finite (T : ℝ) : (nontrivialZeros T).Finite := by + have hfin := (isCompact_closedBall (0 : ℂ) (1 + |T|)).inter_riemannZetaZeros_finite + refine hfin.subset fun ρ hρ => ⟨nontrivialZeros_subset_closedBall T hρ, ?_⟩ + exact mem_riemannZetaZeros.mpr hρ.1 + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Zeta/Kernel.lean b/LeanPool/ZetaZeros/Zeta/Kernel.lean new file mode 100644 index 0000000000..38f65cd632 --- /dev/null +++ b/LeanPool/ZetaZeros/Zeta/Kernel.lean @@ -0,0 +1,806 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Axiom Math +-/ +module + +public import LeanPool.ZetaZeros.Hilbert.AlphaExpansion +public import LeanPool.ZetaZeros.MontgomeryTaylor.Basic +public import LeanPool.ZetaZeros.Zeta.Asymptotics +public import LeanPool.ZetaZeros.Defs +public import LeanPool.ZetaZeros.Zeta.Cutoff +public import LeanPool.ZetaZeros.Zeta.Finite +public import LeanPool.ZetaZeros.Zeta.Mass + +/-! +# Construction of the pair-correlation kernel + +The cutoff construction packages the unweighted pair sum, proves that the normalized cutoff is +admissible, and develops the analytic identities needed to apply pair correlation. +-/ + +@[expose] public section + +namespace ZetaZeros + +open MeasureTheory + +/-- Compact support makes the defining complex-frequency Fourier integrand integrable. -/ +private lemma integrable_fourierC_integrand {f : ℝ → ℝ} (hf : Continuous f) + (hfc : HasCompactSupport f) (z : ℂ) : + Integrable (fun x : ℝ => (f x : ℂ) * + Complex.exp (-(2 * (Real.pi : ℂ)) * Complex.I * z * (x : ℂ))) := by + have hc : HasCompactSupport (fun x : ℝ => (f x : ℂ)) := by + change HasCompactSupport ((fun x : ℝ => (x : ℂ)) ∘ f) + exact hfc.comp_left (g := fun x : ℝ => (x : ℂ)) (by norm_num) + apply Continuous.integrable_of_hasCompactSupport + · fun_prop + · exact hc.mul_right + + +/-- Integration by parts for the complex-frequency Fourier transform. -/ +private lemma fourierC_deriv {f : ℝ → ℝ} (hf : ContDiff ℝ (⊤ : ℕ∞) f) + (hfc : HasCompactSupport f) (z : ℂ) : + fourierC (deriv f) z = (2 * (Real.pi : ℂ) * Complex.I * z) * fourierC f z := by + let c : ℂ := -(2 * (Real.pi : ℂ)) * Complex.I * z + let e : ℝ → ℂ := fun x => Complex.exp (c * (x : ℂ)) + let e' : ℝ → ℂ := fun x => e x * c + let fc : ℝ → ℂ := fun x => (f x : ℂ) + let dfc : ℝ → ℂ := fun x => ((deriv f x : ℝ) : ℂ) + have he : ∀ x : ℝ, HasDerivAt e (e' x) x := by + intro x + have hlin : HasDerivAt (fun y : ℝ => c * (y : ℂ)) c x := by + simpa only [mul_one] using! + ((hasDerivAt_id (x : ℂ)).const_mul c).comp_ofReal + exact hlin.cexp + have hfc_deriv : ∀ x : ℝ, HasDerivAt fc (dfc x) x := by + intro x + exact ((hf.differentiable (by simp)) x).hasDerivAt.ofReal_comp + have hcont_e : Continuous e := by + unfold e + fun_prop + have hcont_e' : Continuous e' := by + unfold e' + fun_prop + have hcont_fc : Continuous fc := by + unfold fc + fun_prop + have hcont_dfc : Continuous dfc := by + unfold dfc + exact Complex.continuous_ofReal.comp (hf.continuous_deriv (by simp)) + have hcompact_fc : HasCompactSupport fc := by + change HasCompactSupport ((fun x : ℝ => (x : ℂ)) ∘ f) + exact hfc.comp_left (g := fun x : ℝ => (x : ℂ)) (by norm_num) + have hcompact_dfc : HasCompactSupport dfc := by + change HasCompactSupport ((fun x : ℝ => (x : ℂ)) ∘ deriv f) + exact hfc.deriv.comp_left (g := fun x : ℝ => (x : ℂ)) (by norm_num) + have hed : Integrable (e * dfc) := + (hcont_e.mul hcont_dfc).integrable_of_hasCompactSupport hcompact_dfc.mul_left + have he'f : Integrable (e' * fc) := + (hcont_e'.mul hcont_fc).integrable_of_hasCompactSupport hcompact_fc.mul_left + have hef : Integrable (e * fc) := + (hcont_e.mul hcont_fc).integrable_of_hasCompactSupport hcompact_fc.mul_left + have hibp : ∫ x : ℝ, e x * dfc x = -∫ x : ℝ, e' x * fc x := + integral_mul_deriv_eq_deriv_mul_of_integrable (fun x _ => he x) + (fun x _ => hfc_deriv x) hed he'f hef + unfold fourierC + change (∫ x : ℝ, dfc x * e x) = + (2 * (Real.pi : ℂ) * Complex.I * z) * ∫ x : ℝ, fc x * e x + calc + ∫ x : ℝ, dfc x * e x = ∫ x : ℝ, e x * dfc x := by + apply integral_congr_ae + filter_upwards with x + ring + _ = -∫ x : ℝ, e' x * fc x := hibp + _ = -c * ∫ x : ℝ, fc x * e x := by + rw [← integral_const_mul, ← integral_neg] + apply integral_congr_ae + filter_upwards with x + unfold e' + ring + _ = (2 * (Real.pi : ℂ) * Complex.I * z) * ∫ x : ℝ, fc x * e x := by + unfold c + ring + +/-- The unweighted second moment of the test kernel over ordered pairs of zeta zeros. -/ +noncomputable def unweightedKernelSum (eta : ℝ → ℝ) (T : ℝ) : ℂ := + ∑ᶠ ρ ∈ nontrivialZeros T, ∑ᶠ ρ' ∈ nontrivialZeros T, + ((zeroMultiplicity ρ * zeroMultiplicity ρ' : ℕ) : ℂ) * + testKernel eta (rescaledDiff T ρ ρ') ^ 2 + + +/-- The cutoff-weighted extremal density is integrable. -/ +private lemma integrable_cutoffWeight {delta : ℝ} {psi : ℝ → ℝ} + (hpsi : IsCutoff delta psi) : Integrable (fun x => psi x ^ 2 * extremalTest x) := by + have hzero : ∀ x ∉ Set.Icc (-(1 / 2) : ℝ) (1 / 2), + psi x ^ 2 * extremalTest x = 0 := by + intro x hx + have h : 1 / 2 ≤ |x| := by + by_contra hlt + exact hx (Set.mem_Icc.mpr (abs_le.mp (le_of_lt (not_le.mp hlt)))) + rw [hpsi.support x h] + ring + have hcont : ContinuousOn (fun x => psi x ^ 2 * extremalTest x) + (Set.Icc (-(1 / 2) : ℝ) (1 / 2)) := by + have hG : Continuous fun x : ℝ => psi x ^ 2 * + (Real.cos (Real.sqrt 2 * x) / (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2))) := + (hpsi.smooth.continuous.pow 2).mul + ((Real.continuous_cos.comp (continuous_const.mul continuous_id)).div_const _) + refine hG.continuousOn.congr ?_ + intro x hx + have hx' : |x| ≤ 1 / 2 := abs_le.mpr (Set.mem_Icc.mp hx) + change psi x ^ 2 * extremalTest x = psi x ^ 2 * + (Real.cos (Real.sqrt 2 * x) / (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2))) + rw [extremalTest, ite_eq_left hx'] + exact hcont.integrableOn_Icc.integrable_of_forall_notMem_eq_zero hzero + +/-- The square of the normalized cutoff is integrable. -/ +lemma integrable_cutoffTestSq {delta : ℝ} (hd : 0 < delta) (hd4 : delta < 1 / 4) + {psi : ℝ → ℝ} (hpsi : IsCutoff delta psi) : Integrable (cutoffTestSq psi) := by + have hdiv : Integrable (fun x => psi x ^ 2 * extremalTest x / cutoffNormaliser psi) := + (integrable_cutoffWeight hpsi).div_const _ + exact hdiv.congr (Filter.Eventually.of_forall fun x => (cutoffTestSq_eq hd hd4 hpsi x).symm) + + +/-- The self-convolution is integrable. -/ +lemma integrable_cutoffSelfConv {delta : ℝ} (hd : 0 < delta) (hd4 : delta < 1 / 4) + {psi : ℝ → ℝ} (hpsi : IsCutoff delta psi) : Integrable (cutoffSelfConv psi) := by + change Integrable + (convolution (cutoffTestSq psi) (cutoffTestSq psi) + (ContinuousLinearMap.mul ℝ ℝ) volume) + exact (integrable_cutoffTestSq hd hd4 hpsi).integrable_convolution + (ContinuousLinearMap.mul ℝ ℝ) (integrable_cutoffTestSq hd hd4 hpsi) + + +/-- The second derivative of the self-convolution has compact support. -/ +lemma hasCompactSupport_iteratedDeriv_two_cutoffSelfConv {delta : ℝ} {psi : ℝ → ℝ} + (hpsi : IsCutoff delta psi) : + HasCompactSupport (iteratedDeriv 2 (cutoffSelfConv psi)) := by + rw [iteratedDeriv_eq_iterate] + simpa only [Function.iterate_succ_apply, Function.iterate_zero_apply] using + (hasCompactSupport_cutoffSelfConv hpsi).deriv.deriv + +/-- The second derivative of the self-convolution is integrable. -/ +lemma integrable_iteratedDeriv_two_cutoffSelfConv {delta : ℝ} + {psi : ℝ → ℝ} (hpsi : IsCutoff delta psi) : + Integrable (iteratedDeriv 2 (cutoffSelfConv psi)) := + ((contDiff_iteratedDeriv_two_cutoffSelfConv hpsi).continuous) + |>.integrable_of_hasCompactSupport + (hasCompactSupport_iteratedDeriv_two_cutoffSelfConv hpsi) + +/-- The complex-frequency Fourier transform of the second derivative has the expected +quadratic multiplier. -/ +@[zz_tag "lem_Q_psi_second_hat"] +lemma fourierC_iteratedDeriv_two_cutoffSelfConv {delta : ℝ} + {psi : ℝ → ℝ} (hpsi : IsCutoff delta psi) (z : ℂ) : + fourierC (iteratedDeriv 2 (cutoffSelfConv psi)) z = + -4 * (Real.pi : ℂ) ^ 2 * z ^ 2 * testKernel (cutoffTest psi) z ^ 2 := by + have hQ := contDiff_cutoffSelfConv hpsi + have hQ' : ContDiff ℝ (⊤ : ℕ∞) (deriv (cutoffSelfConv psi)) := by + simpa only [Function.iterate_one] using hQ.iterate_deriv 1 + rw [iteratedDeriv_eq_iterate] + simp only [Function.iterate_succ_apply, Function.iterate_zero_apply] + rw [fourierC_deriv hQ' (hasCompactSupport_cutoffSelfConv hpsi).deriv, + fourierC_deriv hQ (hasCompactSupport_cutoffSelfConv hpsi), + fourierC_cutoffSelfConv hpsi] + ring_nf + rw [show Complex.I ^ 2 = -(1 : ℂ) by norm_num] + ring + +/-- Fourier transform of the corrected pair-correlation test function. -/ +@[zz_tag "lem_r_psi_hat"] +lemma fourierC_correctedTest {delta : ℝ} + {psi : ℝ → ℝ} (hpsi : IsCutoff delta psi) {T : ℝ} (hT : 1 < T) (z : ℂ) : + fourierC (correctedTest psi T) z = + (1 + (Real.pi : ℂ) ^ 2 * z ^ 2 / (Real.log T : ℂ) ^ 2) * + testKernel (cutoffTest psi) z ^ 2 := by + have hlog : Real.log T ≠ 0 := ne_of_gt (Real.log_pos hT) + have hden : (4 * Real.log T ^ 2 : ℝ) ≠ 0 := mul_ne_zero (by norm_num) (pow_ne_zero 2 hlog) + have hQint := integrable_fourierC_integrand + (contDiff_cutoffSelfConv hpsi).continuous + (hasCompactSupport_cutoffSelfConv hpsi) z + have hQ2int := integrable_fourierC_integrand + (contDiff_iteratedDeriv_two_cutoffSelfConv hpsi).continuous + (hasCompactSupport_iteratedDeriv_two_cutoffSelfConv hpsi) z + unfold correctedTest fourierC + rw [show (fun x : ℝ => + ((cutoffSelfConv psi x - + iteratedDeriv 2 (cutoffSelfConv psi) x / (4 * Real.log T ^ 2) : ℝ) : ℂ) * + Complex.exp (-(2 * (Real.pi : ℂ)) * Complex.I * z * (x : ℂ))) = + (fun x : ℝ => (cutoffSelfConv psi x : ℂ) * + Complex.exp (-(2 * (Real.pi : ℂ)) * Complex.I * z * (x : ℂ)) - + ((iteratedDeriv 2 (cutoffSelfConv psi) x : ℝ) : ℂ) * + Complex.exp (-(2 * (Real.pi : ℂ)) * Complex.I * z * (x : ℂ)) / + ((4 * Real.log T ^ 2 : ℝ) : ℂ)) by + funext x + push_cast + ring] + rw [integral_sub hQint (hQ2int.div_const _), integral_div] + change fourierC (cutoffSelfConv psi) z - + fourierC (iteratedDeriv 2 (cutoffSelfConv psi)) z / + ((4 * Real.log T ^ 2 : ℝ) : ℂ) = _ + rw [fourierC_cutoffSelfConv hpsi, + fourierC_iteratedDeriv_two_cutoffSelfConv hpsi] + push_cast + field_simp + ring + +/-- At a rescaled pair of zeros, the correction factor cancels the unconditional +pair-correlation weight. -/ +@[zz_tag "lem_r_at_z_rho"] +lemma correctedTest_weight_cancel {delta : ℝ} + {psi : ℝ → ℝ} (hpsi : IsCutoff delta psi) {T : ℝ} (hT : 1 < T) + {rho rho' : ℂ} (hrho0 : 0 < rho.re) (hrho1 : rho.re < 1) + (hrho0' : 0 < rho'.re) (hrho1' : rho'.re < 1) : + fourierC (correctedTest psi T) (rescaledDiff T rho rho') * pairWeight (rho - rho') = + testKernel (cutoffTest psi) (rescaledDiff T rho rho') ^ 2 := by + let d : ℂ := rho - rho' + have hdre0 : -1 < d.re := by + dsimp [d] + linarith + have hdre1 : d.re < 1 := by + dsimp [d] + linarith + have hdne_two : d ≠ (2 : ℂ) := by + intro h + have hre := congrArg Complex.re h + norm_num at hre + linarith + have hdne_neg_two : d ≠ -(2 : ℂ) := by + intro h + have hre := congrArg Complex.re h + norm_num at hre + linarith + have hden : 4 - d ^ 2 ≠ 0 := by + rw [show 4 - d ^ 2 = -(d - 2) * (d + 2) by ring] + exact mul_ne_zero (neg_ne_zero.mpr (sub_ne_zero.mpr hdne_two)) (by + intro h + apply hdne_neg_two + linear_combination h) + have hlog : Real.log T ≠ 0 := ne_of_gt (Real.log_pos hT) + have hfactor : + 1 + (Real.pi : ℂ) ^ 2 * (rescaledDiff T rho rho') ^ 2 / + (Real.log T : ℂ) ^ 2 = (4 - d ^ 2) / 4 := by + unfold rescaledDiff + dsimp [d] + push_cast + field_simp [hlog, Real.pi_ne_zero] + ring_nf + rw [show Complex.I ^ 2 = -(1 : ℂ) by norm_num] + ring + rw [fourierC_correctedTest hpsi hT, hfactor] + unfold pairWeight + change ((4 - d ^ 2) / 4 * testKernel (cutoffTest psi) (rescaledDiff T rho rho') ^ 2) * + (4 / (4 - d ^ 2)) = _ + field_simp [hden] + + +/-- The self-convolution is an admissible pair-correlation test function. -/ +lemma cutoffSelfConv_isPairTestFunction {delta : ℝ} (hd : 0 < delta) + (hd4 : delta < 1 / 4) {psi : ℝ → ℝ} (hpsi : IsCutoff delta psi) : + IsPairTestFunction (cutoffSelfConv psi) := by + refine ⟨cutoffSelfConv_neg hpsi, integrable_cutoffSelfConv hd hd4 hpsi, + fun x hx => cutoffSelfConv_eq_zero_of_one_le_abs hpsi hx.le, ?_⟩ + obtain ⟨C, hC⟩ := (contDiff_cutoffSelfConv hpsi).lipschitzWith_of_hasCompactSupport + (hasCompactSupport_cutoffSelfConv hpsi) (by simp) + refine ⟨C, fun x => ?_⟩ + simpa only [Real.norm_eq_abs, sub_zero] using hC.norm_sub_le x 0 + +/-- The second derivative of the self-convolution is an admissible pair-correlation test +function. -/ +lemma iteratedDeriv_two_cutoffSelfConv_isPairTestFunction {delta : ℝ} + {psi : ℝ → ℝ} (hpsi : IsCutoff delta psi) : + IsPairTestFunction (iteratedDeriv 2 (cutoffSelfConv psi)) := by + refine ⟨iteratedDeriv_two_cutoffSelfConv_neg hpsi, + integrable_iteratedDeriv_two_cutoffSelfConv hpsi, + fun x hx => iteratedDeriv_two_cutoffSelfConv_eq_zero hpsi hx, ?_⟩ + obtain ⟨C, hC⟩ := + (contDiff_iteratedDeriv_two_cutoffSelfConv hpsi).lipschitzWith_of_hasCompactSupport + (hasCompactSupport_iteratedDeriv_two_cutoffSelfConv hpsi) (by simp) + refine ⟨C, fun x => ?_⟩ + simpa only [Real.norm_eq_abs, sub_zero] using hC.norm_sub_le x 0 + +/-- The unweighted kernel sum is the difference of the two pair-correlation sums supplied by +the corrected test function, expanded linearly. -/ +@[zz_tag "lem_sum_expansion"] +lemma unweightedKernelSum_cutoffTest_eq_pairCorrelationSum {delta : ℝ} + {psi : ℝ → ℝ} (hpsi : IsCutoff delta psi) + {T : ℝ} (hT : 1 < T) : + unweightedKernelSum (cutoffTest psi) T = + pairCorrelationSum (cutoffSelfConv psi) T - + pairCorrelationSum (iteratedDeriv 2 (cutoffSelfConv psi)) T / + (((4 * Real.log T ^ 2 : ℝ) : ℂ)) := by + let s := (nontrivialZeros_finite T).toFinset + have hlog : Real.log T ≠ 0 := ne_of_gt (Real.log_pos hT) + have hlinear (z : ℂ) : + fourierC (correctedTest psi T) z = + fourierC (cutoffSelfConv psi) z - + fourierC (iteratedDeriv 2 (cutoffSelfConv psi)) z / + (((4 * Real.log T ^ 2 : ℝ) : ℂ)) := by + rw [fourierC_correctedTest hpsi hT, + fourierC_cutoffSelfConv hpsi, + fourierC_iteratedDeriv_two_cutoffSelfConv hpsi] + push_cast + field_simp [hlog] + ring + rw [unweightedKernelSum, pairCorrelationSum, pairCorrelationSum] + rw [finsum_mem_eq_finite_toFinset_sum _ (nontrivialZeros_finite T)] + simp_rw [finsum_mem_eq_finite_toFinset_sum _ (nontrivialZeros_finite T)] + change (∑ rho ∈ s, ∑ rho' ∈ s, + ((zeroMultiplicity rho * zeroMultiplicity rho' : ℕ) : ℂ) * + testKernel (cutoffTest psi) (rescaledDiff T rho rho') ^ 2) = + (∑ rho ∈ s, ∑ rho' ∈ s, + ((zeroMultiplicity rho * zeroMultiplicity rho' : ℕ) : ℂ) * + fourierC (cutoffSelfConv psi) (rescaledDiff T rho rho') * pairWeight (rho - rho')) - + (∑ rho ∈ s, ∑ rho' ∈ s, + ((zeroMultiplicity rho * zeroMultiplicity rho' : ℕ) : ℂ) * + fourierC (iteratedDeriv 2 (cutoffSelfConv psi)) (rescaledDiff T rho rho') * + pairWeight (rho - rho')) / (((4 * Real.log T ^ 2 : ℝ) : ℂ)) + rw [Finset.sum_div] + simp_rw [Finset.sum_div] + rw [← Finset.sum_sub_distrib] + apply Finset.sum_congr rfl + intro rho hrho + rw [← Finset.sum_sub_distrib] + apply Finset.sum_congr rfl + intro rho' hrho' + have hrho_mem : rho ∈ nontrivialZeros T := by + simpa [s] using hrho + have hrho'_mem : rho' ∈ nontrivialZeros T := by + simpa [s] using hrho' + have hcancel := correctedTest_weight_cancel hpsi hT + hrho_mem.2.1 hrho_mem.2.2.1 hrho'_mem.2.1 hrho'_mem.2.2.1 + rw [← hcancel, hlinear] + ring + +/-- The quantitative pair-correlation hypothesis implies convergence to its stated main term. -/ +lemma PairCorrelation.tendsto (hPC : PairCorrelation) {f : ℝ → ℝ} + (hf : IsPairTestFunction f) : + Filter.Tendsto + (fun T => pairCorrelationSum f T / ((zeroScale T : ℝ) : ℂ)) + Filter.atTop (nhds ((pairMainTerm f : ℝ) : ℂ)) := by + obtain ⟨C, hC, T0, hbound⟩ := hPC f hf + have hsqrtlog : Filter.Tendsto (fun T : ℝ => Real.sqrt (Real.log T)) + Filter.atTop Filter.atTop := + Real.tendsto_sqrt_atTop.comp Real.tendsto_log_atTop + have hinv : Filter.Tendsto (fun T : ℝ => (Real.sqrt (Real.log T))⁻¹) + Filter.atTop (nhds 0) := tendsto_inv_atTop_zero.comp hsqrtlog + have herror_bound : Filter.Tendsto (fun T : ℝ => C / Real.sqrt (Real.log T)) + Filter.atTop (nhds 0) := by + simpa only [div_eq_mul_inv, mul_zero] using + (tendsto_const_nhds.mul hinv : Filter.Tendsto + (fun T : ℝ => C * (Real.sqrt (Real.log T))⁻¹) Filter.atTop (nhds (C * 0))) + have herror : Filter.Tendsto + (fun T => pairCorrelationSum f T / ((zeroScale T : ℝ) : ℂ) - + ((pairMainTerm f : ℝ) : ℂ)) Filter.atTop (nhds 0) := by + apply squeeze_zero_norm' + · filter_upwards [Filter.eventually_ge_atTop T0] with T hT + simpa only [zeroScale] using hbound T hT + · exact herror_bound + have hconst : Filter.Tendsto (fun _ : ℝ => ((pairMainTerm f : ℝ) : ℂ)) + Filter.atTop (nhds ((pairMainTerm f : ℝ) : ℂ)) := tendsto_const_nhds + convert hconst.add herror using 1 + · funext T + ring + · simp + +/-- The normalized unweighted cutoff-kernel sum converges to the pair-correlation functional of +the self-convolution. The second-derivative correction vanishes because of its `log T` squared +denominator. -/ +lemma unweightedKernelSum_cutoffTest_tendsto (hPC : PairCorrelation) {delta : ℝ} + (hd : 0 < delta) (hd4 : delta < 1 / 4) {psi : ℝ → ℝ} (hpsi : IsCutoff delta psi) : + Filter.Tendsto + (fun T => unweightedKernelSum (cutoffTest psi) T / ((zeroScale T : ℝ) : ℂ)) + Filter.atTop (nhds ((pairMainTerm (cutoffSelfConv psi) : ℝ) : ℂ)) := by + have hQ := hPC.tendsto (cutoffSelfConv_isPairTestFunction hd hd4 hpsi) + have hQ2 := hPC.tendsto + (iteratedDeriv_two_cutoffSelfConv_isPairTestFunction hpsi) + have hlog_sq : Filter.Tendsto (fun T : ℝ => Real.log T ^ 2) + Filter.atTop Filter.atTop := + (Filter.tendsto_pow_atTop (α := ℝ) (n := 2) (by norm_num)).comp + Real.tendsto_log_atTop + have hden : Filter.Tendsto (fun T : ℝ => 4 * Real.log T ^ 2) + Filter.atTop Filter.atTop := hlog_sq.const_mul_atTop' (by norm_num) + have hinv_real : Filter.Tendsto (fun T : ℝ => (4 * Real.log T ^ 2)⁻¹) + Filter.atTop (nhds 0) := tendsto_inv_atTop_zero.comp hden + have hinv_complex : Filter.Tendsto + (fun T : ℝ => ((((4 * Real.log T ^ 2 : ℝ) : ℂ)))⁻¹) + Filter.atTop (nhds 0) := by + convert (Complex.continuous_ofReal.tendsto 0).comp hinv_real using 1 + · funext T + exact (Complex.ofReal_inv _).symm + · rfl + have hQ2_small : Filter.Tendsto + (fun T => + (pairCorrelationSum (iteratedDeriv 2 (cutoffSelfConv psi)) T / + ((zeroScale T : ℝ) : ℂ)) / (((4 * Real.log T ^ 2 : ℝ) : ℂ))) + Filter.atTop (nhds 0) := by + simpa only [div_eq_mul_inv, mul_zero] using hQ2.mul hinv_complex + have hdiff : Filter.Tendsto + (fun T => + pairCorrelationSum (cutoffSelfConv psi) T / ((zeroScale T : ℝ) : ℂ) - + (pairCorrelationSum (iteratedDeriv 2 (cutoffSelfConv psi)) T / + ((zeroScale T : ℝ) : ℂ)) / (((4 * Real.log T ^ 2 : ℝ) : ℂ))) + Filter.atTop (nhds ((pairMainTerm (cutoffSelfConv psi) : ℝ) : ℂ)) := by + simpa only [sub_zero] using hQ.sub hQ2_small + apply hdiff.congr' + filter_upwards [Filter.eventually_gt_atTop (1 : ℝ)] with T hT + rw [unweightedKernelSum_cutoffTest_eq_pairCorrelationSum hpsi hT] + have hlog : Real.log T ≠ 0 := ne_of_gt (Real.log_pos hT) + have hdenR : (4 * Real.log T ^ 2 : ℝ) ≠ 0 := + mul_ne_zero (by norm_num) (pow_ne_zero 2 hlog) + have hdenC : (((4 * Real.log T ^ 2 : ℝ) : ℂ)) ≠ 0 := by + exact_mod_cast hdenR + have hscaleR : zeroScale T ≠ 0 := by + unfold zeroScale + exact mul_ne_zero + (div_ne_zero (ne_of_gt (lt_trans zero_lt_one hT)) + (mul_ne_zero (by norm_num) Real.pi_ne_zero)) hlog + have hscaleC : ((zeroScale T : ℝ) : ℂ) ≠ 0 := by + exact_mod_cast hscaleR + field_simp [hdenC, hscaleC] + +/-! ## Removing the cutoff + +The last step of the construction. We choose a +cutoff at scale `1 / (n + 5)`, use dominated convergence first for its normalising constant and +then for the two convolution integrals, and finally pass to the pair-correlation functional. +-/ + +/-- The extremal density is integrable on the real line. -/ +private lemma integrable_extremalTest : Integrable extremalTest := by + have hzero : ∀ x ∉ Set.Icc (-(1 / 2) : ℝ) (1 / 2), extremalTest x = 0 := by + intro x hx + rw [extremalTest, ite_eq_right] + intro habs + exact hx (Set.mem_Icc.mpr (abs_le.mp habs)) + have hcont : ContinuousOn extremalTest (Set.Icc (-(1 / 2) : ℝ) (1 / 2)) := by + have hG : Continuous fun x : ℝ => + Real.cos (Real.sqrt 2 * x) / + (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2)) := + (Real.continuous_cos.comp (continuous_const.mul continuous_id)).div_const _ + refine hG.continuousOn.congr ?_ + intro x hx + rw [extremalTest, ite_eq_left (abs_le.mpr (Set.mem_Icc.mp hx))] + exact hcont.integrableOn_Icc.integrable_of_forall_notMem_eq_zero hzero + +/-- A fixed pointwise upper bound for the extremal density. -/ +private noncomputable def extremalBound : ℝ := + 1 / (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2)) + +private lemma extremalBound_pos : 0 < extremalBound := by + unfold extremalBound + have hsqrt : 0 < Real.sqrt 2 := Real.sqrt_pos.2 (by norm_num) + have hsin : 0 < Real.sin (1 / Real.sqrt 2) := sin_inv_sqrt_two_pos + exact one_div_pos.mpr (mul_pos hsqrt hsin) + +private lemma extremalTest_le_bound (x : ℝ) : extremalTest x ≤ extremalBound := by + have hsqrt : 0 < Real.sqrt 2 := Real.sqrt_pos.2 (by norm_num) + have hsin : 0 < Real.sin (1 / Real.sqrt 2) := sin_inv_sqrt_two_pos + have hden : 0 < Real.sqrt 2 * Real.sin (1 / Real.sqrt 2) := mul_pos hsqrt hsin + unfold extremalBound extremalTest + split_ifs with hx + · apply (div_le_div_iff_of_pos_right hden).2 + exact (le_abs_self _).trans (Real.abs_cos_le_one _) + · exact (one_div_pos.mpr hden).le + +/-- A concrete sequence of cutoff widths tending to zero. -/ +private noncomputable def cutoffDelta (n : ℕ) : ℝ := 1 / ((n : ℝ) + 5) + +private lemma cutoffDelta_pos (n : ℕ) : 0 < cutoffDelta n := by + unfold cutoffDelta + positivity + +private lemma cutoffDelta_lt_quarter (n : ℕ) : cutoffDelta n < 1 / 4 := by + unfold cutoffDelta + rw [div_lt_iff₀ (by positivity : 0 < (n : ℝ) + 5)] + have hn : 0 ≤ (n : ℝ) := by positivity + linarith + +private lemma cutoffDelta_tendsto_zero : + Filter.Tendsto cutoffDelta Filter.atTop (nhds 0) := by + have h := (tendsto_mul_add_inv_atTop_nhds_zero (1 : ℝ) 5 (by norm_num)).comp + tendsto_natCast_atTop_atTop + convert h using 1 + funext n + simp only [cutoffDelta, Function.comp_apply, one_mul, one_div] + +/-- One cutoff at every width in `cutoffDelta`. -/ +private noncomputable def cutoffFamily (n : ℕ) : ℝ → ℝ := + Classical.choose (exists_isCutoff (cutoffDelta_pos n) (cutoffDelta_lt_quarter n)) + +private lemma cutoffFamily_isCutoff (n : ℕ) : + IsCutoff (cutoffDelta n) (cutoffFamily n) := + Classical.choose_spec (exists_isCutoff (cutoffDelta_pos n) (cutoffDelta_lt_quarter n)) + +private lemma cutoffFamily_tendsto_one {x : ℝ} (hx : |x| < 1 / 2) : + Filter.Tendsto (fun n => cutoffFamily n x) Filter.atTop (nhds 1) := by + have hgap : 0 < 1 / 2 - |x| := by linarith + have hevent : ∀ᶠ n : ℕ in Filter.atTop, cutoffDelta n < 1 / 2 - |x| := + (tendsto_order.1 cutoffDelta_tendsto_zero).2 _ hgap + have heq : (fun n => cutoffFamily n x) =ᶠ[Filter.atTop] (fun _ : ℕ => 1) := by + filter_upwards [hevent] with n hn + apply (cutoffFamily_isCutoff n).eq_one + linarith + exact tendsto_const_nhds.congr' heq.symm + +private lemma ae_cutoffWeight_tendsto : ∀ᵐ x : ℝ, + Filter.Tendsto (fun n => cutoffFamily n x ^ 2 * extremalTest x) + Filter.atTop (nhds (extremalTest x)) := by + have hboundary : ∀ᵐ x : ℝ, |x| ≠ 1 / 2 := by + rw [ae_iff] + have hset : {x : ℝ | ¬|x| ≠ 1 / 2} = {-(1 / 2), 1 / 2} := by + ext x + simp only [Set.mem_ofPred_eq, not_not, Set.mem_insert_iff, Set.mem_singleton_iff] + constructor + · intro hx + rcases abs_eq (show (0 : ℝ) ≤ 1 / 2 by norm_num) |>.mp hx with hx | hx + · exact Or.inr hx + · exact Or.inl hx + · rintro (hx | hx) <;> subst x <;> norm_num + rw [hset] + exact ((Set.finite_singleton (1 / 2 : ℝ)).insert (-(1 / 2))).countable.measure_zero volume + filter_upwards [hboundary] with x hx + rcases lt_or_gt_of_ne hx with hinside | houtside + · simpa only [one_pow, one_mul] using + (cutoffFamily_tendsto_one hinside).pow 2 |>.mul + (tendsto_const_nhds : Filter.Tendsto (fun _ : ℕ => extremalTest x) + Filter.atTop (nhds (extremalTest x))) + · have hf0 : extremalTest x = 0 := by + rw [extremalTest, ite_eq_right] + exact not_le.mpr houtside + simp only [hf0, mul_zero] + exact tendsto_const_nhds + +/-- The normalisers of the shrinking cutoffs converge to the total extremal mass, namely one. -/ +private lemma cutoffNormaliser_tendsto_one : + Filter.Tendsto (fun n => cutoffNormaliser (cutoffFamily n)) + Filter.atTop (nhds 1) := by + have hmeas : ∀ n : ℕ, AEStronglyMeasurable + (fun x => cutoffFamily n x ^ 2 * extremalTest x) volume := fun n => + (integrable_cutoffWeight (cutoffFamily_isCutoff n)).aestronglyMeasurable + have hbound : ∀ n : ℕ, ∀ᵐ x : ℝ, + ‖cutoffFamily n x ^ 2 * extremalTest x‖ ≤ extremalTest x := by + intro n + filter_upwards with x + have hpsi0 := (cutoffFamily_isCutoff n).nonneg x + have hpsi1 := (cutoffFamily_isCutoff n).le_one x + have hs0 : 0 ≤ cutoffFamily n x ^ 2 := sq_nonneg _ + have hs1 : cutoffFamily n x ^ 2 ≤ 1 := by nlinarith + rw [Real.norm_eq_abs, abs_of_nonneg (mul_nonneg hs0 (extremalTest_nonneg x))] + exact mul_le_of_le_one_left (extremalTest_nonneg x) hs1 + have hlim := MeasureTheory.tendsto_integral_of_dominated_convergence extremalTest + hmeas integrable_extremalTest hbound ae_cutoffWeight_tendsto + simpa only [cutoffNormaliser, integral_extremalTest] using hlim + +private lemma cutoffNormaliser_eventually_gt_half : + ∀ᶠ n : ℕ in Filter.atTop, 1 / 2 < cutoffNormaliser (cutoffFamily n) := + (tendsto_order.1 cutoffNormaliser_tendsto_one).1 _ (by norm_num) + +/-- Away from the two endpoints, the normalised cutoff densities converge to the extremal +density. Since the endpoints form a null set, this is exactly the almost-everywhere convergence +needed below. -/ +private lemma ae_cutoffTestSq_tendsto : ∀ᵐ x : ℝ, + Filter.Tendsto (fun n => cutoffTestSq (cutoffFamily n) x) + Filter.atTop (nhds (extremalTest x)) := by + filter_upwards [ae_cutoffWeight_tendsto] with x hx + have hdiv := hx.div cutoffNormaliser_tendsto_one (by norm_num : (1 : ℝ) ≠ 0) + have heq : (fun n => + cutoffFamily n x ^ 2 * extremalTest x / cutoffNormaliser (cutoffFamily n)) + =ᶠ[Filter.atTop] (fun n => cutoffTestSq (cutoffFamily n) x) := by + filter_upwards with n + exact (cutoffTestSq_eq (cutoffDelta_pos n) (cutoffDelta_lt_quarter n) + (cutoffFamily_isCutoff n) x).symm + simpa using hdiv.congr' heq + +/-- Eventually every normalised cutoff density is non-negative and bounded by twice the extremal +density. -/ +private lemma cutoffTestSq_eventually_bounds : ∀ᶠ n : ℕ in Filter.atTop, + ∀ x : ℝ, 0 ≤ cutoffTestSq (cutoffFamily n) x ∧ + cutoffTestSq (cutoffFamily n) x ≤ 2 * extremalTest x := by + filter_upwards [cutoffNormaliser_eventually_gt_half] with n hA + intro x + have hApos : 0 < cutoffNormaliser (cutoffFamily n) := by linarith + have hpsi0 := (cutoffFamily_isCutoff n).nonneg x + have hpsi1 := (cutoffFamily_isCutoff n).le_one x + have hs1 : cutoffFamily n x ^ 2 ≤ 1 := by nlinarith + have hf0 := extremalTest_nonneg x + rw [cutoffTestSq_eq (cutoffDelta_pos n) (cutoffDelta_lt_quarter n) + (cutoffFamily_isCutoff n) x] + constructor + · exact div_nonneg (mul_nonneg (sq_nonneg _) hf0) hApos.le + · apply (div_le_iff₀ hApos).2 + calc + cutoffFamily n x ^ 2 * extremalTest x ≤ extremalTest x := + mul_le_of_le_one_left hf0 hs1 + _ = (2 * extremalTest x) * (1 / 2) := by ring + _ ≤ (2 * extremalTest x) * cutoffNormaliser (cutoffFamily n) := + mul_le_mul_of_nonneg_left (le_of_lt hA) (mul_nonneg (by norm_num) hf0) + +private lemma integrable_extremalDominating : + Integrable (fun x : ℝ => 4 * extremalBound * extremalTest x) := by + exact integrable_extremalTest.const_mul (4 * extremalBound) + +/-- The self-convolutions of the normalised cutoff densities converge pointwise to the extremal +self-convolution. -/ +private lemma cutoffSelfConv_tendsto (x : ℝ) : + Filter.Tendsto (fun n => cutoffSelfConv (cutoffFamily n) x) + Filter.atTop (nhds (extremalSelfConv x)) := by + have hmeas : ∀ᶠ n : ℕ in Filter.atTop, AEStronglyMeasurable + (fun t : ℝ => cutoffTestSq (cutoffFamily n) t * + cutoffTestSq (cutoffFamily n) (x - t)) volume := by + filter_upwards with n + have hcont := (cutoffTestSq_contDiff (cutoffFamily_isCutoff n)).continuous + exact (hcont.mul (hcont.comp (continuous_const.sub continuous_id))).aestronglyMeasurable + have hbound : ∀ᶠ n : ℕ in Filter.atTop, ∀ᵐ t : ℝ, + ‖cutoffTestSq (cutoffFamily n) t * + cutoffTestSq (cutoffFamily n) (x - t)‖ ≤ + 4 * extremalBound * extremalTest t := by + filter_upwards [cutoffTestSq_eventually_bounds] with n hn + filter_upwards with t + rcases hn t with ⟨ht0, ht1⟩ + rcases hn (x - t) with ⟨hxt0, hxt1⟩ + rw [Real.norm_eq_abs, abs_of_nonneg (mul_nonneg ht0 hxt0)] + calc + cutoffTestSq (cutoffFamily n) t * cutoffTestSq (cutoffFamily n) (x - t) + ≤ (2 * extremalTest t) * (2 * extremalTest (x - t)) := + mul_le_mul ht1 hxt1 hxt0 (mul_nonneg (by norm_num) (extremalTest_nonneg t)) + _ ≤ (2 * extremalTest t) * (2 * extremalBound) := by + apply mul_le_mul_of_nonneg_left + · exact mul_le_mul_of_nonneg_left (extremalTest_le_bound (x - t)) (by norm_num) + · exact mul_nonneg (by norm_num) (extremalTest_nonneg t) + _ = 4 * extremalBound * extremalTest t := by ring + have hlim_left := ae_cutoffTestSq_tendsto + have hlim_right := (volume.measurePreserving_sub_left x).quasiMeasurePreserving.ae + ae_cutoffTestSq_tendsto + have hlim : ∀ᵐ t : ℝ, Filter.Tendsto + (fun n => cutoffTestSq (cutoffFamily n) t * + cutoffTestSq (cutoffFamily n) (x - t)) Filter.atTop + (nhds (extremalTest t * extremalTest (x - t))) := by + filter_upwards [hlim_left, hlim_right] with t ht hxt + exact ht.mul hxt + have h := MeasureTheory.tendsto_integral_filter_of_dominated_convergence + (fun t : ℝ => 4 * extremalBound * extremalTest t) + hmeas hbound integrable_extremalDominating hlim + simpa only [cutoffSelfConv, extremalSelfConv] using h + +/-- The same dominating function gives a uniform bound for all sufficiently small-cutoff +self-convolutions. -/ +private lemma cutoffSelfConv_eventually_bounds : ∀ᶠ n : ℕ in Filter.atTop, + ∀ x : ℝ, 0 ≤ cutoffSelfConv (cutoffFamily n) x ∧ + cutoffSelfConv (cutoffFamily n) x ≤ 4 * extremalBound := by + filter_upwards [cutoffTestSq_eventually_bounds] with n hn + intro x + have hcont := (cutoffTestSq_contDiff (cutoffFamily_isCutoff n)).continuous + have hmeas : AEStronglyMeasurable + (fun t : ℝ => cutoffTestSq (cutoffFamily n) t * + cutoffTestSq (cutoffFamily n) (x - t)) volume := + (hcont.mul (hcont.comp (continuous_const.sub continuous_id))).aestronglyMeasurable + have hnonneg : ∀ t : ℝ, 0 ≤ cutoffTestSq (cutoffFamily n) t * + cutoffTestSq (cutoffFamily n) (x - t) := fun t => mul_nonneg (hn t).1 (hn (x - t)).1 + have hpoint : ∀ t : ℝ, + cutoffTestSq (cutoffFamily n) t * cutoffTestSq (cutoffFamily n) (x - t) ≤ + 4 * extremalBound * extremalTest t := by + intro t + calc + cutoffTestSq (cutoffFamily n) t * cutoffTestSq (cutoffFamily n) (x - t) + ≤ (2 * extremalTest t) * (2 * extremalTest (x - t)) := + mul_le_mul (hn t).2 (hn (x - t)).2 (hn (x - t)).1 + (mul_nonneg (by norm_num) (extremalTest_nonneg t)) + _ ≤ (2 * extremalTest t) * (2 * extremalBound) := by + apply mul_le_mul_of_nonneg_left + · exact mul_le_mul_of_nonneg_left (extremalTest_le_bound (x - t)) (by norm_num) + · exact mul_nonneg (by norm_num) (extremalTest_nonneg t) + _ = 4 * extremalBound * extremalTest t := by ring + have hnorm : ∀ᵐ t : ℝ, + ‖cutoffTestSq (cutoffFamily n) t * cutoffTestSq (cutoffFamily n) (x - t)‖ ≤ + 4 * extremalBound * extremalTest t := by + filter_upwards with t + rw [Real.norm_eq_abs, abs_of_nonneg (hnonneg t)] + exact hpoint t + have hint : Integrable (fun t : ℝ => cutoffTestSq (cutoffFamily n) t * + cutoffTestSq (cutoffFamily n) (x - t)) := + integrable_extremalDominating.mono' hmeas hnorm + constructor + · unfold cutoffSelfConv + exact MeasureTheory.integral_nonneg hnonneg + · unfold cutoffSelfConv + calc + (∫ t : ℝ, cutoffTestSq (cutoffFamily n) t * + cutoffTestSq (cutoffFamily n) (x - t)) + ≤ ∫ t : ℝ, 4 * extremalBound * extremalTest t := + MeasureTheory.integral_mono_ae hint integrable_extremalDominating + (Filter.Eventually.of_forall hpoint) + _ = 4 * extremalBound := by + rw [MeasureTheory.integral_const_mul, integral_extremalTest, mul_one] + +private lemma cutoffSelfConv_interval_tendsto : + Filter.Tendsto + (fun n => ∫ α in (0 : ℝ)..1, α * cutoffSelfConv (cutoffFamily n) α) + Filter.atTop + (nhds (∫ α in (0 : ℝ)..1, α * extremalSelfConv α)) := by + have hmeas : ∀ᶠ n : ℕ in Filter.atTop, AEStronglyMeasurable + (fun α : ℝ => α * cutoffSelfConv (cutoffFamily n) α) + (volume.restrict (Set.uIoc (0 : ℝ) 1)) := by + filter_upwards with n + have hcont := (contDiff_cutoffSelfConv (cutoffFamily_isCutoff n)).continuous + exact (continuous_id.mul hcont).aestronglyMeasurable + have hbound : ∀ᶠ n : ℕ in Filter.atTop, ∀ᵐ α : ℝ, + α ∈ Set.uIoc (0 : ℝ) 1 → + ‖α * cutoffSelfConv (cutoffFamily n) α‖ ≤ 4 * extremalBound := by + filter_upwards [cutoffSelfConv_eventually_bounds] with n hn + filter_upwards with α + intro hα + rw [Set.uIoc_of_le (by norm_num : (0 : ℝ) ≤ 1)] at hα + have hα0 : 0 ≤ α := le_of_lt hα.1 + rcases hn α with ⟨hQ0, hQ1⟩ + rw [Real.norm_eq_abs, abs_of_nonneg (mul_nonneg hα0 hQ0)] + calc + α * cutoffSelfConv (cutoffFamily n) α + ≤ 1 * cutoffSelfConv (cutoffFamily n) α := + mul_le_mul_of_nonneg_right hα.2 hQ0 + _ ≤ 4 * extremalBound := by simpa using hQ1 + have hlim : ∀ᵐ α : ℝ, α ∈ Set.uIoc (0 : ℝ) 1 → Filter.Tendsto + (fun n => α * cutoffSelfConv (cutoffFamily n) α) Filter.atTop + (nhds (α * extremalSelfConv α)) := by + filter_upwards with α + intro _ + exact tendsto_const_nhds.mul (cutoffSelfConv_tendsto α) + exact intervalIntegral.tendsto_integral_filter_of_dominated_convergence + (μ := volume) (a := (0 : ℝ)) (b := 1) + (F := fun n α => α * cutoffSelfConv (cutoffFamily n) α) + (f := fun α => α * extremalSelfConv α) + (fun _ : ℝ => 4 * extremalBound) hmeas hbound intervalIntegrable_const hlim + +private lemma cutoffPairMainTerm_tendsto : + Filter.Tendsto (fun n => pairMainTerm (cutoffSelfConv (cutoffFamily n))) + Filter.atTop (nhds (pairMainTerm extremalSelfConv)) := by + simpa only [pairMainTerm] using + (cutoffSelfConv_tendsto 0).add (cutoffSelfConv_interval_tendsto.const_mul 2) + +/-- **The cutoff constants converge to the Montgomery--Taylor constant** +(`lem_C_delta_limit`). -/ +@[zz_tag "lem_C_delta_limit"] +theorem exists_cutoff_pairMainTerm_close (ε : ℝ) (hε : 0 < ε) : + ∃ delta : ℝ, ∃ psi : ℝ → ℝ, + 0 < delta ∧ delta < 1 / 4 ∧ IsCutoff delta psi ∧ + |pairMainTerm (cutoffSelfConv psi) - montgomeryTaylorConst| < ε := by + have hMT : pairMainTerm extremalSelfConv = montgomeryTaylorConst := by + unfold pairMainTerm + exact montgomeryTaylor + have hlim : Filter.Tendsto (fun n => pairMainTerm (cutoffSelfConv (cutoffFamily n))) + Filter.atTop (nhds montgomeryTaylorConst) := by + rw [← hMT] + exact cutoffPairMainTerm_tendsto + obtain ⟨N, hN⟩ := (Metric.tendsto_atTop.1 hlim) ε hε + refine ⟨cutoffDelta N, cutoffFamily N, cutoffDelta_pos N, + cutoffDelta_lt_quarter N, cutoffFamily_isCutoff N, ?_⟩ + simpa only [Real.dist_eq] using hN N le_rfl + +/-- **Kernel construction** (`lem_kernel_construction`). The cutoff test is admissible, its +pair-correlation constant is arbitrarily close to the Montgomery--Taylor constant, and the +normalized unweighted kernel sum converges to that constant. -/ +@[zz_tag "lem_kernel_construction"] +theorem kernelConstruction (hPC : PairCorrelation) (ε : ℝ) (hε : 0 < ε) : + ∃ eta C, + IsAdmissible (1 / 2) eta ∧ + |C - montgomeryTaylorConst| < ε ∧ + Filter.Tendsto + (fun T => (unweightedKernelSum eta T).re / zeroScale T) + Filter.atTop (nhds C) := by + obtain ⟨delta, psi, hd, hd4, hpsi, hclose⟩ := exists_cutoff_pairMainTerm_close ε hε + refine ⟨cutoffTest psi, pairMainTerm (cutoffSelfConv psi), + isAdmissible_cutoffTest hd hd4 hpsi, hclose, ?_⟩ + have hreal : Filter.Tendsto + (Complex.re ∘ fun T => + unweightedKernelSum (cutoffTest psi) T / ((zeroScale T : ℝ) : ℂ)) + Filter.atTop (nhds (pairMainTerm (cutoffSelfConv psi))) := + (Complex.continuous_re.tendsto + ((pairMainTerm (cutoffSelfConv psi) : ℝ) : ℂ)).comp + (unweightedKernelSum_cutoffTest_tendsto hPC hd hd4 hpsi) + convert hreal using 1 + funext T + exact (Complex.div_ofReal_re _ _).symm + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Zeta/Mass.lean b/LeanPool/ZetaZeros/Zeta/Mass.lean new file mode 100644 index 0000000000..f6038b5152 --- /dev/null +++ b/LeanPool/ZetaZeros/Zeta/Mass.lean @@ -0,0 +1,63 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public import LeanPool.ZetaZeros.Zeta.Basic + +/-! +# The extremal test function has total mass one + +The normalisation that makes `f₀` a probability density, and hence makes the Montgomery–Taylor +constant the value of the pair-correlation functional at its self-convolution rather than a +multiple of it. + +The constant `√2 sin(1/√2)` in the denominator of `f₀` is exactly what the substitution +`x ↦ √2 x` produces, which is why the mass comes out at one on the nose. +-/ + +@[expose] public section + +namespace ZetaZeros + +open MeasureTheory intervalIntegral + +/-- The extremal test function vanishes off `[-1/2, 1/2]`. -/ +private lemma extremalTest_eq_zero_of_notMem {x : ℝ} (hx : x ∉ Set.Icc (-(1 / 2) : ℝ) (1 / 2)) : + extremalTest x = 0 := by + rw [extremalTest, ite_eq_right] + intro habs + exact hx (abs_le.mp habs) + +/-- **The extremal test function has total mass one.** -/ +@[zz_tag "lem_f0_integral"] +theorem integral_extremalTest : ∫ x : ℝ, extremalTest x = 1 := by + have hs2 : 0 < Real.sqrt 2 := Real.sqrt_pos.mpr (by norm_num) + have hsin : 0 < Real.sin (1 / Real.sqrt 2) := sin_inv_sqrt_two_pos + have hden : Real.sqrt 2 * Real.sin (1 / Real.sqrt 2) ≠ 0 := ne_of_gt (mul_pos hs2 hsin) + -- restrict to the support + rw [← setIntegral_eq_integral_of_forall_compl_eq_zero + (s := Set.Icc (-(1/2) : ℝ) (1/2)) fun x hx => extremalTest_eq_zero_of_notMem hx] + rw [MeasureTheory.integral_Icc_eq_integral_Ioc, ← intervalIntegral.integral_of_le (by norm_num)] + -- on the interval the function is the cosine quotient + have hcongr : ∫ x in (-(1/2) : ℝ)..(1/2), extremalTest x + = ∫ x in (-(1/2) : ℝ)..(1/2), + Real.cos (Real.sqrt 2 * x) / (Real.sqrt 2 * Real.sin (1 / Real.sqrt 2)) := by + refine intervalIntegral.integral_congr fun x hx => ?_ + rw [Set.uIcc_of_le (by norm_num : (-(1/2) : ℝ) ≤ 1/2)] at hx + rw [extremalTest, ite_eq_left (abs_le.mpr hx)] + rw [hcongr, intervalIntegral.integral_div, + intervalIntegral.integral_comp_mul_left Real.cos (ne_of_gt hs2), integral_cos] + -- √2 * (1/2) = 1/√2, so the bracket is 2 sin(1/√2) + have hhalf : Real.sqrt 2 * (1 / 2) = 1 / Real.sqrt 2 := by + rw [eq_div_iff (ne_of_gt hs2)] + nlinarith [Real.sq_sqrt (by norm_num : (0:ℝ) ≤ 2)] + rw [show Real.sqrt 2 * (-(1/2) : ℝ) = -(Real.sqrt 2 * (1/2)) by ring, hhalf, Real.sin_neg] + simp only [smul_eq_mul] + field_simp + rw [Real.sq_sqrt (by norm_num : (0:ℝ) ≤ 2)] + norm_num + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Zeta/OrderConj.lean b/LeanPool/ZetaZeros/Zeta/OrderConj.lean new file mode 100644 index 0000000000..6331fc2efd --- /dev/null +++ b/LeanPool/ZetaZeros/Zeta/OrderConj.lean @@ -0,0 +1,272 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public import Mathlib.NumberTheory.Harmonic.ZetaAsymp +public import LeanPool.ZetaZeros.Zeta.Defs + +/-! +# Conjugation preserves the multiplicity of a zero + +BOTH of the symmetries whose composition `ρ ↦ 1 - conj ρ` makes the rescaled zeros +conjugation-invariant. The conjugation half is the cheap one: it follows from `riemannZeta_conj` +together with the fact that pre- and post-composing with conjugation does not change an analytic +order. The reflection half needs the functional equation, and needs the order to be transported +across `ρ ↦ 1 - ρ`, for which Mathlib has nothing ready-made. + +The three private lemmas below are adapted, with thanks, from +`AxiomMath/PrimeNumberTheoremAnd`, `PrimeNumberTheoremAnd/IEANTN/KadiriZeroCounting.lean`, where +they support the same statement for that project's own order function. +-/ + +@[expose] public section + +namespace ZetaZeros + +/-- If `f` has derivative `f'` at `conj z₀`, the double conjugate has derivative `conj f'` at +`z₀`. -/ +private lemma hasDerivAt_conj_conj {f : ℂ → ℂ} {f' z₀ : ℂ} + (hf : HasDerivAt f f' ((starRingEnd ℂ) z₀)) : + HasDerivAt (fun w ↦ (starRingEnd ℂ) (f ((starRingEnd ℂ) w))) + ((starRingEnd ℂ) f') z₀ := by + rw [hasDerivAt_iff_tendsto_slope] at hf ⊢ + have hconj_tendsto : Filter.Tendsto (starRingEnd ℂ) + (nhdsWithin z₀ {z₀}ᶜ) + (nhdsWithin ((starRingEnd ℂ) z₀) {(starRingEnd ℂ) z₀}ᶜ) := by + apply tendsto_nhdsWithin_of_tendsto_nhds_of_eventually_within + · exact (Complex.continuous_conj.tendsto z₀).mono_left nhdsWithin_le_nhds + · filter_upwards [self_mem_nhdsWithin] with w hw + intro h + apply hw + have := congrArg (starRingEnd ℂ) h + simpa [Complex.conj_conj] using this + have hcomp := (Complex.continuous_conj.tendsto f').comp (hf.comp hconj_tendsto) + refine Filter.Tendsto.congr (fun u ↦ ?_) hcomp + simp only [Function.comp_apply, slope_def_field, map_div₀, map_sub, Complex.conj_conj] + +/-- Analyticity of the double conjugate. -/ +private lemma analyticAt_conj_conj {f : ℂ → ℂ} {z₀ : ℂ} + (hf : AnalyticAt ℂ f ((starRingEnd ℂ) z₀)) : + AnalyticAt ℂ (fun w ↦ (starRingEnd ℂ) (f ((starRingEnd ℂ) w))) z₀ := by + rw [Complex.analyticAt_iff_eventually_differentiableAt] at hf ⊢ + have hc : Filter.Tendsto (starRingEnd ℂ) (nhds z₀) (nhds ((starRingEnd ℂ) z₀)) := + Complex.continuous_conj.tendsto z₀ + filter_upwards [hc.eventually hf] with w hw + exact (hasDerivAt_conj_conj hw.hasDerivAt).differentiableAt + +/-- The analytic order of the double conjugate at `z₀` is the analytic order of `f` at +`conj z₀`. -/ +private lemma analyticOrderAt_conj_conj {f : ℂ → ℂ} {z₀ : ℂ} + (hf : AnalyticAt ℂ f ((starRingEnd ℂ) z₀)) : + analyticOrderAt (fun w ↦ (starRingEnd ℂ) (f ((starRingEnd ℂ) w))) z₀ = + analyticOrderAt f ((starRingEnd ℂ) z₀) := by + have hcfc := analyticAt_conj_conj hf + have hc : Filter.Tendsto (starRingEnd ℂ) (nhds z₀) (nhds ((starRingEnd ℂ) z₀)) := + Complex.continuous_conj.tendsto z₀ + cases h : analyticOrderAt f ((starRingEnd ℂ) z₀) with + | top => + rw [analyticOrderAt_eq_top] at h ⊢ + filter_upwards [hc.eventually h] with w hw + rw [hw, map_zero] + | coe n => + rw [hf.analyticOrderAt_eq_natCast] at h + obtain ⟨g, hg, hg0, hfg⟩ := h + rw [hcfc.analyticOrderAt_eq_natCast] + refine ⟨fun w ↦ (starRingEnd ℂ) (g ((starRingEnd ℂ) w)), + analyticAt_conj_conj hg, ?_, ?_⟩ + · intro h0 + apply hg0 + have := congrArg (starRingEnd ℂ) h0 + simpa [Complex.conj_conj] using this + · filter_upwards [hc.eventually hfg] with w hw + rw [hw] + simp only [smul_eq_mul, map_mul, map_pow, map_sub, Complex.conj_conj] + +/-- Zeta is analytic away from its pole. -/ +private lemma analyticAt_riemannZeta {s : ℂ} (hs : s ≠ 1) : AnalyticAt ℂ riemannZeta s := by + rw [Complex.analyticAt_iff_eventually_differentiableAt] + filter_upwards [isOpen_ne.mem_nhds hs] with w hw + exact differentiableAt_riemannZeta hw + +/-- **Conjugation preserves the multiplicity of a zero.** -/ +@[zz_tag "lem_order_conj"] +theorem zeroMultiplicity_conj {ρ : ℂ} (hρ : ρ ≠ 1) : + zeroMultiplicity ((starRingEnd ℂ) ρ) = zeroMultiplicity ρ := by + have hρ' : (starRingEnd ℂ) ρ ≠ 1 := by + intro h + apply hρ + have := congrArg (starRingEnd ℂ) h + simpa [Complex.conj_conj] using this + have key := analyticOrderAt_conj_conj (analyticAt_riemannZeta hρ') + have hfun : (fun w ↦ (starRingEnd ℂ) (riemannZeta ((starRingEnd ℂ) w))) = riemannZeta := by + funext w + rw [riemannZeta_conj, Complex.conj_conj] + rw [hfun] at key + simp only [zeroMultiplicity, analyticOrderNatAt, key] + +/-! ### The functional equation preserves multiplicity + +Mathlib has no form of this statement; what it supplies is `riemannZeta_one_sub`, the functional +equation in the form +`zeta (1 - s) = functionalEqFactor s * zeta s`. + +Two things then have to be shown, and the second is the real work: + +* `functionalEqFactor` is analytic and NON-VANISHING on the strip. `Gamma` is non-zero where the + real part is positive, the `cpow` factor is an `exp`, and `cos (pi s / 2)` vanishes only at odd + integers -- none of which have real part strictly between `0` and `1`. +* The order has to move ACROSS the reflection, from `rho` to `1 - rho`. Mathlib has no lemma for + that, so `analyticOrderAt_comp_const_sub` is proved here: the order of `w -> f (a - w)` at `z` is + the order of `f` at `a - z`. It is stated for a general `f` and `a` because nothing in it is about + zeta, and it is the piece worth reusing. -/ + +/-- One direction of the transport of the vanishing order along `w ↦ a - w`. -/ +theorem le_analyticOrderAt_comp_const_sub {f : ℂ → ℂ} {a z : ℂ} {n : ℕ} + (h : (n : ℕ∞) ≤ analyticOrderAt f (a - z)) : + (n : ℕ∞) ≤ analyticOrderAt (fun w => f (a - w)) z := by + by_cases hf : AnalyticAt ℂ f (a - z) + · rw [natCast_le_analyticOrderAt hf] at h + obtain ⟨g, hg, hfg⟩ := h + have hinner : AnalyticAt ℂ (fun w : ℂ => a - w) z := + analyticAt_const.sub analyticAt_id + have htend : Filter.Tendsto (fun w : ℂ => a - w) (nhds z) (nhds (a - z)) := + (continuous_const.sub continuous_id).tendsto z + have hfc : AnalyticAt ℂ (fun w => f (a - w)) z := hf.comp hinner + have hgc : AnalyticAt ℂ (fun w => g (a - w)) z := hg.comp hinner + refine (natCast_le_analyticOrderAt hfc).2 + ⟨fun w => (-1 : ℂ) ^ n * g (a - w), analyticAt_const.mul hgc, ?_⟩ + · filter_upwards [htend.eventually hfg] with w hw + simp only [smul_eq_mul] at hw ⊢ + rw [hw, show a - w - (a - z) = -(w - z) by ring, neg_pow] + ring + · rw [analyticOrderAt_of_not_analyticAt hf, nonpos_iff_eq_zero, Nat.cast_eq_zero] at h + subst h + simp + +/-- **Transport of the vanishing order along an affine reflection.** -/ +theorem analyticOrderAt_comp_const_sub (f : ℂ → ℂ) (a z : ℂ) : + analyticOrderAt (fun w => f (a - w)) z = analyticOrderAt f (a - z) := by + refine le_antisymm (ENat.forall_natCast_le_iff_le.mp ?_) + (ENat.forall_natCast_le_iff_le.mp ?_) + · intro n hn + have h2 : (n : ℕ∞) ≤ analyticOrderAt (fun w => f (a - w)) (a - (a - z)) := by + rwa [sub_sub_cancel] + have h3 := le_analyticOrderAt_comp_const_sub h2 + have hfun : (fun w : ℂ => f (a - (a - w))) = f := by + funext w; rw [sub_sub_cancel] + rw [hfun] at h3 + exact h3 + · intro n hn + exact le_analyticOrderAt_comp_const_sub hn + +/-! ### The factor in the functional equation -/ + +/-- The factor in `ζ (1 - s) = functionalEqFactor s * ζ s`, written with `exp` rather than `cpow` +so that its analyticity is visible. -/ +noncomputable def functionalEqFactor (s : ℂ) : ℂ := + 2 * Complex.exp (Complex.log (2 * (Real.pi : ℂ)) * (-s)) * Complex.Gamma s * + Complex.cos ((Real.pi : ℂ) * s / 2) + +theorem two_mul_pi_ne_zero : (2 * (Real.pi : ℂ)) ≠ 0 := + mul_ne_zero two_ne_zero (Complex.ofReal_ne_zero.mpr Real.pi_ne_zero) + +theorem zeta_one_sub_eq (s : ℂ) (hn : ∀ n : ℕ, s ≠ -(n : ℂ)) (h1 : s ≠ 1) : + riemannZeta (1 - s) = functionalEqFactor s * riemannZeta s := by + rw [riemannZeta_one_sub hn h1, functionalEqFactor, Complex.cpow_def_of_ne_zero two_mul_pi_ne_zero] + +theorem differentiableOn_functionalEqFactor : + DifferentiableOn ℂ functionalEqFactor {s : ℂ | 0 < s.re} := by + intro s hs + have hs' : ∀ m : ℕ, s ≠ -(m : ℂ) := by + intro m hm + rw [hm] at hs + simp only [Set.mem_ofPred_eq] at hs + simp at hs + linarith [hs] + have hGamma : DifferentiableAt ℂ Complex.Gamma s := Complex.differentiableAt_Gamma s hs' + have : DifferentiableAt ℂ functionalEqFactor s := by + unfold functionalEqFactor + exact (((differentiableAt_const _).mul + ((Complex.differentiable_exp _).comp _ + (((differentiableAt_const _).mul (differentiableAt_id.neg))))).mul hGamma).mul + ((Complex.differentiable_cos _).comp _ + (((differentiableAt_const _).mul differentiableAt_id).div_const _)) + exact this.differentiableWithinAt + +theorem analyticAt_functionalEqFactor {ρ : ℂ} (h0 : 0 < ρ.re) : + AnalyticAt ℂ functionalEqFactor ρ := by + refine differentiableOn_functionalEqFactor.analyticAt ?_ + exact (isOpen_lt continuous_const Complex.continuous_re).mem_nhds h0 + +theorem functionalEqFactor_ne_zero {ρ : ℂ} (h0 : 0 < ρ.re) (h1 : ρ.re < 1) : + functionalEqFactor ρ ≠ 0 := by + have hpi : (Real.pi : ℂ) ≠ 0 := Complex.ofReal_ne_zero.mpr Real.pi_ne_zero + have hGamma : Complex.Gamma ρ ≠ 0 := Complex.Gamma_ne_zero_of_re_pos h0 + have hcos : Complex.cos ((Real.pi : ℂ) * ρ / 2) ≠ 0 := by + intro hc + rw [Complex.cos_eq_zero_iff] at hc + obtain ⟨k, hk⟩ := hc + have h2 : (Real.pi : ℂ) * ρ = (Real.pi : ℂ) * (2 * (k : ℂ) + 1) := by + linear_combination 2 * hk + have hρ : ρ = 2 * (k : ℂ) + 1 := mul_left_cancel₀ hpi h2 + have hre : ρ.re = 2 * (k : ℝ) + 1 := by + rw [hρ]; simp + rw [hre] at h0 h1 + have hk0 : (0 : ℝ) < 2 * (k : ℝ) + 1 := h0 + have hk1 : 2 * (k : ℝ) + 1 < 1 := h1 + have : (0 : ℤ) < 2 * k + 1 := by + have : (0 : ℝ) < ((2 * k + 1 : ℤ) : ℝ) := by push_cast; linarith + exact_mod_cast this + have : (2 * k + 1 : ℤ) < 1 := by + have : ((2 * k + 1 : ℤ) : ℝ) < ((1 : ℤ) : ℝ) := by push_cast; linarith + exact_mod_cast this + omega + unfold functionalEqFactor + have hexp : Complex.exp (Complex.log (2 * (Real.pi : ℂ)) * (-ρ)) ≠ 0 := Complex.exp_ne_zero _ + exact mul_ne_zero (mul_ne_zero (mul_ne_zero two_ne_zero hexp) hGamma) hcos + +/-- **The functional equation preserves multiplicity.** For every `ρ` in the open critical strip, +the multiplicity of `1 - ρ` as a zero of `ζ` equals that of `ρ`. -/ +@[zz_tag "lem_order_one_sub"] +theorem zeroMultiplicity_one_sub {ρ : ℂ} (h0 : 0 < ρ.re) (h1 : ρ.re < 1) : + zeroMultiplicity (1 - ρ) = zeroMultiplicity ρ := by + have key : analyticOrderAt riemannZeta (1 - ρ) = analyticOrderAt riemannZeta ρ := by + -- Step 1: reflect + rw [← analyticOrderAt_comp_const_sub riemannZeta 1 ρ] + -- Step 2: use the functional equation near `ρ` + have hU : IsOpen {s : ℂ | 0 < s.re ∧ s.re < 1} := + (isOpen_lt continuous_const Complex.continuous_re).inter + (isOpen_lt Complex.continuous_re continuous_const) + have hmem : {s : ℂ | 0 < s.re ∧ s.re < 1} ∈ nhds ρ := hU.mem_nhds ⟨h0, h1⟩ + have heq : (fun w => riemannZeta (1 - w)) =ᶠ[nhds ρ] + fun w => functionalEqFactor w * riemannZeta w := by + filter_upwards [hmem] with s hs + obtain ⟨hs0, hs1⟩ := hs + have hne1 : s ≠ 1 := by + intro h; rw [h] at hs1; simp at hs1 + have hnen : ∀ n : ℕ, s ≠ -(n : ℂ) := by + intro m hm + rw [hm] at hs0 + simp at hs0 + linarith [hs0] + exact zeta_one_sub_eq s hnen hne1 + rw [analyticOrderAt_congr heq] + -- Step 3: multiplication by an analytic non-vanishing factor + have hz : AnalyticAt ℂ riemannZeta ρ := by + refine differentiableOn_riemannZeta.analyticAt ?_ + refine IsOpen.mem_nhds isOpen_compl_singleton ?_ + simp only [Set.mem_compl_iff, Set.mem_singleton_iff] + intro h; rw [h] at h1; simp at h1 + have hG := analyticAt_functionalEqFactor h0 + have : (fun w => functionalEqFactor w * riemannZeta w) = functionalEqFactor * riemannZeta := rfl + rw [this, analyticOrderAt_mul hG hz] + have : analyticOrderAt functionalEqFactor ρ = 0 := + analyticOrderAt_eq_zero.2 (Or.inr (functionalEqFactor_ne_zero h0 h1)) + rw [this, zero_add] + unfold zeroMultiplicity analyticOrderNatAt + rw [key] + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Zeta/Proportion.lean b/LeanPool/ZetaZeros/Zeta/Proportion.lean new file mode 100644 index 0000000000..ea3e2e2f01 --- /dev/null +++ b/LeanPool/ZetaZeros/Zeta/Proportion.lean @@ -0,0 +1,68 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kenny Lau +-/ +module + +public import LeanPool.ZetaZeros.Zeta.Asymptotics + +/-! +# The final asymptotic passage + +Once the zero count and kernel energy have their required normalized limits, the two finite-height +inequalities turn into the claimed eventual proportion bounds. +-/ + +@[expose] public section + +namespace ZetaZeros + +open Filter Topology + +/-- Dividing two quantities normalized by the same eventually non-zero scale preserves their +quotient limit. -/ +theorem tendsto_ratio_of_tendsto_div_scale + (N S scale : ℝ → ℝ) (C : ℝ) + (hN : Tendsto (fun T => N T / scale T) atTop (nhds 1)) + (hS : Tendsto (fun T => S T / scale T) atTop (nhds C)) + (hscale : ∀ᶠ T in atTop, scale T ≠ 0) : + Tendsto (fun T => S T / N T) atTop (nhds C) := by + have hdiv := hS.div hN (by norm_num : (1 : ℝ) ≠ 0) + have heq : (fun T => S T / N T) =ᶠ[atTop] + (fun T => (S T / scale T) / (N T / scale T)) := by + filter_upwards [hscale] with T hs + field_simp + simpa only [div_one] using hdiv.congr' heq.symm + +/-- The simple-real lower bound gives the eventual simple-zero proportion once the kernel energy +per zero tends to `C`. -/ +theorem eventually_simple_proportion + (N A S : ℝ → ℝ) (C ε : ℝ) (hε : 0 < ε) + (hNpos : ∀ᶠ T in atTop, 0 < N T) + (hS : Tendsto (fun T => S T / N T) atTop (nhds C)) + (hbound : ∀ᶠ T in atTop, 2 * N T - S T ≤ A T) : + ∀ᶠ T in atTop, 2 - C - ε < A T / N T := by + have hlt : ∀ᶠ T in atTop, S T / N T < C + ε := + (tendsto_order.1 hS).2 _ (by linarith) + filter_upwards [hNpos, hlt, hbound] with T hNT hST hAT + rw [lt_div_iff₀ hNT] + have hSN : S T < (C + ε) * N T := (div_lt_iff₀ hNT).mp hST + nlinarith + +/-- The distinct-element lower bound gives the eventual distinct-zero proportion once the kernel +energy per zero tends to `C`. -/ +theorem eventually_distinct_proportion + (N A S : ℝ → ℝ) (C ε : ℝ) (hε : 0 < ε) + (hNpos : ∀ᶠ T in atTop, 0 < N T) + (hS : Tendsto (fun T => S T / N T) atTop (nhds C)) + (hbound : ∀ᶠ T in atTop, 3 / 2 * N T - S T / 2 ≤ A T) : + ∀ᶠ T in atTop, 3 / 2 - C / 2 - ε < A T / N T := by + have hlt : ∀ᶠ T in atTop, S T / N T < C + 2 * ε := + (tendsto_order.1 hS).2 _ (by linarith) + filter_upwards [hNpos, hlt, hbound] with T hNT hST hAT + rw [lt_div_iff₀ hNT] + have hSN : S T < (C + 2 * ε) * N T := (div_lt_iff₀ hNT).mp hST + nlinarith + +end ZetaZeros diff --git a/LeanPool/ZetaZeros/Zeta/Transfer.lean b/LeanPool/ZetaZeros/Zeta/Transfer.lean new file mode 100644 index 0000000000..88b2381d9f --- /dev/null +++ b/LeanPool/ZetaZeros/Zeta/Transfer.lean @@ -0,0 +1,66 @@ +/- +Copyright (c) 2026 Axiom Math. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Axiom Math +-/ +module + +public import LeanPool.ZetaZeros.Zeta.Kernel + +/-! +# Transfer of the Hilbert-space inequalities to zeta zeros + +Conjugation invariance of the rescaled zero multiset, the identification of its finite kernel sum +with the canonical sum over zeros, and the transfer of the two abstract finite-set inequalities to +the three zero-counting functions. +-/ + +@[expose] public section + +namespace ZetaZeros + + +/-- **The finite-set lower bound transferred to simple zeros on the critical line.** -/ +@[zz_tag "lem_N_simple_lower"] +theorem simpleOnLineCount_lower {lam T : ℝ} {eta : ℝ → ℝ} + (hT : 1 < T) (hη : IsAdmissible lam eta) : + 2 * (zeroCount T : ℝ) - (unweightedKernelSum eta T).re + ≤ (simpleOnLineCount T : ℝ) := by + have hbound := card_simpleRealPart_lower hη (isConjInvariant_rescaledZerosFinset hT) + have hsum : + ∑ z ∈ rescaledZerosFinset T, (rescaledMult T z : ℝ) = (zeroCount T : ℝ) := by + exact_mod_cast sum_rescaledMult_eq_zeroCount hT + have hkernel := congrArg Complex.re + (sum_testKernel_sq_eq_finsum_rescaledDiff hT eta) + change _ = (unweightedKernelSum eta T).re at hkernel + simp only [Nat.cast_mul] at hkernel + have hcard : + ((simpleRealPart (rescaledZerosFinset T) (rescaledMult T)).card : ℝ) = + (simpleOnLineCount T : ℝ) := by + exact_mod_cast card_simpleRealPart_rescaled_eq_simpleOnLineCount hT + rwa [hsum, hkernel, hcard] at hbound + +/-- **The finite-set lower bound transferred to distinct zeros.** -/ +@[zz_tag "lem_N_distinct_lower"] +theorem distinctZeroCount_lower {lam T : ℝ} {eta : ℝ → ℝ} + (hT : 1 < T) (hη : IsAdmissible lam eta) : + 3 / 2 * (zeroCount T : ℝ) - (unweightedKernelSum eta T).re / 2 + ≤ (distinctZeroCount T : ℝ) := by + have hbound := card_lower hη (isConjInvariant_rescaledZerosFinset hT) + have hsum : + ∑ z ∈ rescaledZerosFinset T, (rescaledMult T z : ℝ) = (zeroCount T : ℝ) := by + exact_mod_cast sum_rescaledMult_eq_zeroCount hT + have hkernel := congrArg Complex.re + (sum_testKernel_sq_eq_finsum_rescaledDiff hT eta) + change _ = (unweightedKernelSum eta T).re at hkernel + simp only [Nat.cast_mul] at hkernel + have hcard : ((rescaledZerosFinset T).card : ℝ) = (distinctZeroCount T : ℝ) := by + exact_mod_cast card_rescaledZerosFinset_eq_distinctZeroCount hT + rw [hsum, hkernel, hcard] at hbound + calc + 3 / 2 * (zeroCount T : ℝ) - (unweightedKernelSum eta T).re / 2 = + (3 / 2 : ℝ) * (zeroCount T : ℝ) - + (1 / 2 : ℝ) * (unweightedKernelSum eta T).re := by ring + _ ≤ (distinctZeroCount T : ℝ) := hbound + +end ZetaZeros diff --git a/LeanPool/projects.yml b/LeanPool/projects.yml index 932da2c6f0..ad9992615a 100644 --- a/LeanPool/projects.yml +++ b/LeanPool/projects.yml @@ -1,4 +1,64 @@ projects: + - slug: zeta-zeros + title: Simple zeros of the Riemann zeta function + summary: >- + Formalizes Lamzouri's Hilbert-space proof of lower bounds for simple + and distinct zeros of the Riemann zeta function. From explicit + Riemann--von Mangoldt and unconditional pair-correlation hypotheses, + the development proves that eventually more than 67.25% of nontrivial + zeros are simple and on the critical line and more than 83.625% are + distinct. It includes the finite-multiset Hilbert-space inequality, + zeta-zero multiplicity symmetries, an admissible cutoff construction, + and certified trigonometric estimates. + branch: analytic number theory + entry_module: LeanPool.ZetaZeros + authors: + - Kenny Lau + source: + title: >- + A new proof that more than 2/3 of the zeros of the Riemann zeta + function are simple and on the critical line + authors: + - Youness Lamzouri + arxiv: 2609.02882 + doi: 10.48550/arXiv.2609.02882 + url: https://github.com/AxiomMath/ZetaZeros + github_repo: AxiomMath/ZetaZeros + commit: 4bcaf70e544506c311d83a5a5b143a134b9fc5f7 + license: Apache-2.0 + status: verified + provenance: AI + main_declarations: + - ZetaZeros.simple_proportion_d4 + main_results: + - declaration: ZetaZeros.simple_proportion_d4 + informal: >- + Assuming the stated Riemann--von Mangoldt and unconditional + pair-correlation formulas, beyond some height more than 67.25% of + nontrivial zeta zeros are simple and lie on the critical line. + - declaration: ZetaZeros.distinct_proportion_d5 + informal: >- + Under the same two explicit analytic hypotheses, beyond some height + more than 83.625% of nontrivial zeta zeros are distinct. + - declaration: ZetaZeros.card_simpleRealPart_lower + informal: >- + Bounds from below the number of multiplicity-one real points in a + finite nonempty conjugation-invariant multiset using the squared + test-kernel sum of any admissible function. + - declaration: ZetaZeros.card_lower + informal: >- + Bounds from below the number of distinct points in a finite nonempty + conjugation-invariant multiset using the same Hilbert-space kernel + method. + tags: + - analytic-number-theory + - riemann-zeta-function + - critical-line + - simple-zeros + - pair-correlation + msc: + - "11M06" + - "11M26" - slug: common-neighbour-conjecture title: Common-neighbour counterexamples for Saxl graphs summary: >- From a33ea80d5eea7f25c7714517d83653d8ab638fed Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Thu, 3 Sep 2026 07:19:28 -0700 Subject: [PATCH 2/2] fix: expose the complete ZetaZeros module closure --- LeanPool/ZetaZeros.lean | 1 + 1 file changed, 1 insertion(+) diff --git a/LeanPool/ZetaZeros.lean b/LeanPool/ZetaZeros.lean index e0492196c8..067a7edbce 100644 --- a/LeanPool/ZetaZeros.lean +++ b/LeanPool/ZetaZeros.lean @@ -5,6 +5,7 @@ Authors: Axiom Math, Kenny Lau -/ import LeanPool.ZetaZeros.Main +import LeanPool.ZetaZeros.Hilbert.InnerReal /-! # Simple zeros of the Riemann zeta function