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