Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/modular/arithgroup/arithgroup_generic.py
4057 views
1
r"""
2
Arithmetic subgroups (finite index subgroups of `{\rm SL}_2(\ZZ)`)
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
18
import sage.groups.group as group
19
from sage.rings.all import ZZ
20
import sage.rings.arith as arith
21
from sage.misc.cachefunc import cached_method
22
from copy import copy # for making copies of lists of cusps
23
from sage.modular.modsym.p1list import lift_to_sl2z
24
from sage.modular.cusps import Cusp
25
26
def is_ArithmeticSubgroup(x):
27
r"""
28
Return True if x is of type ArithmeticSubgroup.
29
30
EXAMPLE::
31
32
sage: from sage.modular.arithgroup.all import is_ArithmeticSubgroup
33
sage: is_ArithmeticSubgroup(GL(2, GF(7)))
34
False
35
sage: is_ArithmeticSubgroup(Gamma0(4))
36
True
37
"""
38
39
return isinstance(x, ArithmeticSubgroup)
40
41
42
class ArithmeticSubgroup(group.Group):
43
r"""
44
Base class for arithmetic subgroups of `{\rm SL}_2(\ZZ)`. Not
45
intended to be used directly, but still includes quite a few
46
general-purpose routines which compute data about an arithmetic subgroup
47
assuming that it has a working element testing routine.
48
"""
49
50
def __init__(self):
51
r"""
52
Standard init routine.
53
54
EXAMPLE::
55
56
sage: G = Gamma1(7)
57
sage: G.category() # indirect doctest
58
Category of groups
59
"""
60
group.Group.__init__(self)
61
62
def _repr_(self):
63
r"""
64
Return the string representation of self.
65
66
NOTE: This function should be overridden by all subclasses.
67
68
EXAMPLES::
69
70
sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup()._repr_()
71
'Generic arithmetic subgroup of SL2Z'
72
"""
73
return "Generic arithmetic subgroup of SL2Z"
74
75
def __reduce__(self):
76
r"""
77
Used for pickling self.
78
79
NOTE: This function should be overridden by all subclasses.
80
81
EXAMPLES::
82
83
sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().__reduce__()
84
Traceback (most recent call last):
85
...
86
NotImplementedError: all subclasses must define a __reduce__ method
87
"""
88
raise NotImplementedError, "all subclasses must define a __reduce__ method"
89
90
def __hash__(self):
91
r"""
92
Return a hash of self.
93
94
EXAMPLES::
95
96
sage: Gamma0(11).__hash__()
97
-545929996 # 32-bit
98
466678398374495476 # 64-bit
99
sage: Gamma1(11).__hash__()
100
-830809815 # 32-bit
101
4909638266971150633 # 64-bit
102
"""
103
return hash(str(self))
104
105
def coset_reps(self, G=None):
106
r"""
107
Return right coset representatives for self \\ G, where G is another
108
arithmetic subgroup that contains self. If G = None, default to G =
109
SL2Z.
110
111
For generic arithmetic subgroups G this is carried out by Todd-Coxeter
112
enumeration; here G is treated as a black box, implementing nothing but
113
membership testing.
114
115
EXAMPLES::
116
117
sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().coset_reps()
118
Traceback (most recent call last):
119
...
120
NotImplementedError
121
sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.coset_reps(Gamma0(3))
122
[
123
[1 0] [ 0 -1] [ 0 -1] [ 0 -1]
124
[0 1], [ 1 0], [ 1 1], [ 1 2]
125
]
126
"""
127
return self.todd_coxeter(G)[0]
128
129
@cached_method
130
def todd_coxeter(self, G=None, on_right=True):
131
r"""
132
Compute coset representatives for self \\ G and action of standard
133
generators on them via Todd-Coxeter enumeration.
134
135
If ``G`` is ``None``, default to ``SL2Z``. The method also computes
136
generators of the subgroup at same time.
137
138
INPUT:
139
140
- ``G`` - intermediate subgroup (currently not implemented if diffferent
141
from SL(2,Z))
142
143
- ``on_right`` - boolean (default: True) - if True return right coset
144
enumeration, if False return left one.
145
146
This is *extremely* slow in general.
147
148
OUTPUT:
149
150
- a list of coset representatives
151
152
- a list of generators for the group
153
154
- ``l`` - list of integers that correspond to the action of the
155
standard parabolic element [[1,1],[0,1]] of `SL(2,\ZZ)` on the cosets
156
of self.
157
158
- ``s`` - list of integers that correspond to the action of the standard
159
element of order `2` [[0,-1],[1,0]] on the cosets of self.
160
161
EXAMPLES::
162
163
sage: L = SL2Z([1,1,0,1])
164
sage: S = SL2Z([0,-1,1,0])
165
166
sage: G = Gamma(2)
167
sage: reps, gens, l, s = G.todd_coxeter()
168
sage: len(reps) == G.index()
169
True
170
sage: all(reps[i] * L * ~reps[l[i]] in G for i in xrange(6))
171
True
172
sage: all(reps[i] * S * ~reps[s[i]] in G for i in xrange(6))
173
True
174
175
sage: G = Gamma0(7)
176
sage: reps, gens, l, s = G.todd_coxeter()
177
sage: len(reps) == G.index()
178
True
179
sage: all(reps[i] * L * ~reps[l[i]] in G for i in xrange(8))
180
True
181
sage: all(reps[i] * S * ~reps[s[i]] in G for i in xrange(8))
182
True
183
184
sage: G = Gamma1(3)
185
sage: reps, gens, l, s = G.todd_coxeter(on_right=False)
186
sage: len(reps) == G.index()
187
True
188
sage: all(~reps[l[i]] * L * reps[i] in G for i in xrange(8))
189
True
190
sage: all(~reps[s[i]] * S * reps[i] in G for i in xrange(8))
191
True
192
193
sage: G = Gamma0(5)
194
sage: reps, gens, l, s = G.todd_coxeter(on_right=False)
195
sage: len(reps) == G.index()
196
True
197
sage: all(~reps[l[i]] * L * reps[i] in G for i in xrange(6))
198
True
199
sage: all(~reps[s[i]] * S * reps[i] in G for i in xrange(6))
200
True
201
"""
202
from all import SL2Z
203
204
if G is None:
205
G = SL2Z
206
if G != SL2Z:
207
raise NotImplementedError, "Don't know how to compute coset reps for subgroups yet"
208
209
id = SL2Z([1,0,0,1])
210
l = SL2Z([1,1,0,1])
211
s = SL2Z([0,-1,1,0])
212
213
reps = [id] # coset representatives
214
reps_inv = {id:0} # coset representatives index
215
216
l_wait_back = [id] # rep with no incoming s_edge
217
s_wait_back = [id] # rep with no incoming l_edge
218
l_wait = [id] # rep with no outgoing l_edge
219
s_wait = [id] # rep with no outgoing s_edge
220
221
l_edges = [None] # edges for l
222
s_edges = [None] # edges for s
223
224
gens = []
225
226
while l_wait or s_wait:
227
if l_wait:
228
x = l_wait.pop(0)
229
y = x
230
not_end = True
231
while not_end:
232
if on_right:
233
y = y*l
234
else:
235
y = l*y
236
for i in xrange(len(l_wait_back)):
237
v = l_wait_back[i]
238
if on_right:
239
yy = y*~v
240
else:
241
yy = ~v*y
242
if yy in self:
243
l_edges[reps_inv[x]] = reps_inv[v]
244
del l_wait_back[i]
245
if yy != id:
246
gens.append(self(yy))
247
not_end = False
248
break
249
else:
250
reps_inv[y] = len(reps)
251
l_edges[reps_inv[x]] = len(reps)
252
reps.append(y)
253
l_edges.append(None)
254
s_edges.append(None)
255
s_wait_back.append(y)
256
s_wait.append(y)
257
x = y
258
259
if s_wait:
260
x = s_wait.pop(0)
261
y = x
262
not_end = True
263
while not_end:
264
if on_right:
265
y = y*s
266
else:
267
y = s*y
268
for i in xrange(len(s_wait_back)):
269
v = s_wait_back[i]
270
if on_right:
271
yy = y*~v
272
else:
273
yy = ~v*y
274
if yy in self:
275
s_edges[reps_inv[x]] = reps_inv[v]
276
del s_wait_back[i]
277
if yy != id:
278
gens.append(self(yy))
279
not_end = False
280
break
281
else:
282
reps_inv[y] = len(reps)
283
s_edges[reps_inv[x]] = len(reps)
284
reps.append(y)
285
l_edges.append(None)
286
s_edges.append(None)
287
l_wait_back.append(y)
288
l_wait.append(y)
289
x = y
290
291
return reps, gens, l_edges, s_edges
292
293
def nu2(self):
294
r"""
295
Return the number of orbits of elliptic points of order 2 for this
296
arithmetic subgroup.
297
298
EXAMPLES::
299
300
sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().nu2()
301
Traceback (most recent call last):
302
...
303
NotImplementedError
304
sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.nu2(Gamma0(1105)) == 8
305
True
306
"""
307
308
# Subgroups not containing -1 have no elliptic points of order 2.
309
310
if not self.is_even():
311
return 0
312
313
# Cheap trick: if self is a subgroup of something with no elliptic points,
314
# then self has no elliptic points either.
315
316
from all import Gamma0, is_CongruenceSubgroup
317
if is_CongruenceSubgroup(self):
318
if self.is_subgroup(Gamma0(self.level())) and Gamma0(self.level()).nu2() == 0:
319
return 0
320
321
# Otherwise, the number of elliptic points is the number of g in self \
322
# SL2Z such that the stabiliser of g * i in self is not trivial. (Note
323
# that the points g*i for g in the coset reps are not distinct, but it
324
# still works, since the failure of these points to be distinct happens
325
# precisely when the preimages are not elliptic.)
326
327
from all import SL2Z
328
count = 0
329
for g in self.coset_reps():
330
if g * SL2Z([0,1,-1,0]) * (~g) in self:
331
count += 1
332
return count
333
334
def nu3(self):
335
r"""
336
Return the number of orbits of elliptic points of order 3 for this
337
arithmetic subgroup.
338
339
EXAMPLES::
340
341
sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().nu3()
342
Traceback (most recent call last):
343
...
344
NotImplementedError
345
sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.nu3(Gamma0(1729)) == 8
346
True
347
348
We test that a bug in handling of subgroups not containing -1 is fixed: ::
349
350
sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.nu3(GammaH(7, [2]))
351
2
352
"""
353
354
# Cheap trick: if self is a subgroup of something with no elliptic points,
355
# then self has no elliptic points either.
356
357
from all import Gamma0, is_CongruenceSubgroup
358
if is_CongruenceSubgroup(self):
359
if self.is_subgroup(Gamma0(self.level())) and Gamma0(self.level()).nu3() == 0:
360
return 0
361
362
from all import SL2Z
363
count = 0
364
for g in self.coset_reps():
365
if g * SL2Z([0,1,-1,-1]) * (~g) in self:
366
count += 1
367
368
if self.is_even():
369
return count
370
else:
371
return count/2
372
373
def __cmp__(self, other):
374
r"""
375
Compare self to other.
376
377
NOTE: This function must be overridden by all subclasses.
378
379
EXAMPLES::
380
381
sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().__cmp__(ZZ)
382
Traceback (most recent call last):
383
...
384
NotImplementedError
385
"""
386
raise NotImplementedError
387
388
def is_abelian(self):
389
r"""
390
Return True if this arithmetic subgroup is abelian.
391
392
Since arithmetic subgroups are always nonabelian, this always
393
returns False.
394
395
EXAMPLES::
396
397
sage: SL2Z.is_abelian()
398
False
399
sage: Gamma0(3).is_abelian()
400
False
401
sage: Gamma1(12).is_abelian()
402
False
403
sage: GammaH(4, [3]).is_abelian()
404
False
405
"""
406
return False
407
408
def is_finite(self):
409
r"""
410
Return True if this arithmetic subgroup is finite.
411
412
Since arithmetic subgroups are always infinite, this always
413
returns False.
414
415
EXAMPLES::
416
417
sage: SL2Z.is_finite()
418
False
419
sage: Gamma0(3).is_finite()
420
False
421
sage: Gamma1(12).is_finite()
422
False
423
sage: GammaH(4, [3]).is_finite()
424
False
425
"""
426
return False
427
428
def is_subgroup(self, right):
429
r"""
430
Return True if self is a subgroup of right, and False otherwise. For
431
generic arithmetic subgroups this is done by the absurdly slow
432
algorithm of checking all of the generators of self to see if they are
433
in right.
434
435
EXAMPLES::
436
437
sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().is_subgroup(SL2Z)
438
Traceback (most recent call last):
439
...
440
NotImplementedError
441
sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.is_subgroup(Gamma1(18), Gamma0(6))
442
True
443
"""
444
# ridiculously slow generic algorithm
445
446
w = self.gens()
447
for g in w:
448
if not (g in right):
449
return False
450
return True
451
452
def is_normal(self):
453
r"""
454
Return True precisely if this subgroup is a normal subgroup of SL2Z.
455
456
EXAMPLES::
457
458
sage: Gamma(3).is_normal()
459
True
460
sage: Gamma1(3).is_normal()
461
False
462
"""
463
from all import SL2Z
464
for x in self.gens():
465
for y in SL2Z.gens():
466
if y*SL2Z(x)*(~y) not in self:
467
return False
468
return True
469
470
def is_odd(self):
471
r"""
472
Return True precisely if this subgroup does not contain the
473
matrix -1.
474
475
EXAMPLES::
476
477
sage: SL2Z.is_odd()
478
False
479
sage: Gamma0(20).is_odd()
480
False
481
sage: Gamma1(5).is_odd()
482
True
483
sage: GammaH(11, [3]).is_odd()
484
True
485
"""
486
return not self.is_even()
487
488
def is_even(self):
489
r"""
490
Return True precisely if this subgroup contains the matrix -1.
491
492
EXAMPLES::
493
494
sage: SL2Z.is_even()
495
True
496
sage: Gamma0(20).is_even()
497
True
498
sage: Gamma1(5).is_even()
499
False
500
sage: GammaH(11, [3]).is_even()
501
False
502
"""
503
return [-1, 0, 0, -1] in self
504
505
def to_even_subgroup(self):
506
r"""
507
Return the smallest even subgroup of `SL(2, \ZZ)` containing self.
508
509
EXAMPLE::
510
511
sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().to_even_subgroup()
512
Traceback (most recent call last):
513
...
514
NotImplementedError
515
"""
516
if self.is_even():
517
return self
518
else:
519
raise NotImplementedError
520
521
def order(self):
522
r"""
523
Return the number of elements in this arithmetic subgroup.
524
525
Since arithmetic subgroups are always infinite, this always returns
526
infinity.
527
528
EXAMPLES::
529
530
sage: SL2Z.order()
531
+Infinity
532
sage: Gamma0(5).order()
533
+Infinity
534
sage: Gamma1(2).order()
535
+Infinity
536
sage: GammaH(12, [5]).order()
537
+Infinity
538
"""
539
from sage.rings.infinity import infinity
540
return infinity
541
542
def reduce_cusp(self, c):
543
r"""
544
Given a cusp `c \in \mathbb{P}^1(\QQ)`, return the unique reduced cusp
545
equivalent to c under the action of self, where a reduced cusp is an
546
element `\tfrac{r}{s}` with r,s coprime non-negative integers, s as
547
small as possible, and r as small as possible for that s.
548
549
NOTE: This function should be overridden by all subclasses.
550
551
EXAMPLES::
552
553
sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().reduce_cusp(1/4)
554
Traceback (most recent call last):
555
...
556
NotImplementedError
557
"""
558
raise NotImplementedError
559
560
def cusps(self, algorithm='default'):
561
r"""
562
Return a sorted list of inequivalent cusps for self, i.e. a set of
563
representatives for the orbits of self on `\mathbb{P}^1(\QQ)`.
564
These should be returned in a reduced form where this makes sense.
565
566
INPUTS:
567
568
- ``algorithm`` -- which algorithm to use to compute the cusps of self.
569
``'default'`` finds representatives for a known complete set of
570
cusps. ``'modsym'`` computes the boundary map on the space of weight
571
two modular symbols associated to self, which finds the cusps for
572
self in the process.
573
574
EXAMPLES::
575
576
sage: Gamma0(36).cusps()
577
[0, 1/18, 1/12, 1/9, 1/6, 1/4, 1/3, 5/12, 1/2, 2/3, 5/6, Infinity]
578
sage: Gamma0(36).cusps(algorithm='modsym') == Gamma0(36).cusps()
579
True
580
sage: GammaH(36, [19,29]).cusps() == Gamma0(36).cusps()
581
True
582
sage: Gamma0(1).cusps()
583
[Infinity]
584
"""
585
try:
586
return copy(self._cusp_list[algorithm])
587
except (AttributeError,KeyError):
588
self._cusp_list = {}
589
590
from congroup_sl2z import is_SL2Z
591
if is_SL2Z(self):
592
from sage.modular.cusps import Cusp
593
s = [Cusp(1,0)]
594
595
if algorithm == 'default':
596
s = self._find_cusps()
597
elif algorithm == 'modsym':
598
s = sorted([self.reduce_cusp(c) for c in self.modular_symbols().cusps()])
599
else:
600
raise ValueError, "unknown algorithm: %s"%algorithm
601
602
self._cusp_list[algorithm] = s
603
return copy(s)
604
605
def _find_cusps(self):
606
r"""
607
Calculate a list of inequivalent cusps.
608
609
EXAMPLES::
610
611
sage: sage.modular.arithgroup.congroup_generic.CongruenceSubgroup(5)._find_cusps()
612
Traceback (most recent call last):
613
...
614
NotImplementedError
615
616
NOTE: There is a generic algorithm implemented at the top level that
617
uses the coset representatives of self. This is *very slow* and for all
618
the standard congruence subgroups there is a quicker way of doing it,
619
so this should usually be overridden in subclasses; but it doesn't have
620
to be.
621
"""
622
from sage.modular.cusps import Cusp
623
i = Cusp([1,0])
624
L = [i]
625
for a in self.coset_reps():
626
ai = i.apply([a.a(), a.b(), a.c(), a.d()])
627
new = 1
628
for v in L:
629
if self.are_equivalent(ai, v):
630
new = 0
631
break
632
if new == 1:
633
L.append(ai)
634
return L
635
636
def are_equivalent(self, x, y, trans = False):
637
r"""
638
Test whether or not cusps x and y are equivalent modulo self. If self
639
has a reduce_cusp() method, use that; otherwise do a slow explicit
640
test.
641
642
If trans = False, returns True or False. If trans = True, then return
643
either False or an element of self mapping x onto y.
644
645
EXAMPLE::
646
647
sage: Gamma0(7).are_equivalent(Cusp(1/3), Cusp(0), trans=True)
648
[ 3 -1]
649
[-14 5]
650
sage: Gamma0(7).are_equivalent(Cusp(1/3), Cusp(1/7))
651
False
652
"""
653
x = Cusp(x)
654
y = Cusp(y)
655
if not trans:
656
try:
657
xr = self.reduce_cusp(x)
658
yr = self.reduce_cusp(y)
659
if xr != yr:
660
return False
661
if xr == yr:
662
return True
663
except NotImplementedError:
664
pass
665
666
from all import SL2Z
667
668
vx = lift_to_sl2z(x.numerator(),x.denominator(), 0)
669
dx = SL2Z([vx[2], -vx[0], vx[3], -vx[1]])
670
vy = lift_to_sl2z(y.numerator(),y.denominator(), 0)
671
dy = SL2Z([vy[2], -vy[0], vy[3], -vy[1]])
672
673
for i in xrange(self.index()):
674
# Note that the width of any cusp is bounded above by the index of self.
675
# If self is congruence, then the level of self is a much better bound, but
676
# this method is written to work with non-congruence subgroups as well,
677
if dy * SL2Z([1,i,0,1])*(~dx) in self:
678
if trans:
679
return dy * SL2Z([1,i,0,1]) * ~dx
680
else:
681
return True
682
elif (self.is_odd() and dy * SL2Z([-1,-i,0,-1]) * ~dx in self):
683
if trans:
684
return dy * SL2Z([-1,-i,0,-1]) * ~dx
685
else:
686
return True
687
return False
688
689
def cusp_data(self, c):
690
r"""
691
Return a triple (g, w, t) where g is an element of self generating the
692
stabiliser of the given cusp, w is the width of the cusp, and t is 1 if
693
the cusp is regular and -1 if not.
694
695
EXAMPLES::
696
697
sage: Gamma1(4).cusp_data(Cusps(1/2))
698
(
699
[ 1 -1]
700
[ 4 -3], 1, -1
701
)
702
"""
703
c = Cusp(c)
704
from all import SL2Z # can't import at top as that would cause a circular import
705
706
# first find an element of SL2Z sending infinity to the given cusp
707
w = lift_to_sl2z(c.denominator(), c.numerator(), 0)
708
g = SL2Z([w[3], w[1], w[2],w[0]])
709
710
for d in xrange(1,1+self.index()):
711
if g * SL2Z([1,d,0,1]) * (~g) in self:
712
return (g * SL2Z([1,d,0,1]) * (~g), d, 1)
713
elif g * SL2Z([-1,-d,0,-1]) * (~g) in self:
714
return (g * SL2Z([-1,-d,0,-1]) * (~g), d, -1)
715
raise ArithmeticError, "Can't get here!"
716
717
def is_regular_cusp(self, c):
718
r"""
719
Return True if the orbit of the given cusp is a regular cusp for self,
720
otherwise False. This is automatically true if -1 is in self.
721
722
EXAMPLES::
723
724
sage: Gamma1(4).is_regular_cusp(Cusps(1/2))
725
False
726
sage: Gamma1(4).is_regular_cusp(Cusps(oo))
727
True
728
"""
729
if self.is_even(): return True
730
return (self.cusp_data(c)[2] == 1)
731
732
def cusp_width(self, c):
733
r"""
734
Return the width of the orbit of cusps represented by c.
735
736
EXAMPLES::
737
738
sage: Gamma0(11).cusp_width(Cusps(oo))
739
1
740
sage: Gamma0(11).cusp_width(0)
741
11
742
sage: [Gamma0(100).cusp_width(c) for c in Gamma0(100).cusps()]
743
[100, 1, 4, 1, 1, 1, 4, 25, 1, 1, 4, 1, 25, 4, 1, 4, 1, 1]
744
"""
745
return self.cusp_data(c)[1]
746
747
def index(self):
748
r"""
749
Return the index of self in the full modular group.
750
751
EXAMPLES::
752
753
sage: Gamma0(17).index()
754
18
755
sage: sage.modular.arithgroup.congroup_generic.CongruenceSubgroup(5).index()
756
Traceback (most recent call last):
757
...
758
NotImplementedError
759
"""
760
761
return len(list(self.coset_reps()))
762
763
def generalised_level(self):
764
r"""
765
Return the generalised level of self, i.e. the least common multiple of
766
the widths of all cusps.
767
768
If self is *even*, Wohlfart's theorem tells us that this is equal to
769
the (conventional) level of self when self is a congruence subgroup.
770
This can fail if self is odd, but the actual level is at most twice the
771
generalised level. See the paper by Kiming, Schuett and Verrill for
772
more examples.
773
774
EXAMPLE::
775
776
sage: Gamma0(18).generalised_level()
777
18
778
sage: sage.modular.arithgroup.congroup_generic.CongruenceSubgroup(5).index()
779
Traceback (most recent call last):
780
...
781
NotImplementedError
782
783
In the following example, the actual level is twice the generalised
784
level. This is the group `G_2` from Example 17 of K-S-V.
785
786
::
787
788
sage: G = CongruenceSubgroup(8, [ [1,1,0,1], [3,-1,4,-1] ])
789
sage: G.level()
790
8
791
sage: G.generalised_level()
792
4
793
"""
794
return arith.lcm([self.cusp_width(c) for c in self.cusps()])
795
796
def projective_index(self):
797
r"""
798
Return the index of the image of self in `{\rm PSL}_2(\ZZ)`. This is equal
799
to the index of self if self contains -1, and half of this otherwise.
800
801
This is equal to the degree of the natural map from the modular curve
802
of self to the `j`-line.
803
804
EXAMPLE::
805
806
sage: Gamma0(5).projective_index()
807
6
808
sage: Gamma1(5).projective_index()
809
12
810
"""
811
812
if self.is_even():
813
return self.index()
814
else:
815
return self.index() // 2
816
817
def is_congruence(self):
818
r"""
819
Return True if self is a congruence subgroup.
820
821
EXAMPLE::
822
823
sage: Gamma0(5).is_congruence()
824
True
825
sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().is_congruence()
826
Traceback (most recent call last):
827
...
828
NotImplementedError
829
"""
830
831
raise NotImplementedError
832
833
def genus(self):
834
r"""
835
Return the genus of the modular curve of self.
836
837
EXAMPLES::
838
839
sage: Gamma1(5).genus()
840
0
841
sage: Gamma1(31).genus()
842
26
843
sage: Gamma1(157).genus() == dimension_cusp_forms(Gamma1(157), 2)
844
True
845
sage: GammaH(7, [2]).genus()
846
0
847
sage: [Gamma0(n).genus() for n in [1..23]]
848
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 2, 2]
849
sage: [n for n in [1..200] if Gamma0(n).genus() == 1]
850
[11, 14, 15, 17, 19, 20, 21, 24, 27, 32, 36, 49]
851
852
853
"""
854
855
return ZZ(1 + (self.projective_index()) / ZZ(12) - (self.nu2())/ZZ(4) - (self.nu3())/ZZ(3) - self.ncusps()/ZZ(2))
856
857
def farey_symbol(self):
858
r"""
859
Return the Farey symbol associated to this subgroup. See the
860
:mod:`~sage.modular.arithgroup.farey_symbol` module for more
861
information.
862
863
EXAMPLE::
864
865
sage: Gamma1(4).farey_symbol()
866
FareySymbol(Congruence Subgroup Gamma1(4))
867
"""
868
from farey_symbol import Farey
869
return Farey(self)
870
871
@cached_method
872
def generators(self, algorithm="farey"):
873
r"""
874
Return a list of generators for this congruence subgroup. The result is cached.
875
876
INPUT:
877
878
- ``algorithm`` (string): either ``farey`` or ``todd-coxeter``.
879
880
If ``algorithm`` is set to ``"farey"``, then the generators will be
881
calculated using Farey symbols, which will always return a *minimal*
882
generating set. See :mod:`~sage.modular.arithgroup.farey_symbol` for
883
more information.
884
885
If ``algorithm`` is set to ``"todd-coxeter"``, a simpler algorithm
886
based on Todd-Coxeter enumeration will be used. This is *exceedingly*
887
slow for general subgroups, and the list of generators will be far from
888
minimal (indeed it may contain repetitions).
889
890
EXAMPLE::
891
892
sage: Gamma(2).generators()
893
[
894
[1 2] [ 3 -2] [-1 0]
895
[0 1], [ 2 -1], [ 0 -1]
896
]
897
sage: Gamma(2).generators(algorithm="todd-coxeter")
898
[
899
[1 2] [-1 0] [ 1 0] [-1 0] [-1 2] [-1 0] [1 0]
900
[0 1], [ 0 -1], [-2 1], [ 0 -1], [-2 3], [ 2 -1], [2 1]
901
]
902
"""
903
if algorithm=="farey":
904
return self.farey_symbol().generators()
905
elif algorithm == "todd-coxeter":
906
return self.todd_coxeter()[1]
907
else:
908
raise ValueError, "Unknown algorithm '%s' (should be either 'farey' or 'todd-coxeter')" % algorithm
909
910
def gens(self, *args, **kwds):
911
r"""
912
Return a tuple of generators for this congruence subgroup.
913
914
The generators need not be minimal. For arguments, see :meth:`~generators`.
915
916
EXAMPLES::
917
918
sage: SL2Z.gens()
919
(
920
[ 0 -1] [1 1]
921
[ 1 0], [0 1]
922
)
923
"""
924
return tuple(self.generators(*args, **kwds))
925
926
def gen(self, i):
927
r"""
928
Return the i-th generator of self, i.e. the i-th element of the
929
tuple self.gens().
930
931
EXAMPLES::
932
933
sage: SL2Z.gen(1)
934
[1 1]
935
[0 1]
936
"""
937
return self.generators()[i]
938
939
def ngens(self):
940
r"""
941
Return the size of the minimal generating set of self returned by
942
:meth:`generators`.
943
944
EXAMPLES::
945
946
sage: Gamma0(22).ngens()
947
8
948
sage: Gamma1(14).ngens()
949
13
950
sage: GammaH(11, [3]).ngens()
951
3
952
sage: SL2Z.ngens()
953
2
954
"""
955
return len(self.generators())
956
957
def ncusps(self):
958
r"""
959
Return the number of cusps of this arithmetic subgroup. This is
960
provided as a separate function since for dimension formulae in even
961
weight all we need to know is the number of cusps, and this can be
962
calculated very quickly, while enumerating all cusps is much slower.
963
964
EXAMPLES::
965
966
sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.ncusps(Gamma0(7))
967
2
968
"""
969
970
return ZZ(len(self.cusps()))
971
972
def nregcusps(self):
973
r"""
974
Return the number of cusps of self that are "regular", i.e. their
975
stabiliser has a generator with both eigenvalues +1 rather than -1. If
976
the group contains -1, every cusp is clearly regular.
977
978
EXAMPLES::
979
980
sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.nregcusps(Gamma1(4))
981
2
982
"""
983
return self.ncusps() - self.nirregcusps()
984
985
def nirregcusps(self):
986
r"""
987
Return the number of cusps of self that are "irregular", i.e. their
988
stabiliser can only be generated by elements with both eigenvalues -1
989
rather than +1. If the group contains -1, every cusp is clearly
990
regular.
991
992
EXAMPLES::
993
994
sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup.nirregcusps(Gamma1(4))
995
1
996
"""
997
if self.is_even():
998
return 0
999
else:
1000
return ZZ(len([c for c in self.cusps() if not self.is_regular_cusp(c)]))
1001
1002
def dimension_modular_forms(self, k=2):
1003
r"""
1004
Return the dimension of the space of weight k modular forms for this
1005
group. This is given by a standard formula in terms of k and various
1006
invariants of the group; see Diamond + Shurman, "A First Course in
1007
Modular Forms", section 3.5 and 3.6. If k is not given, defaults to k =
1008
2.
1009
1010
For dimensions of spaces of modular forms with character for Gamma1, use
1011
the standalone function dimension_modular_forms().
1012
1013
For weight 1 modular forms this function only works in cases where one
1014
can prove solely in terms of Riemann-Roch theory that there aren't any
1015
cusp forms (i.e. when the number of regular cusps is strictly greater
1016
than the degree of the canonical divisor). Otherwise a
1017
NotImplementedError is raised.
1018
1019
EXAMPLE::
1020
1021
sage: Gamma1(31).dimension_modular_forms(2)
1022
55
1023
sage: Gamma1(3).dimension_modular_forms(1)
1024
1
1025
sage: Gamma1(4).dimension_modular_forms(1) # irregular cusp
1026
1
1027
sage: Gamma1(31).dimension_modular_forms(1)
1028
Traceback (most recent call last):
1029
...
1030
NotImplementedError: Computation of dimensions of weight 1 cusp forms spaces not implemented in general
1031
"""
1032
1033
k = ZZ(k)
1034
if k < 0: return ZZ(0)
1035
if k == 0: return ZZ(1)
1036
1037
if not (k % 2):
1038
# k even
1039
1040
return (k-1) * (self.genus() - 1) + (k // ZZ(4))*self.nu2() + (k // ZZ(3))*self.nu3() + (k // ZZ(2))*self.ncusps()
1041
1042
else:
1043
# k odd
1044
if self.is_even():
1045
return ZZ(0)
1046
else:
1047
e_reg = self.nregcusps()
1048
e_irr = self.nirregcusps()
1049
1050
if k > 1:
1051
return (k-1)*(self.genus()-1) + (k // ZZ(3)) * self.nu3() + (k * e_reg)/ZZ(2) + (k-1)/ZZ(2) * e_irr
1052
else:
1053
if e_reg > 2*self.genus() - 2:
1054
return e_reg / ZZ(2)
1055
else:
1056
raise NotImplementedError, "Computation of dimensions of weight 1 modular forms spaces not implemented in general"
1057
1058
def dimension_cusp_forms(self, k=2):
1059
r"""
1060
Return the dimension of the space of weight k cusp forms for this
1061
group. This is given by a standard formula in terms of k and various
1062
invariants of the group; see Diamond + Shurman, "A First Course in
1063
Modular Forms", section 3.5 and 3.6. If k is not given, default to k =
1064
2.
1065
1066
For dimensions of spaces of cusp forms with character for Gamma1, use
1067
the standalone function dimension_cusp_forms().
1068
1069
For weight 1 cusp forms this function only works in cases where one can
1070
prove solely in terms of Riemann-Roch theory that there aren't any cusp
1071
forms (i.e. when the number of regular cusps is strictly greater than
1072
the degree of the canonical divisor). Otherwise a NotImplementedError is
1073
raised.
1074
1075
EXAMPLE::
1076
1077
sage: Gamma1(31).dimension_cusp_forms(2)
1078
26
1079
sage: Gamma1(3).dimension_cusp_forms(1)
1080
0
1081
sage: Gamma1(4).dimension_cusp_forms(1) # irregular cusp
1082
0
1083
sage: Gamma1(31).dimension_cusp_forms(1)
1084
Traceback (most recent call last):
1085
...
1086
NotImplementedError: Computation of dimensions of weight 1 cusp forms spaces not implemented in general
1087
"""
1088
k = ZZ(k)
1089
if k <= 0: return ZZ(0)
1090
1091
if not (k % 2):
1092
# k even
1093
1094
if k == 2:
1095
return self.genus()
1096
1097
else:
1098
return (k-1) * (self.genus() - 1) + (k // ZZ(4))*self.nu2() + (k // ZZ(3))*self.nu3() + (k // ZZ(2) - 1)*self.ncusps()
1099
1100
else:
1101
# k odd
1102
1103
if self.is_even():
1104
return ZZ(0)
1105
1106
else:
1107
e_reg = self.nregcusps()
1108
e_irr = self.nirregcusps()
1109
1110
if k > 1:
1111
return (k-1)*(self.genus()-1) + (k // ZZ(3)) * self.nu3() + (k-2)/ZZ(2) * e_reg + (k-1)/ZZ(2) * e_irr
1112
else:
1113
if e_reg > 2*self.genus() - 2:
1114
return ZZ(0)
1115
else:
1116
raise NotImplementedError, "Computation of dimensions of weight 1 cusp forms spaces not implemented in general"
1117
1118
def dimension_eis(self, k=2):
1119
r"""
1120
Return the dimension of the space of weight k Eisenstein series for
1121
this group, which is a subspace of the space of modular forms
1122
complementary to the space of cusp forms.
1123
1124
INPUT:
1125
1126
- ``k`` - an integer (default 2).
1127
1128
EXAMPLES::
1129
1130
sage: GammaH(33,[2]).dimension_eis()
1131
7
1132
sage: GammaH(33,[2]).dimension_eis(3)
1133
0
1134
sage: GammaH(33, [2,5]).dimension_eis(2)
1135
3
1136
sage: GammaH(33, [4]).dimension_eis(1)
1137
4
1138
"""
1139
1140
if k < 0: return ZZ(0)
1141
if k == 0: return ZZ(1)
1142
1143
if not (k % 2): # k even
1144
if k > 2:
1145
return self.ncusps()
1146
else: # k = 2
1147
return self.ncusps() - 1
1148
1149
else: # k odd
1150
if self.is_even():
1151
return 0
1152
if k > 1:
1153
return self.nregcusps()
1154
else: # k = 1
1155
return ZZ(self.nregcusps()/ ZZ(2))
1156
1157
def as_permutation_group(self):
1158
r"""
1159
Return self as an arithmetic subgroup defined in terms of the
1160
permutation action of `SL(2,\ZZ)` on its right cosets.
1161
1162
This method uses Todd-coxeter enumeration (via the method
1163
:meth:`~todd_coxeter`) which can be extremely slow for arithmetic
1164
subgroups with relatively large index in `SL(2,\ZZ)`.
1165
1166
EXAMPLES::
1167
1168
sage: G = Gamma(3)
1169
sage: P = G.as_permutation_group(); P
1170
Arithmetic subgroup of index 24
1171
sage: G.ncusps() == P.ncusps()
1172
True
1173
sage: G.nu2() == P.nu2()
1174
True
1175
sage: G.nu3() == P.nu3()
1176
True
1177
sage: G.an_element() in P
1178
True
1179
sage: P.an_element() in G
1180
True
1181
"""
1182
_,_,l_edges,s2_edges=self.todd_coxeter()
1183
n = len(l_edges)
1184
s3_edges = [None] * n
1185
r_edges = [None] * n
1186
for i in xrange(n):
1187
ii = s2_edges[l_edges[i]]
1188
s3_edges[ii] = i
1189
r_edges[ii] = s2_edges[i]
1190
if self.is_even():
1191
from sage.modular.arithgroup.arithgroup_perm import EvenArithmeticSubgroup_Permutation
1192
g=EvenArithmeticSubgroup_Permutation(S2=s2_edges,S3=s3_edges,L=l_edges,R=r_edges)
1193
else:
1194
from sage.modular.arithgroup.arithgroup_perm import OddArithmeticSubgroup_Permutation
1195
g=OddArithmeticSubgroup_Permutation(S2=s2_edges,S3=s3_edges,L=l_edges,R=r_edges)
1196
g.relabel()
1197
return g
1198
1199
def sturm_bound(self, weight=2):
1200
r"""
1201
Returns the Sturm bound for modular forms of the given weight and level
1202
this subgroup.
1203
1204
INPUT:
1205
1206
- ``weight`` - an integer `\geq 2` (default: 2)
1207
1208
EXAMPLES::
1209
sage: Gamma0(11).sturm_bound(2)
1210
2
1211
sage: Gamma0(389).sturm_bound(2)
1212
65
1213
sage: Gamma0(1).sturm_bound(12)
1214
1
1215
sage: Gamma0(100).sturm_bound(2)
1216
30
1217
sage: Gamma0(1).sturm_bound(36)
1218
3
1219
sage: Gamma0(11).sturm_bound()
1220
2
1221
sage: Gamma0(13).sturm_bound()
1222
3
1223
sage: Gamma0(16).sturm_bound()
1224
4
1225
sage: GammaH(16,[13]).sturm_bound()
1226
8
1227
sage: GammaH(16,[15]).sturm_bound()
1228
16
1229
sage: Gamma1(16).sturm_bound()
1230
32
1231
sage: Gamma1(13).sturm_bound()
1232
28
1233
sage: Gamma1(13).sturm_bound(5)
1234
70
1235
1236
FURTHER DETAILS: This function returns a positive integer
1237
`n` such that the Hecke operators
1238
`T_1,\ldots, T_n` acting on *cusp forms* generate the
1239
Hecke algebra as a `\ZZ`-module when the character
1240
is trivial or quadratic. Otherwise, `T_1,\ldots,T_n`
1241
generate the Hecke algebra at least as a
1242
`\ZZ[\varepsilon]`-module, where
1243
`\ZZ[\varepsilon]` is the ring generated by the
1244
values of the Dirichlet character `\varepsilon`.
1245
Alternatively, this is a bound such that if two cusp forms
1246
associated to this space of modular symbols are congruent modulo
1247
`(\lambda, q^n)`, then they are congruent modulo
1248
`\lambda`.
1249
1250
REFERENCES:
1251
1252
- See the Agashe-Stein appendix to Lario and Schoof,
1253
*Some computations with Hecke rings and deformation rings*,
1254
Experimental Math., 11 (2002), no. 2, 303-311.
1255
1256
- This result originated in the paper Sturm,
1257
*On the congruence of modular forms*,
1258
Springer LNM 1240, 275-280, 1987.
1259
1260
REMARK: Kevin Buzzard pointed out to me (William Stein) in Fall
1261
2002 that the above bound is fine for `\Gamma_1(N)` with
1262
character, as one sees by taking a power of `f`. More
1263
precisely, if `f \cong 0 \pmod{p}` for first
1264
`s` coefficients, then `f^r \cong 0 \pmod{p}` for
1265
first `sr` coefficients. Since the weight of `f^r`
1266
is `r\cdot k(f)`, it follows that if
1267
`s \geq b`, where `b` is the Sturm bound for
1268
`\Gamma_0(N)` at weight `k(f)`, then `f^r`
1269
has valuation large enough to be forced to be `0` at
1270
`r*k(f)` by Sturm bound (which is valid if we choose
1271
`r` correctly). Thus `f \cong 0 \pmod{p}`.
1272
Conclusion: For `\Gamma_1(N)` with fixed character, the
1273
Sturm bound is *exactly* the same as for `\Gamma_0(N)`.
1274
1275
A key point is that we are finding
1276
`\ZZ[\varepsilon]` generators for the Hecke algebra
1277
here, not `\ZZ`-generators. So if one wants
1278
generators for the Hecke algebra over `\ZZ`, this
1279
bound must be suitably modified (and I'm not sure what the
1280
modification is).
1281
1282
AUTHORS:
1283
1284
- William Stein
1285
"""
1286
return ZZ((self.index() * weight / ZZ(12)).ceil())
1287
1288