Skip to content

Commit bbe8c9a

Browse files
committed
feat(NumberTheory/ModularForms/EisensteinSeries): Update gammaSet (#27840)
We redefine gammaSet to include vectors with non-trivial gcd. This is useful for giving the q-expansions of Eisenstein series at level one. Which can be seen here #27606 (these equivalences are used in `tsum_prod_eisSummand_eq_riemannZeta_eisensteinSeries` in the QExpansions file). Co-authored-by: Chris Birkbeck <c.birkbeck@uea.ac.uk>
1 parent 41c7044 commit bbe8c9a

File tree

3 files changed

+116
-19
lines changed

3 files changed

+116
-19
lines changed

Mathlib/NumberTheory/ModularForms/EisensteinSeries/Defs.lean

Lines changed: 103 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ Copyright (c) 2024 Chris Birkbeck. All rights reserved.
33
Released under Apache 2.0 license as described in the file LICENSE.
44
Authors: Chris Birkbeck, David Loeffler
55
-/
6+
import Mathlib.Algebra.EuclideanDomain.Int
67
import Mathlib.NumberTheory.ModularForms.SlashInvariantForms
7-
import Mathlib.NumberTheory.ModularForms.CongruenceSubgroups
8+
import Mathlib.RingTheory.EuclideanDomain
89

910
/-!
1011
# Eisenstein Series
@@ -25,52 +26,139 @@ import Mathlib.NumberTheory.ModularForms.CongruenceSubgroups
2526

2627
noncomputable section
2728

28-
open ModularForm UpperHalfPlane Complex Matrix CongruenceSubgroup
29+
open ModularForm UpperHalfPlane Complex Matrix CongruenceSubgroup Set
2930

3031
open scoped MatrixGroups
3132

3233
namespace EisensteinSeries
3334

34-
variable (N : ℕ) (a : Fin 2 → ZMod N)
35+
variable (N r : ℕ) (a : Fin 2 → ZMod N)
3536

3637
section gammaSet_def
3738

38-
/-- The set of pairs of coprime integers congruent to `a` mod `N`. -/
39-
def gammaSet := {v : Fin 2 → ℤ | (↑) ∘ v = a ∧ IsCoprime (v 0) (v 1)}
39+
/-- The set of pairs of integers congruent to `a` mod `N` and with `gcd` equal to `r`. -/
40+
def gammaSet := {v : Fin 2 → ℤ | (↑) ∘ v = a ∧ (v 0).gcd (v 1) = r}
4041

4142
open scoped Function in -- required for scoped `on` notation
42-
lemma pairwise_disjoint_gammaSet : Pairwise (Disjoint on gammaSet N) := by
43+
lemma pairwise_disjoint_gammaSet : Pairwise (Disjoint on gammaSet N r) := by
4344
refine fun u v huv ↦ ?_
4445
contrapose! huv
4546
obtain ⟨f, hf⟩ := Set.not_disjoint_iff.mp huv
4647
exact hf.1.1.symm.trans hf.2.1
4748

4849
/-- For level `N = 1`, the gamma sets are all equal. -/
49-
lemma gammaSet_one_eq (a a' : Fin 2 → ZMod 1) : gammaSet 1 a = gammaSet 1 a' :=
50+
lemma gammaSet_one_const (a a' : Fin 2 → ZMod 1) : gammaSet 1 r a = gammaSet 1 r a' :=
5051
congr_arg _ (Subsingleton.elim _ _)
5152

53+
/-- For level `N = 1`, the gamma sets simplify to only a `gcd` condition. -/
54+
lemma gammaSet_one_eq (a : Fin 2 → ZMod 1) :
55+
gammaSet 1 r a = {v : Fin 2 → ℤ | (v 0).gcd (v 1) = r} := by
56+
simp [gammaSet, Subsingleton.eq_zero]
57+
58+
lemma gammaSet_one_mem_iff (v : Fin 2 → ℤ) : v ∈ gammaSet 1 r 0 ↔ (v 0).gcd (v 1) = r := by
59+
simp [gammaSet, Subsingleton.eq_zero]
60+
5261
/-- For level `N = 1`, the gamma sets are all equivalent; this is the equivalence. -/
53-
def gammaSet_one_equiv (a a' : Fin 2 → ZMod 1) : gammaSet 1 a ≃ gammaSet 1 a' :=
54-
Equiv.setCongr (gammaSet_one_eq a a')
62+
def gammaSet_one_equiv (a a' : Fin 2 → ZMod 1) : gammaSet 1 r a ≃ gammaSet 1 r a' :=
63+
Equiv.setCongr (gammaSet_one_const r a a')
64+
65+
/-- The map from `Fin 2 → ℤ` sending `![a,b]` to `a.gcd b`. -/
66+
abbrev finGcdMap (v : Fin 2 → ℤ) : ℕ := (v 0).gcd (v 1)
67+
68+
lemma finGcdMap_div {r : ℕ} [NeZero r] (v : Fin 2 → ℤ) (hv : finGcdMap v = r) :
69+
IsCoprime ((v / r) 0 ) ((v / r) 1) := by
70+
rw [← hv]
71+
apply isCoprime_div_gcd_div_gcd_of_gcd_ne_zero
72+
have := NeZero.ne r
73+
aesop
74+
75+
lemma finGcdMap_smul {r : ℕ} (a : ℤ) {v : Fin 2 → ℤ} (hv : finGcdMap v = r) :
76+
finGcdMap (a • v) = a.natAbs * r := by
77+
simp [finGcdMap, Int.gcd_mul_left, hv]
78+
79+
/-- An abbreviation of the map which divides a integer vector by an integer. -/
80+
abbrev divIntMap (r : ℤ) {m : ℕ} (v : Fin m → ℤ) : Fin m → ℤ := v / r
81+
82+
lemma mem_gammaSet_one (v : Fin 2 → ℤ) : v ∈ gammaSet 1 1 0 ↔ IsCoprime (v 0) (v 1) := by
83+
rw [gammaSet_one_mem_iff, Int.isCoprime_iff_gcd_eq_one]
84+
85+
lemma gammaSet_div_gcd {r : ℕ} {v : Fin 2 → ℤ} (hv : v ∈ (gammaSet 1 r 0)) (i : Fin 2) :
86+
(r : ℤ) ∣ v i := by
87+
fin_cases i <;> simp [← hv.2, Int.gcd_dvd_left, Int.gcd_dvd_right]
88+
89+
lemma gammaSet_div_gcd_to_gammaSet10_bijection (r : ℕ) [NeZero r] :
90+
Set.BijOn (divIntMap r) (gammaSet 1 r 0) (gammaSet 1 1 0) := by
91+
refine ⟨?_, ?_, ?_⟩
92+
· intro x hx
93+
simp only [divIntMap, mem_gammaSet_one] at *
94+
exact finGcdMap_div _ hx.2
95+
· intro x hx v hv hv2
96+
ext i
97+
exact (Int.ediv_left_inj (gammaSet_div_gcd hx i) (gammaSet_div_gcd hv i)).mp
98+
(congr_fun hv2 i)
99+
· intro x hx
100+
use r • x
101+
simp only [nsmul_eq_mul, divIntMap, Int.cast_natCast]
102+
constructor
103+
· rw [mem_gammaSet_one, Int.isCoprime_iff_gcd_eq_one] at hx
104+
exact ⟨Subsingleton.eq_zero _, by simp [Int.gcd_mul_left, hx]⟩
105+
· ext i
106+
simp_all [NeZero.ne r]
107+
108+
lemma gammaSet_eq_gcd_mul_divIntMap {r : ℕ} {v : Fin 2 → ℤ} (hv : v ∈ gammaSet 1 r 0) :
109+
v = r • (divIntMap r v) := by
110+
by_cases hr : r = 0
111+
· have hv := hv.2
112+
simp only [hr, Fin.isValue, Int.gcd_eq_zero_iff, CharP.cast_eq_zero, zero_smul] at *
113+
ext i
114+
fin_cases i <;> simp [hv]
115+
· ext i
116+
simp_all [Pi.smul_apply, divIntMap, ← Int.mul_ediv_assoc _ (gammaSet_div_gcd hv i)]
117+
118+
/-- The equivalence between `gammaSet 1 r 0` and `gammaSet 1 1 0` for non-zero `r`. -/
119+
def gammaSetDivGcdEquiv (r : ℕ) [NeZero r] : gammaSet 1 r 0 ≃ gammaSet 1 1 0 :=
120+
Set.BijOn.equiv _ (gammaSet_div_gcd_to_gammaSet10_bijection r)
121+
122+
/-- The equivalence between `(Fin 2 → ℤ)` and `Σ n : ℕ, gammaSet 1 n 0)` . -/
123+
def gammaSetDivGcdSigmaEquiv : (Fin 2 → ℤ) ≃ (Σ r : ℕ, gammaSet 1 r 0) := by
124+
apply (Equiv.sigmaFiberEquiv finGcdMap).symm.trans
125+
refine Equiv.sigmaCongrRight fun b => ?_
126+
apply Equiv.setCongr
127+
rw [gammaSet_one_eq]
128+
rfl
129+
130+
@[simp]
131+
lemma gammaSetDivGcdSigmaEquiv_symm_eq (v : Σ r : ℕ, gammaSet 1 r 0) :
132+
(gammaSetDivGcdSigmaEquiv.symm v) = v.2 := rfl
55133

56134
end gammaSet_def
57135

58-
variable {N a}
136+
variable {N a r} [NeZero r]
59137

60138
section gamma_action
61139

140+
/-- Right-multiplying a vector by a matrix in `SL(2, ℤ)` doesn't change its gcd. -/
141+
lemma vecMulSL_gcd {v : Fin 2 → ℤ} (hab : finGcdMap v = r) (A : SL(2, ℤ)) :
142+
finGcdMap (v ᵥ* A.1) = r := by
143+
have hvr : v = r • (v / r) := by
144+
ext i
145+
refine Eq.symm (Int.mul_ediv_cancel' ?_)
146+
fin_cases i <;> simp [← hab, Int.gcd_dvd_left, Int.gcd_dvd_right]
147+
rw [hvr, smul_vecMul]
148+
simpa using finGcdMap_smul r (Int.isCoprime_iff_gcd_eq_one.mp ((finGcdMap_div v hab).vecMulSL A))
149+
62150
/-- Right-multiplying by `γ ∈ SL(2, ℤ)` sends `gammaSet N a` to `gammaSet N (a ᵥ* γ)`. -/
63-
lemma vecMul_SL2_mem_gammaSet {v : Fin 2 → ℤ} (hv : v ∈ gammaSet N a) (γ : SL(2, ℤ)) :
64-
v ᵥ* γ ∈ gammaSet N (a ᵥ* γ) := by
65-
refine ⟨?_, hv.2.vecMulSL γ⟩
151+
lemma vecMul_SL2_mem_gammaSet {v : Fin 2 → ℤ} (hv : v ∈ gammaSet N r a)
152+
(γ : SL(2, ℤ)) : v ᵥ* γ ∈ gammaSet N r (a ᵥ* γ) := by
153+
refine ⟨?_, vecMulSL_gcd hv.2 γ⟩
66154
have := RingHom.map_vecMul (m := Fin 2) (n := Fin 2) (Int.castRingHom (ZMod N)) γ v
67155
simp only [eq_intCast, Int.coe_castRingHom] at this
68156
simp_rw [Function.comp_def, this, hv.1]
69157
simp
70158

71159
variable (a) in
72160
/-- The bijection between `GammaSets` given by multiplying by an element of `SL(2, ℤ)`. -/
73-
def gammaSetEquiv (γ : SL(2, ℤ)) : gammaSet N a ≃ gammaSet N (a ᵥ* γ) where
161+
def gammaSetEquiv (γ : SL(2, ℤ)) : gammaSet N r a ≃ gammaSet N r (a ᵥ* γ) where
74162
toFun v := ⟨v.1 ᵥ* γ, vecMul_SL2_mem_gammaSet v.2 γ⟩
75163
invFun v := ⟨v.1 ᵥ* ↑(γ⁻¹), by
76164
have := vecMul_SL2_mem_gammaSet v.2 γ⁻¹
@@ -106,7 +194,7 @@ end eisSummand
106194
variable (a)
107195

108196
/-- An Eisenstein series of weight `k` and level `Γ(N)`, with congruence condition `a`. -/
109-
def _root_.eisensteinSeries (k : ℤ) (z : ℍ) : ℂ := ∑' x : gammaSet N a, eisSummand k x z
197+
def _root_.eisensteinSeries (k : ℤ) (z : ℍ) : ℂ := ∑' x : gammaSet N 1 a, eisSummand k x z
110198

111199
lemma eisensteinSeries_slash_apply (k : ℤ) (γ : SL(2, ℤ)) :
112200
eisensteinSeries a k ∣[k] γ = eisensteinSeries (a ᵥ* γ) k := by

Mathlib/NumberTheory/ModularForms/EisensteinSeries/UniformConvergence.lean

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ namespace EisensteinSeries
3838
/-- The sum defining the Eisenstein series (of weight `k` and level `Γ(N)` with congruence
3939
condition `a : Fin 2 → ZMod N`) converges locally uniformly on `ℍ`. -/
4040
theorem eisensteinSeries_tendstoLocallyUniformly {k : ℤ} (hk : 3 ≤ k) {N : ℕ} (a : Fin 2 → ZMod N) :
41-
TendstoLocallyUniformly (fun (s : Finset (gammaSet N a)) ↦ (∑ x ∈ s, eisSummand k x ·))
41+
TendstoLocallyUniformly (fun (s : Finset (gammaSet N 1 a)) ↦ (∑ x ∈ s, eisSummand k x ·))
4242
(eisensteinSeries a k ·) Filter.atTop := by
4343
have hk' : (2 : ℝ) < k := by norm_cast
44-
have p_sum : Summable fun x : gammaSet N a ↦ ‖x.val‖ ^ (-k) :=
45-
mod_cast (summable_one_div_norm_rpow hk').subtype (gammaSet N a)
44+
have p_sum : Summable fun x : gammaSet N 1 a ↦ ‖x.val‖ ^ (-k) :=
45+
mod_cast (summable_one_div_norm_rpow hk').subtype (gammaSet N 1 a)
4646
simp only [tendstoLocallyUniformly_iff_forall_isCompact, eisensteinSeries]
4747
intro K hK
4848
obtain ⟨A, B, hB, HABK⟩ := subset_verticalStrip_of_isCompact hK
@@ -54,7 +54,7 @@ theorem eisensteinSeries_tendstoLocallyUniformly {k : ℤ} (hk : 3 ≤ k) {N :
5454
/-- Variant of `eisensteinSeries_tendstoLocallyUniformly` formulated with maps `ℂ → ℂ`, which is
5555
nice to have for holomorphicity later. -/
5656
lemma eisensteinSeries_tendstoLocallyUniformlyOn {k : ℤ} {N : ℕ} (hk : 3 ≤ k)
57-
(a : Fin 2 → ZMod N) : TendstoLocallyUniformlyOn (fun (s : Finset (gammaSet N a)) ↦
57+
(a : Fin 2 → ZMod N) : TendstoLocallyUniformlyOn (fun (s : Finset (gammaSet N 1 a)) ↦
5858
↑ₕ(fun (z : ℍ) ↦ ∑ x ∈ s, eisSummand k x z)) (↑ₕ(eisensteinSeries_SIF a k).toFun)
5959
Filter.atTop {z : ℂ | 0 < z.im} := by
6060
rw [← Subtype.coe_image_univ {z : ℂ | 0 < z.im}]

Mathlib/RingTheory/EuclideanDomain.lean

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ theorem isCoprime_div_gcd_div_gcd (hq : q ≠ 0) :
5151
(EuclideanDomain.mul_div_cancel' (gcd_ne_zero_of_right hq) <| gcd_dvd_right _ _).symm <|
5252
gcd_ne_zero_of_right hq
5353

54+
/-- This is a version of `isCoprime_div_gcd_div_gcd` which replaces the `q ≠ 0` assumption with
55+
`gcd p q ≠ 0`. -/
56+
theorem isCoprime_div_gcd_div_gcd_of_gcd_ne_zero (hpq : GCDMonoid.gcd p q ≠ 0) :
57+
IsCoprime (p / GCDMonoid.gcd p q) (q / GCDMonoid.gcd p q) :=
58+
(gcd_isUnit_iff _ _).1 <|
59+
isUnit_gcd_of_eq_mul_gcd
60+
(EuclideanDomain.mul_div_cancel' (hpq) <| gcd_dvd_left _ _).symm
61+
(EuclideanDomain.mul_div_cancel' (hpq) <| gcd_dvd_right _ _).symm <| hpq
62+
5463
end GCDMonoid
5564

5665
namespace EuclideanDomain

0 commit comments

Comments
 (0)