-
Notifications
You must be signed in to change notification settings - Fork 368
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Eisenstein series uniform convergence (#10377)
We show that the sum defining an Eisenstein Series converges locally uniformly. This is the basis for later proving that they are holomorphic (see #11013 ) and bounded at infinity (see #12456), which combine to show they are modular forms (see #12604). Co-authored-by: Chris Birkbeck <[email protected]> Co-authored-by: David Loeffler <[email protected]>
- Loading branch information
Showing
3 changed files
with
203 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
197 changes: 197 additions & 0 deletions
197
Mathlib/NumberTheory/ModularForms/EisensteinSeries/UniformConvergence.lean
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
/- | ||
Copyright (c) 2024 Chris Birkbeck. All rights reserved. | ||
Released under Apache 2.0 license as described in the file LICENSE. | ||
Authors: Chris Birkbeck, David Loeffler | ||
-/ | ||
|
||
import Mathlib.Analysis.Complex.UpperHalfPlane.Topology | ||
import Mathlib.Analysis.NormedSpace.FunctionSeries | ||
import Mathlib.Analysis.PSeries | ||
import Mathlib.Order.Interval.Finset.Box | ||
import Mathlib.NumberTheory.ModularForms.EisensteinSeries.Basic | ||
|
||
/-! | ||
# Uniform convergence of Eisenstein series | ||
We show that the sum of `eisSummand` converges locally uniformly on `ℍ` to the Eisenstein series | ||
of weight `k` and level `Γ(N)` with congruence condition `a : Fin 2 → ZMod N`. | ||
## Outline of argument | ||
The key lemma `r_mul_max_le` shows that, for `z ∈ ℍ` and `c, d ∈ ℤ` (not both zero), | ||
`|c z + d|` is bounded below by `r z * max (|c|, |d|)`, where `r z` is an explicit function of `z` | ||
(independent of `c, d`) satisfying `0 < r z < 1` for all `z`. | ||
We then show in `summable_one_div_rpow_max` that the sum of `max (|c|, |d|) ^ (-k)` over | ||
`(c, d) ∈ ℤ × ℤ` is convergent for `2 < k`. This is proved by decomposing `ℤ × ℤ` using the | ||
`Finset.box` lemmas. | ||
-/ | ||
|
||
noncomputable section | ||
|
||
open Complex UpperHalfPlane Set Finset | ||
|
||
open scoped BigOperators | ||
|
||
variable (z : ℍ) | ||
|
||
namespace EisensteinSeries | ||
|
||
lemma norm_eq_max_natAbs (x : Fin 2 → ℤ) : ‖x‖ = max (x 0).natAbs (x 1).natAbs := by | ||
rw [← coe_nnnorm, ← NNReal.coe_natCast, NNReal.coe_inj, Nat.cast_max] | ||
refine eq_of_forall_ge_iff fun c ↦ ?_ | ||
simp only [pi_nnnorm_le_iff, Fin.forall_fin_two, max_le_iff, NNReal.natCast_natAbs] | ||
|
||
section bounding_functions | ||
|
||
/-- Auxiliary function used for bounding Eisenstein series, defined as | ||
`z.im ^ 2 / (z.re ^ 2 + z.im ^ 2)`. -/ | ||
def r1 : ℝ := z.im ^ 2 / (z.re ^ 2 + z.im ^ 2) | ||
|
||
lemma r1_eq : r1 z = 1 / ((z.re / z.im) ^ 2 + 1) := by | ||
rw [div_pow, div_add_one (by positivity), one_div_div, r1] | ||
|
||
lemma r1_pos : 0 < r1 z := by | ||
dsimp only [r1] | ||
positivity | ||
|
||
/-- For `c, d ∈ ℝ` with `1 ≤ d ^ 2`, we have `r1 z ≤ |c * z + d| ^ 2`. -/ | ||
lemma r1_aux_bound (c : ℝ) {d : ℝ} (hd : 1 ≤ d ^ 2) : | ||
r1 z ≤ (c * z.re + d) ^ 2 + (c * z.im) ^ 2 := by | ||
have H1 : (c * z.re + d) ^ 2 + (c * z.im) ^ 2 = | ||
c ^ 2 * (z.re ^ 2 + z.im ^ 2) + d * 2 * c * z.re + d ^ 2 := by ring | ||
have H2 : (c ^ 2 * (z.re ^ 2 + z.im ^ 2) + d * 2 * c * z.re + d ^ 2) * (z.re ^ 2 + z.im ^ 2) | ||
- z.im ^ 2 = (c * (z.re ^ 2 + z.im ^ 2) + d * z.re) ^ 2 + (d ^ 2 - 1) * z.im ^ 2 := by ring | ||
rw [r1, H1, div_le_iff (by positivity), ← sub_nonneg, H2] | ||
exact add_nonneg (sq_nonneg _) (mul_nonneg (sub_nonneg.mpr hd) (sq_nonneg _)) | ||
|
||
/-- This function is used to give an upper bound on the summands in Eisenstein series; it is | ||
defined by `z ↦ min z.im √(z.im ^ 2 / (z.re ^ 2 + z.im ^ 2))`. -/ | ||
def r : ℝ := min z.im √(r1 z) | ||
|
||
lemma r_pos : 0 < r z := by | ||
simp only [r, lt_min_iff, im_pos, Real.sqrt_pos, r1_pos, and_self] | ||
|
||
lemma r_lower_bound_on_verticalStrip {A B : ℝ} (h : 0 < B) (hz : z ∈ verticalStrip A B) : | ||
r ⟨⟨A, B⟩, h⟩ ≤ r z := by | ||
apply min_le_min hz.2 | ||
rw [Real.sqrt_le_sqrt_iff (by apply (r1_pos z).le)] | ||
simp only [r1_eq, div_pow, one_div] | ||
rw [inv_le_inv (by positivity) (by positivity), add_le_add_iff_right] | ||
apply div_le_div (sq_nonneg _) _ (by positivity) (pow_le_pow_left h.le hz.2 2) | ||
simpa only [even_two.pow_abs] using pow_le_pow_left (abs_nonneg _) hz.1 2 | ||
|
||
lemma auxbound1 {c : ℝ} (d : ℝ) (hc : 1 ≤ c ^ 2) : r z ≤ Complex.abs (c * z + d) := by | ||
rcases z with ⟨z, hz⟩ | ||
have H1 : z.im ≤ √((c * z.re + d) ^ 2 + (c * z).im ^ 2) := by | ||
rw [Real.le_sqrt' hz, im_ofReal_mul, mul_pow] | ||
exact (le_mul_of_one_le_left (sq_nonneg _) hc).trans <| le_add_of_nonneg_left (sq_nonneg _) | ||
simpa only [r, abs_apply, normSq_apply, add_re, re_ofReal_mul, coe_re, ← pow_two, add_im, mul_im, | ||
coe_im, ofReal_im, zero_mul, add_zero, min_le_iff] using Or.inl H1 | ||
|
||
lemma auxbound2 (c : ℝ) {d : ℝ} (hd : 1 ≤ d ^ 2) : r z ≤ Complex.abs (c * z + d) := by | ||
have H1 : √(r1 z) ≤ √((c * z.re + d) ^ 2 + (c * z.im) ^ 2) := | ||
(Real.sqrt_le_sqrt_iff (by positivity)).mpr (r1_aux_bound _ _ hd) | ||
simpa only [r, abs_apply, normSq_apply, add_re, re_ofReal_mul, coe_re, ofReal_re, ← pow_two, | ||
add_im, im_ofReal_mul, coe_im, ofReal_im, add_zero, min_le_iff] using Or.inr H1 | ||
|
||
lemma div_max_sq_ge_one (x : Fin 2 → ℤ) (hx : x ≠ 0) : | ||
1 ≤ (x 0 / ‖x‖) ^ 2 ∨ 1 ≤ (x 1 / ‖x‖) ^ 2 := by | ||
refine (max_choice (x 0).natAbs (x 1).natAbs).imp (fun H0 ↦ ?_) (fun H1 ↦ ?_) | ||
· have : x 0 ≠ 0 := by | ||
rwa [← norm_ne_zero_iff, norm_eq_max_natAbs, H0, Nat.cast_ne_zero, Int.natAbs_ne_zero] at hx | ||
simp only [norm_eq_max_natAbs, H0, Int.cast_natAbs, Int.cast_abs, div_pow, _root_.sq_abs, ne_eq, | ||
OfNat.ofNat_ne_zero, not_false_eq_true, pow_eq_zero_iff, Int.cast_eq_zero, this, div_self, | ||
le_refl] | ||
· have : x 1 ≠ 0 := by | ||
rwa [← norm_ne_zero_iff, norm_eq_max_natAbs, H1, Nat.cast_ne_zero, Int.natAbs_ne_zero] at hx | ||
simp only [norm_eq_max_natAbs, H1, Int.cast_natAbs, Int.cast_abs, div_pow, _root_.sq_abs, ne_eq, | ||
OfNat.ofNat_ne_zero, not_false_eq_true, pow_eq_zero_iff, Int.cast_eq_zero, this, div_self, | ||
le_refl] | ||
|
||
lemma r_mul_max_le {x : Fin 2 → ℤ} (hx : x ≠ 0) : r z * ‖x‖ ≤ Complex.abs (x 0 * z + x 1) := by | ||
have hn0 : ‖x‖ ≠ 0 := by rwa [norm_ne_zero_iff] | ||
have h11 : x 0 * (z : ℂ) + x 1 = (x 0 / ‖x‖ * z + x 1 / ‖x‖) * ‖x‖ := by | ||
rw [div_mul_eq_mul_div, ← add_div, div_mul_cancel₀ _ (mod_cast hn0)] | ||
rw [norm_eq_max_natAbs, h11, map_mul, Complex.abs_ofReal, abs_norm, norm_eq_max_natAbs] | ||
gcongr | ||
· rcases div_max_sq_ge_one x hx with H1 | H2 | ||
· simpa only [norm_eq_max_natAbs, ofReal_div, ofReal_intCast] using auxbound1 z (x 1 / ‖x‖) H1 | ||
· simpa only [norm_eq_max_natAbs, ofReal_div, ofReal_intCast] using auxbound2 z (x 0 / ‖x‖) H2 | ||
|
||
/-- Upper bound for the summand `|c * z + d| ^ (-k)`, as a product of a function of `z` and a | ||
function of `c, d`. -/ | ||
lemma summand_bound {k : ℝ} (hk : 0 ≤ k) (x : Fin 2 → ℤ) : | ||
Complex.abs (x 0 * z + x 1) ^ (-k) ≤ (r z) ^ (-k) * ‖x‖ ^ (-k) := by | ||
by_cases hx : x = 0 | ||
· simp only [hx, Pi.zero_apply, Int.cast_zero, zero_mul, add_zero, ← norm_eq_abs, norm_zero] | ||
by_cases h : -k = 0 | ||
· rw [h, Real.rpow_zero, Real.rpow_zero, one_mul] | ||
· rw [Real.zero_rpow h, mul_zero] | ||
· rw [← Real.mul_rpow (r_pos _).le (norm_nonneg _)] | ||
exact Real.rpow_le_rpow_of_nonpos (mul_pos (r_pos _) (norm_pos_iff.mpr hx)) (r_mul_max_le z hx) | ||
(neg_nonpos.mpr hk) | ||
|
||
variable {z} in | ||
lemma summand_bound_of_mem_verticalStrip {k : ℝ} (hk : 0 ≤ k) (x : Fin 2 → ℤ) | ||
{A B : ℝ} (hB : 0 < B) (hz : z ∈ verticalStrip A B) : | ||
Complex.abs (x 0 * z + x 1) ^ (-k) ≤ r ⟨⟨A, B⟩, hB⟩ ^ (-k) * ‖x‖ ^ (-k) := by | ||
refine (summand_bound z hk x).trans (mul_le_mul_of_nonneg_right ?_ (by positivity)) | ||
exact Real.rpow_le_rpow_of_nonpos (r_pos _) (r_lower_bound_on_verticalStrip z hB hz) | ||
(neg_nonpos.mpr hk) | ||
|
||
end bounding_functions | ||
|
||
section summability | ||
|
||
/-- The function `ℤ ^ 2 → ℝ` given by `x ↦ ‖x‖ ^ (-k)` is summable if `2 < k`. We prove this by | ||
splitting into boxes using `Finset.box`. -/ | ||
lemma summable_one_div_norm_rpow {k : ℝ} (hk : 2 < k) : | ||
Summable fun (x : Fin 2 → ℤ) ↦ ‖x‖ ^ (-k) := by | ||
rw [← (finTwoArrowEquiv _).symm.summable_iff, summable_partition _ Int.existsUnique_mem_box] | ||
· simp only [finTwoArrowEquiv_symm_apply, Function.comp_def] | ||
refine ⟨fun n ↦ (hasSum_fintype (β := box (α := ℤ × ℤ) n) _).summable, ?_⟩ | ||
suffices Summable fun n : ℕ ↦ ∑' (_ : box (α := ℤ × ℤ) n), (n : ℝ) ^ (-k) by | ||
refine this.congr fun n ↦ tsum_congr fun p ↦ ?_ | ||
simp only [← Int.mem_box.mp p.2, Nat.cast_max, norm_eq_max_natAbs, Matrix.cons_val_zero, | ||
Matrix.cons_val_one, Matrix.head_cons] | ||
simp only [tsum_fintype, univ_eq_attach, sum_const, card_attach, nsmul_eq_mul] | ||
apply ((Real.summable_nat_rpow.mpr (by linarith : 1 - k < -1)).mul_left | ||
8).of_norm_bounded_eventually_nat | ||
filter_upwards [Filter.eventually_gt_atTop 0] with n hn | ||
rw [Int.card_box hn.ne', Real.norm_of_nonneg (by positivity), sub_eq_add_neg, | ||
Real.rpow_add (Nat.cast_pos.mpr hn), Real.rpow_one, Nat.cast_mul, Nat.cast_ofNat, mul_assoc] | ||
· exact fun n ↦ Real.rpow_nonneg (norm_nonneg _) _ | ||
|
||
/-- The sum defining the Eisenstein series (of weight `k` and level `Γ(N)` with congruence | ||
condition `a : Fin 2 → ZMod N`) converges locally uniformly on `ℍ`. -/ | ||
theorem eisensteinSeries_tendstoLocallyUniformly {k : ℤ} (hk : 3 ≤ k) {N : ℕ} (a : Fin 2 → ZMod N) : | ||
TendstoLocallyUniformly (fun (s : Finset (gammaSet N a)) ↦ (∑ x in s, eisSummand k x ·)) | ||
(eisensteinSeries a k ·) Filter.atTop := by | ||
have hk' : (2 : ℝ) < k := by norm_cast | ||
have p_sum : Summable fun x : gammaSet N a ↦ ‖x.val‖ ^ (-k) := | ||
mod_cast (summable_one_div_norm_rpow hk').subtype (gammaSet N a) | ||
simp only [tendstoLocallyUniformly_iff_forall_isCompact, eisensteinSeries] | ||
intro K hK | ||
obtain ⟨A, B, hB, HABK⟩ := subset_verticalStrip_of_isCompact hK | ||
refine (tendstoUniformlyOn_tsum (hu := p_sum.mul_left <| r ⟨⟨A, B⟩, hB⟩ ^ (-k : ℝ)) | ||
(fun p z hz ↦ ?_)).mono HABK | ||
simpa only [eisSummand, one_div, ← zpow_neg, norm_eq_abs, abs_zpow, ← Real.rpow_intCast, | ||
Int.cast_neg] using summand_bound_of_mem_verticalStrip (by positivity) p hB hz | ||
|
||
/-- Extend a function on `ℍ` arbitrarily to a function on all of `ℂ`. -/ | ||
local notation "↑ₕ" f => f ∘ (PartialHomeomorph.symm (openEmbedding_coe.toPartialHomeomorph _)) | ||
|
||
/-- Variant of `eisensteinSeries_tendstoLocallyUniformly` formulated with maps `ℂ → ℂ`, which is | ||
nice to have for holomorphicity later. -/ | ||
lemma eisensteinSeries_tendstoLocallyUniformlyOn {k : ℤ} {N : ℕ} (hk : 3 ≤ k) | ||
(a : Fin 2 → ZMod N) : TendstoLocallyUniformlyOn (fun (s : Finset (gammaSet N a )) ↦ | ||
↑ₕ(fun (z : ℍ) ↦ ∑ x in s, eisSummand k x z )) (↑ₕ(eisensteinSeries_SIF a k).toFun) | ||
Filter.atTop (UpperHalfPlane.coe '' ⊤) := by | ||
apply TendstoLocallyUniformlyOn.comp (s := ⊤) _ _ _ (PartialHomeomorph.continuousOn_symm _) | ||
· simp only [SlashInvariantForm.toFun_eq_coe, Set.top_eq_univ, tendstoLocallyUniformlyOn_univ] | ||
apply eisensteinSeries_tendstoLocallyUniformly hk | ||
· simp only [OpenEmbedding.toPartialHomeomorph_target, Set.top_eq_univ, mapsTo_range_iff, | ||
Set.mem_univ, forall_const] | ||
|
||
end summability |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters