Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/modular/arithgroup/congroup_gamma0.py
8820 views
1
r"""
2
Congruence Subgroup `\Gamma_0(N)`
3
"""
4
5
################################################################################
6
#
7
# Copyright (C) 2009, The Sage Group -- http://www.sagemath.org/
8
#
9
# Distributed under the terms of the GNU General Public License (GPL)
10
#
11
# The full text of the GPL is available at:
12
#
13
# http://www.gnu.org/licenses/
14
#
15
################################################################################
16
17
from congroup_gammaH import GammaH_class
18
from congroup_gamma1 import is_Gamma1
19
from sage.modular.modsym.p1list import lift_to_sl2z
20
from congroup_generic import CongruenceSubgroup
21
22
from sage.modular.cusps import Cusp
23
from sage.misc.cachefunc import cached_method
24
from sage.rings.all import (IntegerModRing, kronecker_symbol, ZZ)
25
from sage.misc.misc import prod
26
import sage.modular.modsym.p1list
27
import sage.rings.arith as arith
28
29
# Just for now until we make an SL_2 group type.
30
from sage.matrix.matrix_space import MatrixSpace
31
Mat2Z = MatrixSpace(ZZ,2)
32
33
34
def is_Gamma0(x):
35
"""
36
Return True if x is a congruence subgroup of type Gamma0.
37
38
EXAMPLES::
39
40
sage: from sage.modular.arithgroup.all import is_Gamma0
41
sage: is_Gamma0(SL2Z)
42
True
43
sage: is_Gamma0(Gamma0(13))
44
True
45
sage: is_Gamma0(Gamma1(6))
46
False
47
"""
48
return isinstance(x, Gamma0_class)
49
50
_gamma0_cache = {}
51
def Gamma0_constructor(N):
52
"""
53
Return the congruence subgroup Gamma0(N).
54
55
EXAMPLES::
56
57
sage: G = Gamma0(51) ; G # indirect doctest
58
Congruence Subgroup Gamma0(51)
59
sage: G == Gamma0(51)
60
True
61
sage: G is Gamma0(51)
62
True
63
"""
64
from all import SL2Z
65
if N == 1: return SL2Z
66
try:
67
return _gamma0_cache[N]
68
except KeyError:
69
_gamma0_cache[N] = Gamma0_class(N)
70
return _gamma0_cache[N]
71
72
class Gamma0_class(GammaH_class):
73
r"""
74
The congruence subgroup `\Gamma_0(N)`.
75
76
TESTS::
77
78
sage: Gamma0(11).dimension_cusp_forms(2)
79
1
80
sage: a = Gamma0(1).dimension_cusp_forms(2); a
81
0
82
sage: type(a)
83
<type 'sage.rings.integer.Integer'>
84
sage: Gamma0(5).dimension_cusp_forms(0)
85
0
86
sage: Gamma0(20).dimension_cusp_forms(1)
87
0
88
sage: Gamma0(20).dimension_cusp_forms(4)
89
6
90
91
sage: Gamma0(23).dimension_cusp_forms(2)
92
2
93
sage: Gamma0(1).dimension_cusp_forms(24)
94
2
95
sage: Gamma0(3).dimension_cusp_forms(3)
96
0
97
sage: Gamma0(11).dimension_cusp_forms(-1)
98
0
99
100
sage: Gamma0(22).dimension_new_cusp_forms()
101
0
102
sage: Gamma0(100).dimension_new_cusp_forms(2, 5)
103
5
104
105
Independently compute the dimension 5 above::
106
107
sage: m = ModularSymbols(100, 2,sign=1).cuspidal_subspace()
108
sage: m.new_subspace(5)
109
Modular Symbols subspace of dimension 5 of Modular Symbols space of dimension 18 for Gamma_0(100) of weight 2 with sign 1 over Rational Field
110
111
"""
112
113
114
def __init__(self, level):
115
r"""
116
The congruence subgroup `\Gamma_0(N)`.
117
118
EXAMPLES::
119
120
sage: G = Gamma0(11); G
121
Congruence Subgroup Gamma0(11)
122
sage: TestSuite(G).run()
123
sage: G is loads(dumps(G))
124
True
125
126
TESTS::
127
128
sage: g = Gamma0(5)([1,1,0,1])
129
sage: g in Gamma0(7)
130
True
131
sage: g = Gamma0(5)([1,0,5,1])
132
sage: g in Gamma0(7)
133
False
134
sage: g = Gamma0(2)([1,0,0,1])
135
sage: g in SL2Z
136
True
137
"""
138
CongruenceSubgroup.__init__(self, level)
139
140
# We *don't* call the GammaH init script, as this requires calculating
141
# generators for the units modulo N which is time-consuming; this will
142
# be done if needed by the _generators_for_H and _list_of_elements_in_H
143
# methods.
144
#
145
#GammaH_class.__init__(self, level, [int(x) for x in IntegerModRing(level).unit_gens()])
146
147
def _repr_(self):
148
"""
149
Return the string representation of self.
150
151
EXAMPLES::
152
153
sage: Gamma0(98)._repr_()
154
'Congruence Subgroup Gamma0(98)'
155
"""
156
return "Congruence Subgroup Gamma0(%s)"%self.level()
157
158
def __reduce__(self):
159
"""
160
Used for pickling self.
161
162
EXAMPLES::
163
164
sage: Gamma0(22).__reduce__()
165
(<function Gamma0_constructor at ...>, (22,))
166
"""
167
return Gamma0_constructor, (self.level(),)
168
169
def _latex_(self):
170
r"""
171
Return the \LaTeX representation of self.
172
173
EXAMPLES::
174
175
sage: Gamma0(20)._latex_()
176
'\\Gamma_0(20)'
177
sage: latex(Gamma0(20))
178
\Gamma_0(20)
179
"""
180
return "\\Gamma_0(%s)"%self.level()
181
182
@cached_method
183
def _generators_for_H(self):
184
"""
185
Return generators for the subgroup H of the units mod
186
self.level() that defines self.
187
188
EXAMPLES::
189
190
sage: Gamma0(15)._generators_for_H()
191
[11, 7]
192
"""
193
if self.level() in [1, 2]:
194
return []
195
return [ZZ(x) for x in IntegerModRing(self.level()).unit_gens()]
196
197
@cached_method
198
def _list_of_elements_in_H(self):
199
"""
200
Returns a sorted list of Python ints that are representatives
201
between 0 and N-1 of the elements of H.
202
203
EXAMPLES::
204
205
sage: G = Gamma0(11)
206
sage: G._list_of_elements_in_H()
207
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
208
209
sage: G = Gamma0(6)
210
sage: G._list_of_elements_in_H()
211
[1, 5]
212
213
sage: G = Gamma0(1)
214
sage: G._list_of_elements_in_H()
215
[1]
216
"""
217
N = self.level()
218
if N != 1:
219
gcd = arith.gcd
220
H = [ x for x in range(1, N) if gcd(x, N) == 1 ]
221
else:
222
H = [1]
223
224
return H
225
226
def divisor_subgroups(self):
227
r"""
228
Return the subgroups of SL2Z of the form Gamma0(M) that contain this subgroup,
229
i.e. those for M a divisor of N.
230
231
EXAMPLE::
232
233
sage: Gamma0(24).divisor_subgroups()
234
[Modular Group SL(2,Z),
235
Congruence Subgroup Gamma0(2),
236
Congruence Subgroup Gamma0(3),
237
Congruence Subgroup Gamma0(4),
238
Congruence Subgroup Gamma0(6),
239
Congruence Subgroup Gamma0(8),
240
Congruence Subgroup Gamma0(12),
241
Congruence Subgroup Gamma0(24)]
242
"""
243
return [Gamma0_constructor(M) for M in self.level().divisors()]
244
245
def is_even(self):
246
r"""
247
Return True precisely if this subgroup contains the matrix -1.
248
249
Since `\Gamma0(N)` always contains the matrix -1, this always
250
returns True.
251
252
EXAMPLES::
253
254
sage: Gamma0(12).is_even()
255
True
256
sage: SL2Z.is_even()
257
True
258
"""
259
return True
260
261
def is_subgroup(self, right):
262
"""
263
Return True if self is a subgroup of right.
264
265
EXAMPLES::
266
267
sage: G = Gamma0(20)
268
sage: G.is_subgroup(SL2Z)
269
True
270
sage: G.is_subgroup(Gamma0(4))
271
True
272
sage: G.is_subgroup(Gamma0(20))
273
True
274
sage: G.is_subgroup(Gamma0(7))
275
False
276
sage: G.is_subgroup(Gamma1(20))
277
False
278
sage: G.is_subgroup(GammaH(40, []))
279
False
280
sage: Gamma0(80).is_subgroup(GammaH(40, [31, 21, 17]))
281
True
282
sage: Gamma0(2).is_subgroup(Gamma1(2))
283
True
284
"""
285
if right.level() == 1:
286
return True
287
if is_Gamma0(right):
288
return self.level() % right.level() == 0
289
if is_Gamma1(right):
290
if right.level() >= 3:
291
return False
292
elif right.level() == 2:
293
return self.level() == 2
294
# case level 1 dealt with above
295
else:
296
return GammaH_class.is_subgroup(self, right)
297
298
def coset_reps(self):
299
r"""
300
Return representatives for the right cosets of this congruence
301
subgroup in `{\rm SL}_2(\ZZ)` as a generator object.
302
303
Use ``list(self.coset_reps())`` to obtain coset reps as a
304
list.
305
306
EXAMPLES::
307
308
sage: list(Gamma0(5).coset_reps())
309
[
310
[1 0] [ 0 -1] [1 0] [ 0 -1] [ 0 -1] [ 0 -1]
311
[0 1], [ 1 0], [1 1], [ 1 2], [ 1 3], [ 1 4]
312
]
313
sage: list(Gamma0(4).coset_reps())
314
[
315
[1 0] [ 0 -1] [1 0] [ 0 -1] [ 0 -1] [1 0]
316
[0 1], [ 1 0], [1 1], [ 1 2], [ 1 3], [2 1]
317
]
318
sage: list(Gamma0(1).coset_reps())
319
[
320
[1 0]
321
[0 1]
322
]
323
"""
324
from all import SL2Z
325
N = self.level()
326
if N == 1: # P1List isn't very happy working modulo 1
327
yield SL2Z([1,0,0,1])
328
else:
329
for z in sage.modular.modsym.p1list.P1List(N):
330
yield SL2Z(lift_to_sl2z(z[0], z[1], N))
331
332
@cached_method
333
def generators(self, algorithm="farey"):
334
r"""
335
Return generators for this congruence subgroup.
336
337
INPUT:
338
339
- ``algorithm`` (string): either ``farey`` (default) or
340
``todd-coxeter``.
341
342
If ``algorithm`` is set to ``"farey"``, then the generators will be
343
calculated using Farey symbols, which will always return a *minimal*
344
generating set. See :mod:`~sage.modular.arithgroup.farey_symbol` for
345
more information.
346
347
If ``algorithm`` is set to ``"todd-coxeter"``, a simpler algorithm
348
based on Todd-Coxeter enumeration will be used. This tends to return
349
far larger sets of generators.
350
351
EXAMPLE::
352
353
sage: Gamma0(3).generators()
354
[
355
[1 1] [-1 1]
356
[0 1], [-3 2]
357
]
358
sage: Gamma0(3).generators(algorithm="todd-coxeter")
359
[
360
[1 1] [-1 0] [ 1 -1] [1 0] [1 1] [-1 0] [ 1 0]
361
[0 1], [ 0 -1], [ 0 1], [3 1], [0 1], [ 3 -1], [-3 1]
362
]
363
sage: SL2Z.gens()
364
(
365
[ 0 -1] [1 1]
366
[ 1 0], [0 1]
367
)
368
"""
369
if self.level() == 1:
370
# we return a fixed set of generators for SL2Z, for historical
371
# reasons, which aren't the ones the Farey symbol code gives
372
return [ self([0,-1,1,0]), self([1,1,0,1]) ]
373
374
elif algorithm=="farey":
375
return self.farey_symbol().generators()
376
377
elif algorithm=="todd-coxeter":
378
from sage.modular.modsym.p1list import P1List
379
from congroup_pyx import generators_helper
380
level = self.level()
381
if level == 1: # P1List isn't very happy working mod 1
382
return [ self([0,-1,1,0]), self([1,1,0,1]) ]
383
gen_list = generators_helper(P1List(level), level, Mat2Z)
384
return [self(g, check=False) for g in gen_list]
385
386
else:
387
raise ValueError, "Unknown algorithm '%s' (should be either 'farey' or 'todd-coxeter')" % algorithm
388
389
def gamma_h_subgroups(self):
390
r"""
391
Return the subgroups of the form `\Gamma_H(N)` contained
392
in self, where `N` is the level of self.
393
394
EXAMPLES::
395
396
sage: G = Gamma0(11)
397
sage: G.gamma_h_subgroups()
398
[Congruence Subgroup Gamma0(11), Congruence Subgroup Gamma_H(11) with H generated by [4], Congruence Subgroup Gamma_H(11) with H generated by [10], Congruence Subgroup Gamma1(11)]
399
sage: G = Gamma0(12)
400
sage: G.gamma_h_subgroups()
401
[Congruence Subgroup Gamma0(12), Congruence Subgroup Gamma_H(12) with H generated by [7], Congruence Subgroup Gamma_H(12) with H generated by [11], Congruence Subgroup Gamma_H(12) with H generated by [5], Congruence Subgroup Gamma1(12)]
402
"""
403
from all import GammaH
404
N = self.level()
405
R = IntegerModRing(N)
406
return [GammaH(N, H) for H in R.multiplicative_subgroups()]
407
408
def _contains_sl2(self, a,b,c,d):
409
r"""
410
Test whether x is an element of this group.
411
412
EXAMPLES::
413
414
sage: G = Gamma0(12)
415
sage: [1, 0, 24, 1] in G
416
True
417
sage: matrix(ZZ, 2, [1, 1, -12, -11]) in G
418
True
419
sage: SL2Z([0,-1,1,0]) in G
420
False
421
sage: 1 in G
422
True
423
sage: -1 in G
424
True
425
sage: 2 in G
426
False
427
428
The _element_constructor_ method calls this method::
429
430
sage: G([1, 0, 23, 1]) # indirect doctest
431
Traceback (most recent call last):
432
...
433
TypeError: matrix [ 1 0]
434
[23 1] is not an element of Congruence Subgroup Gamma0(12)
435
"""
436
return (c % self.level() == 0)
437
438
def _find_cusps(self):
439
r"""
440
Return an ordered list of inequivalent cusps for self, i.e. a
441
set of representatives for the orbits of self on
442
`\mathbb{P}^1(\QQ)`. These are returned in a reduced
443
form; see self.reduce_cusp for the definition of reduced.
444
445
ALGORITHM:
446
Uses explicit formulae specific to `\Gamma_0(N)`: a reduced cusp on
447
`\Gamma_0(N)` is always of the form `a/d` where `d | N`, and `a_1/d
448
\sim a_2/d` if and only if `a_1 \cong a_2 \bmod {\rm gcd}(d,
449
N/d)`.
450
451
EXAMPLES::
452
453
sage: Gamma0(90)._find_cusps()
454
[0, 1/45, 1/30, 1/18, 1/15, 1/10, 1/9, 2/15, 1/6, 1/5, 1/3, 11/30, 1/2, 2/3, 5/6, Infinity]
455
sage: Gamma0(1).cusps()
456
[Infinity]
457
sage: Gamma0(180).cusps() == Gamma0(180).cusps(algorithm='modsym')
458
True
459
"""
460
N = self.level()
461
s = []
462
463
for d in arith.divisors(N):
464
w = arith.gcd(d, N//d)
465
if w == 1:
466
if d == 1:
467
s.append(Cusp(1,0))
468
elif d == N:
469
s.append(Cusp(0,1))
470
else:
471
s.append(Cusp(1,d))
472
else:
473
for a in xrange(1, w):
474
if arith.gcd(a, w) == 1:
475
while arith.gcd(a, d//w) != 1:
476
a += w
477
s.append(Cusp(a,d))
478
return sorted(s)
479
480
def ncusps(self):
481
r"""
482
Return the number of cusps of this subgroup `\Gamma_0(N)`.
483
484
EXAMPLES::
485
486
sage: [Gamma0(n).ncusps() for n in [1..19]]
487
[1, 2, 2, 3, 2, 4, 2, 4, 4, 4, 2, 6, 2, 4, 4, 6, 2, 8, 2]
488
sage: [Gamma0(n).ncusps() for n in prime_range(2,100)]
489
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
490
"""
491
n = self.level()
492
return sum([arith.euler_phi(arith.gcd(d,n//d)) for d in n.divisors()])
493
494
495
def nu2(self):
496
r"""
497
Return the number of elliptic points of order 2 for this congruence
498
subgroup `\Gamma_0(N)`. The number of these is given by a standard formula:
499
0 if `N` is divisible by 4 or any prime congruent to -1 mod 4, and
500
otherwise `2^d` where d is the number of odd primes dividing `N`.
501
502
EXAMPLE::
503
504
sage: Gamma0(2).nu2()
505
1
506
sage: Gamma0(4).nu2()
507
0
508
sage: Gamma0(21).nu2()
509
0
510
sage: Gamma0(1105).nu2()
511
8
512
sage: [Gamma0(n).nu2() for n in [1..19]]
513
[1, 1, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 2, 0, 0]
514
"""
515
n = self.level()
516
if n%4 == 0:
517
return ZZ(0)
518
return prod([ 1 + kronecker_symbol(-4, p) for p, _ in n.factor()])
519
520
def nu3(self):
521
r"""
522
Return the number of elliptic points of order 3 for this congruence
523
subgroup `\Gamma_0(N)`. The number of these is given by a standard formula:
524
0 if `N` is divisible by 9 or any prime congruent to -1 mod 3, and
525
otherwise `2^d` where d is the number of primes other than 3 dividing `N`.
526
527
EXAMPLE::
528
529
sage: Gamma0(2).nu3()
530
0
531
sage: Gamma0(3).nu3()
532
1
533
sage: Gamma0(9).nu3()
534
0
535
sage: Gamma0(7).nu3()
536
2
537
sage: Gamma0(21).nu3()
538
2
539
sage: Gamma0(1729).nu3()
540
8
541
"""
542
n = self.level()
543
if (n % 9 == 0):
544
return ZZ(0)
545
return prod([ 1 + kronecker_symbol(-3, p) for p, _ in n.factor()])
546
547
def index(self):
548
r"""
549
Return the index of self in the full modular group. This is given by
550
551
.. math::
552
553
N \prod_{\substack{p \mid N \\ \text{$p$ prime}}}\left(1 + \frac{1}{p}\right).
554
555
EXAMPLE::
556
sage: [Gamma0(n).index() for n in [1..19]]
557
[1, 3, 4, 6, 6, 12, 8, 12, 12, 18, 12, 24, 14, 24, 24, 24, 18, 36, 20]
558
sage: Gamma0(32041).index()
559
32220
560
"""
561
return prod([p**e + p**(e-1) for (p,e) in self.level().factor()])
562
563
564
565
566