Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/modular/arithgroup/congroup_generic.py
8820 views
1
r"""
2
Congruence arithmetic subgroups of `{\rm SL}_2(\ZZ)`
3
4
Sage can compute extensively with the standard congruence subgroups
5
`\Gamma_0(N)`, `\Gamma_1(N)`, and `\Gamma_H(N)`.
6
7
AUTHORS:
8
9
- William Stein
10
- David Loeffler (2009, 10) -- modifications to work with more general arithmetic subgroups
11
"""
12
13
################################################################################
14
#
15
# Copyright (C) 2004, 2006 William Stein <[email protected]>
16
#
17
# Distributed under the terms of the GNU General Public License (GPL)
18
#
19
# The full text of the GPL is available at:
20
#
21
# http://www.gnu.org/licenses/
22
#
23
################################################################################
24
25
from sage.rings.all import QQ, ZZ, Zmod
26
from sage.rings.arith import gcd
27
from sage.sets.set import Set
28
from sage.groups.matrix_gps.all import MatrixGroup
29
from sage.matrix.matrix_space import MatrixSpace
30
from sage.misc.misc_c import prod
31
from arithgroup_generic import ArithmeticSubgroup
32
33
34
def CongruenceSubgroup_constructor(*args):
35
r"""
36
Attempt to create a congruence subgroup from the given data.
37
38
The allowed inputs are as follows:
39
40
- A :class:`~sage.groups.matrix_gps.matrix_group.MatrixGroup` object. This
41
must be a group of matrices over `\ZZ / N\ZZ` for some `N`, with
42
determinant 1, in which case the function will return the group of
43
matrices in `SL(2, \ZZ)` whose reduction mod `N` is in the given group.
44
45
- A list of matrices over `\ZZ / N\ZZ` for some `N`. The function will then
46
compute the subgroup of `SL(2, \ZZ)` generated by these matrices, and
47
proceed as above.
48
49
- An integer `N` and a list of matrices (over any ring coercible to `\ZZ /
50
N\ZZ`, e.g. over `\ZZ`). The matrices will then be coerced to `\ZZ /
51
N\ZZ`.
52
53
The function checks that the input G is valid. It then tests to see if
54
`G` is the preimage mod `N` of some group of matrices modulo a proper
55
divisor `M` of `N`, in which case it replaces `G` with this group before
56
continuing.
57
58
EXAMPLES::
59
60
sage: from sage.modular.arithgroup.congroup_generic import CongruenceSubgroup_constructor as CS
61
sage: CS(2, [[1,1,0,1]])
62
Congruence subgroup of SL(2,Z) of level 2, preimage of:
63
Matrix group over Ring of integers modulo 2 with 1 generators (
64
[1 1]
65
[0 1]
66
)
67
sage: CS([matrix(Zmod(2), 2, [1,1,0,1])])
68
Congruence subgroup of SL(2,Z) of level 2, preimage of:
69
Matrix group over Ring of integers modulo 2 with 1 generators (
70
[1 1]
71
[0 1]
72
)
73
sage: CS(MatrixGroup([matrix(Zmod(2), 2, [1,1,0,1])]))
74
Congruence subgroup of SL(2,Z) of level 2, preimage of:
75
Matrix group over Ring of integers modulo 2 with 1 generators (
76
[1 1]
77
[0 1]
78
)
79
sage: CS(SL(2, 2))
80
Modular Group SL(2,Z)
81
82
Some invalid inputs::
83
84
sage: CS(SU(2, 7))
85
Traceback (most recent call last):
86
...
87
TypeError: Ring of definition must be Z / NZ for some N
88
"""
89
from sage.groups.matrix_gps.matrix_group import is_MatrixGroup
90
if is_MatrixGroup(args[0]):
91
G = args[0]
92
93
elif type(args[0]) == type([]):
94
G = MatrixGroup(args[0])
95
96
elif args[0] in ZZ:
97
M = MatrixSpace(Zmod(args[0]), 2)
98
G = MatrixGroup([M(x) for x in args[1]])
99
100
R = G.matrix_space().base_ring()
101
if not hasattr(R, "cover_ring") or R.cover_ring() != ZZ:
102
raise TypeError, "Ring of definition must be Z / NZ for some N"
103
104
if not all([x.matrix().det() == 1 for x in G.gens()]):
105
raise ValueError, "Group must be contained in SL(2, Z / N)"
106
GG = _minimize_level(G)
107
if GG in ZZ:
108
from all import Gamma
109
return Gamma(GG)
110
else:
111
return CongruenceSubgroupFromGroup(GG)
112
113
def is_CongruenceSubgroup(x):
114
"""
115
Return True if x is of type CongruenceSubgroup.
116
117
Note that this may be False even if `x` really is a congruence subgroup --
118
it tests whether `x` is "obviously" congruence, i.e.~whether it has a
119
congruence subgroup datatype. To test whether or not an arithmetic subgroup
120
of `SL(2, \ZZ)` is congruence, use the ``is_congruence()`` method instead.
121
122
EXAMPLES::
123
124
sage: from sage.modular.arithgroup.congroup_generic import is_CongruenceSubgroup
125
sage: is_CongruenceSubgroup(SL2Z)
126
True
127
sage: is_CongruenceSubgroup(Gamma0(13))
128
True
129
sage: is_CongruenceSubgroup(Gamma1(6))
130
True
131
sage: is_CongruenceSubgroup(GammaH(11, [3]))
132
True
133
sage: G = ArithmeticSubgroup_Permutation(L = "(1, 2)", R = "(1, 2)"); is_CongruenceSubgroup(G)
134
False
135
sage: G.is_congruence()
136
True
137
sage: is_CongruenceSubgroup(SymmetricGroup(3))
138
False
139
"""
140
return isinstance(x, CongruenceSubgroupBase)
141
142
class CongruenceSubgroupBase(ArithmeticSubgroup):
143
144
def __init__(self, level):
145
"""
146
Create a congruence subgroup with given level.
147
148
EXAMPLES::
149
150
sage: Gamma0(500)
151
Congruence Subgroup Gamma0(500)
152
"""
153
level = ZZ(level)
154
if level <= 0:
155
raise ArithmeticError, "Congruence groups only defined for positive levels."
156
self.__level = level
157
ArithmeticSubgroup.__init__(self)
158
159
def _an_element_(self):
160
r"""
161
Return an element of self (mainly for use by the test suite).
162
163
EXAMPLE::
164
165
sage: Gamma(3).an_element() # indirect doctest
166
[-2 -3]
167
[ 3 4]
168
"""
169
N = self.level()
170
return self([1-N, -N, N, 1+N])
171
172
def is_congruence(self):
173
r"""
174
Return True, since this is a congruence subgroup.
175
176
EXAMPLE::
177
178
sage: Gamma0(7).is_congruence()
179
True
180
"""
181
182
return True
183
184
def level(self):
185
"""
186
Return the level of this congruence subgroup.
187
188
EXAMPLES::
189
190
sage: SL2Z.level()
191
1
192
sage: Gamma0(20).level()
193
20
194
sage: Gamma1(11).level()
195
11
196
sage: GammaH(14, [5]).level()
197
14
198
"""
199
return self.__level
200
201
def __cmp__(self, other):
202
r"""
203
EXAMPLE::
204
205
sage: CongruenceSubgroup(3,[ [1,1,0,1] ]) == Gamma1(3)
206
True
207
sage: CongruenceSubgroup(3,[ [1,1,0,1] ]) == Gamma(3)
208
False
209
sage: CongruenceSubgroup(3,[ [1,1,0,1] ]) == QQ
210
False
211
"""
212
# This is carefully laid out so it can be called early on in the Sage
213
# startup process when we want to create the standard generators of
214
# SL2Z for use in arithgroup_perm. Hence it must work in this case
215
# without being able to import the arithgroup_perm module. That's why
216
# the most general case is *first*, not last.
217
# Note that lazy_import doesn't work here, because it doesn't play
218
# nicely with isinstance().
219
if not isinstance(other, ArithmeticSubgroup):
220
return cmp(type(self), type(other))
221
222
elif is_CongruenceSubgroup(other):
223
t = cmp(self.level(), other.level())
224
if t: return t
225
if self.level() == 1: return 0 # shouldn't come up except with pickling/unpickling
226
t = cmp(self.index(), other.index())
227
if t: return t
228
return cmp(self.image_mod_n(),other.image_mod_n())
229
230
from sage.modular.arithgroup.arithgroup_perm import ArithmeticSubgroup_Permutation_class
231
if isinstance(other, ArithmeticSubgroup_Permutation_class):
232
return cmp(self.as_permutation_group(), other)
233
234
else:
235
# we shouldn't ever get here
236
raise NotImplementedError
237
238
class CongruenceSubgroupFromGroup(CongruenceSubgroupBase):
239
r"""
240
A congruence subgroup, defined by the data of an integer `N` and a subgroup
241
`G` of the finite group `SL(2, \ZZ / N\ZZ)`; the congruence subgroup
242
consists of all the matrices in `SL(2, \ZZ)` whose reduction modulo `N`
243
lies in `G`.
244
245
This class should not be instantiated directly, but created using the
246
factory function
247
:func:`~sage.modular.arithgroup.congroup_generic.CongruenceSubgroup_constructor`,
248
which accepts much more flexible input, and checks the input to make sure
249
it is valid.
250
251
TESTS::
252
253
sage: G = CongruenceSubgroup(5, [[0,-1,1,0]]); G
254
Congruence subgroup of SL(2,Z) of level 5, preimage of:
255
Matrix group over Ring of integers modulo 5 with 1 generators (
256
[0 4]
257
[1 0]
258
)
259
sage: TestSuite(G).run()
260
"""
261
262
def __init__(self, G):
263
r"""
264
Standard init function.
265
266
TESTS::
267
268
sage: from sage.modular.arithgroup.congroup_generic import CongruenceSubgroupFromGroup
269
sage: G = MatrixGroup([matrix(Zmod(2), 2, [1,1,1,0])])
270
sage: CongruenceSubgroupFromGroup(G).index() # indirect doctest
271
2
272
"""
273
N = G.base_ring().characteristic()
274
self.__G = G
275
CongruenceSubgroupBase.__init__(self, N)
276
277
def __reduce__(self):
278
r"""
279
Data defining self (for pickling).
280
281
EXAMPLE::
282
283
sage: G = CongruenceSubgroup(5, [[0,-1,1,0]])
284
sage: G.__reduce__()
285
(<function CongruenceSubgroup_constructor at ...>,
286
(Matrix group over Ring of integers modulo 5 with 1 generators (
287
[0 4]
288
[1 0]
289
),))
290
"""
291
return CongruenceSubgroup_constructor, (self.image_mod_n(),)
292
293
def _contains_sl2(self, a,b,c,d):
294
r"""
295
Test whether ``[a,b;c,d]`` is an element of self.
296
297
EXAMPLE::
298
299
sage: G = MatrixGroup([matrix(Zmod(2), 2, [1,1,1,0])])
300
sage: H = sage.modular.arithgroup.congroup_generic.CongruenceSubgroupFromGroup(G)
301
sage: H(1)
302
[1 0]
303
[0 1]
304
sage: H([0,-1,1,0])
305
Traceback (most recent call last):
306
...
307
TypeError: matrix [ 0 -1]
308
[ 1 0] is not an element of Congruence subgroup of SL(2,Z) of level 2, preimage of:
309
Matrix group over Ring of integers modulo 2 with 1 generators (
310
[1 1]
311
[1 0]
312
)
313
sage: H([1,2,0,1])
314
[1 2]
315
[0 1]
316
sage: H(SL2Z([0,-1,1,0]), check=False)
317
[ 0 -1]
318
[ 1 0]
319
sage: H([1,2,0,1]).parent()
320
Modular Group SL(2,Z)
321
"""
322
return ([a,b,c,d] in self.image_mod_n())
323
324
def to_even_subgroup(self):
325
r"""
326
Return the smallest even subgroup of `SL(2, \ZZ)` containing self.
327
328
EXAMPLE::
329
330
sage: G = Gamma(3)
331
sage: G.to_even_subgroup()
332
Congruence subgroup of SL(2,Z) of level 3, preimage of:
333
Matrix group over Ring of integers modulo 3 with 1 generators (
334
[2 0]
335
[0 2]
336
)
337
"""
338
if self.is_even():
339
return self
340
else:
341
G = self.image_mod_n()
342
H = MatrixGroup([ g.matrix() for g in G.gens()] + [G.matrix_space()(-1)])
343
return CongruenceSubgroup_constructor(H)
344
345
def _repr_(self):
346
r"""
347
String representation of self.
348
349
EXAMPLE::
350
351
sage: sage.modular.arithgroup.congroup_generic.CongruenceSubgroupFromGroup(MatrixGroup([matrix(Zmod(2), 2, [1,1,1,0])]))._repr_()
352
'Congruence subgroup of SL(2,Z) of level 2, preimage of:\n Matrix group over Ring of integers modulo 2 with 1 generators (\n[1 1]\n[1 0]\n)'
353
"""
354
return "Congruence subgroup of SL(2,Z) of level %s, preimage of:\n %s" % (self.level(), self.image_mod_n())
355
356
def index(self):
357
r"""
358
Return the index of self in the full modular group. This is equal to
359
the index in `SL(2, \ZZ / N\ZZ)` of the image of this group modulo
360
`\Gamma(N)`.
361
362
EXAMPLE::
363
364
sage: sage.modular.arithgroup.congroup_generic.CongruenceSubgroupFromGroup(MatrixGroup([matrix(Zmod(2), 2, [1,1,1,0])])).index()
365
2
366
"""
367
return prod([p**(3*e-2)*(p*p-1) for (p,e) in self.level().factor()]) // self.image_mod_n().order()
368
369
def image_mod_n(self):
370
r"""
371
Return the subgroup of `SL(2, \ZZ / N\ZZ)` of which this is the preimage, where `N` is the level of self.
372
373
EXAMPLE::
374
375
sage: G = MatrixGroup([matrix(Zmod(2), 2, [1,1,1,0])])
376
sage: H = sage.modular.arithgroup.congroup_generic.CongruenceSubgroupFromGroup(G); H.image_mod_n()
377
Matrix group over Ring of integers modulo 2 with 1 generators (
378
[1 1]
379
[1 0]
380
)
381
sage: H.image_mod_n() == G
382
True
383
"""
384
return self.__G
385
386
class CongruenceSubgroup(CongruenceSubgroupFromGroup):
387
r"""
388
One of the "standard" congruence subgroups `\Gamma_0(N)`, `\Gamma_1(N)`,
389
`\Gamma(N)`, or `\Gamma_H(N)` (for some `H`).
390
391
This class is not intended to be instantiated directly. Derived subclasses
392
must override ``_contains_sl2``, ``_repr_``, and ``image_mod_n``.
393
"""
394
395
def image_mod_n(self):
396
r"""
397
Raise an error: all derived subclasses should override this function.
398
399
EXAMPLE::
400
401
sage: sage.modular.arithgroup.congroup_generic.CongruenceSubgroup(5).image_mod_n()
402
Traceback (most recent call last):
403
...
404
NotImplementedError
405
"""
406
raise NotImplementedError
407
408
def __init__(self,*args, **kwds):
409
r"""
410
Bypass the init function of the CongruenceSubgroupFromGroup class.
411
412
EXAMPLE::
413
414
sage: sage.modular.arithgroup.congroup_generic.CongruenceSubgroup(5) # indirect doctest
415
Generic congruence subgroup of level 5
416
"""
417
return CongruenceSubgroupBase.__init__(self, *args, **kwds)
418
419
def _repr_(self):
420
"""
421
Return the string representation of self.
422
423
NOTE: This function should be overridden by all subclasses.
424
425
EXAMPLES::
426
427
sage: sage.modular.arithgroup.congroup_generic.CongruenceSubgroup(5)._repr_()
428
'Generic congruence subgroup of level 5'
429
"""
430
return "Generic congruence subgroup of level %s" % self.level()
431
432
def modular_symbols(self, sign=0, weight=2, base_ring=QQ):
433
"""
434
Return the space of modular symbols of the specified weight and sign
435
on the congruence subgroup self.
436
437
EXAMPLES::
438
439
sage: G = Gamma0(23)
440
sage: G.modular_symbols()
441
Modular Symbols space of dimension 5 for Gamma_0(23) of weight 2 with sign 0 over Rational Field
442
sage: G.modular_symbols(weight=4)
443
Modular Symbols space of dimension 12 for Gamma_0(23) of weight 4 with sign 0 over Rational Field
444
sage: G.modular_symbols(base_ring=GF(7))
445
Modular Symbols space of dimension 5 for Gamma_0(23) of weight 2 with sign 0 over Finite Field of size 7
446
sage: G.modular_symbols(sign=1)
447
Modular Symbols space of dimension 3 for Gamma_0(23) of weight 2 with sign 1 over Rational Field
448
"""
449
from sage.modular.modsym.modsym import ModularSymbols
450
return ModularSymbols(self, sign=sign, weight=weight, base_ring=base_ring)
451
452
def modular_abelian_variety(self):
453
"""
454
Return the modular abelian variety corresponding to the congruence
455
subgroup self.
456
457
EXAMPLES::
458
459
sage: Gamma0(11).modular_abelian_variety()
460
Abelian variety J0(11) of dimension 1
461
sage: Gamma1(11).modular_abelian_variety()
462
Abelian variety J1(11) of dimension 1
463
sage: GammaH(11,[3]).modular_abelian_variety()
464
Abelian variety JH(11,[3]) of dimension 1
465
"""
466
from sage.modular.abvar.abvar_ambient_jacobian import ModAbVar_ambient_jacobian
467
return ModAbVar_ambient_jacobian(self)
468
469
def _new_group_from_level(self, level):
470
r"""
471
Return a new group of the same type (Gamma0, Gamma1, or
472
GammaH) as self of the given level. In the case that self is of type
473
GammaH, we take the largest H inside `(\ZZ/ \text{level}\ZZ)^\times`
474
which maps to H, namely its inverse image under the natural reduction
475
map.
476
477
EXAMPLES::
478
479
sage: G = Gamma0(20)
480
sage: G._new_group_from_level(4)
481
Congruence Subgroup Gamma0(4)
482
sage: G._new_group_from_level(40)
483
Congruence Subgroup Gamma0(40)
484
485
sage: G = Gamma1(10)
486
sage: G._new_group_from_level(6)
487
Traceback (most recent call last):
488
...
489
ValueError: one level must divide the other
490
491
sage: G = GammaH(50,[7]); G
492
Congruence Subgroup Gamma_H(50) with H generated by [7]
493
sage: G._new_group_from_level(25)
494
Congruence Subgroup Gamma_H(25) with H generated by [7]
495
sage: G._new_group_from_level(10)
496
Congruence Subgroup Gamma0(10)
497
sage: G._new_group_from_level(100)
498
Congruence Subgroup Gamma_H(100) with H generated by [7, 57]
499
"""
500
from congroup_gamma0 import is_Gamma0
501
from congroup_gamma1 import is_Gamma1
502
from congroup_gammaH import is_GammaH
503
from all import Gamma0, Gamma1, GammaH
504
N = self.level()
505
if (level%N) and (N%level):
506
raise ValueError, "one level must divide the other"
507
if is_Gamma0(self):
508
return Gamma0(level)
509
elif is_Gamma1(self):
510
return Gamma1(level)
511
elif is_GammaH(self):
512
H = self._generators_for_H()
513
if level > N:
514
d = level // N
515
diffs = [ N*i for i in range(d) ]
516
newH = [ h + diff for h in H for diff in diffs ]
517
return GammaH(level, [x for x in newH if gcd(level, x) == 1])
518
else:
519
return GammaH(level, [ h%level for h in H ])
520
else:
521
raise NotImplementedError
522
523
def _minimize_level(G):
524
r"""
525
Utility function. Given a matrix group `G` contained in `SL(2, \ZZ / N\ZZ)`
526
for some `N`, test whether or not `G` is the preimage of a subgroup of
527
smaller level, and if so, return that subgroup.
528
529
The trivial group is handled specially: instead of returning a group, it
530
returns an integer `N`, representing the trivial subgroup of `SL(2, \ZZ /
531
N\ZZ)`.
532
533
EXAMPLE::
534
535
sage: M = MatrixSpace(Zmod(9), 2, 2)
536
sage: G = MatrixGroup([M(x) for x in [[1,1,0,1],[1,3,0,1],[1,0,3,1],[4,0,0,7]]]); G
537
Matrix group over Ring of integers modulo 9 with 4 generators (
538
[1 1] [1 3] [1 0] [4 0]
539
[0 1], [0 1], [3 1], [0 7]
540
)
541
sage: sage.modular.arithgroup.congroup_generic._minimize_level(G)
542
Matrix group over Ring of integers modulo 3 with 1 generators (
543
[1 1]
544
[0 1]
545
)
546
sage: G = MatrixGroup([M(x) for x in [[1,3,0,1],[1,0,3,1],[4,0,0,7]]]); G
547
Matrix group over Ring of integers modulo 9 with 3 generators (
548
[1 3] [1 0] [4 0]
549
[0 1], [3 1], [0 7]
550
)
551
sage: sage.modular.arithgroup.congroup_generic._minimize_level(G)
552
3
553
"""
554
from congroup_gamma import Gamma_constructor as Gamma
555
Glist = list(G)
556
N = G.base_ring().characteristic()
557
i = Gamma(N).index()
558
559
for p in N.prime_divisors():
560
j = Gamma(N // p).index()
561
k = len([g for g in Glist if g.matrix().change_ring(Zmod(N // p)) == 1])
562
if k == i // j:
563
if N // p == 1:
564
return ZZ(1)
565
H = MatrixGroup([g.matrix().change_ring(Zmod(N//p)) for g in G.gens()])
566
return _minimize_level(H)
567
568
# now sanitize the generators (remove duplicates and copies of the identity)
569
new_gens = [x.matrix() for x in G.gens() if x.matrix() != 1]
570
all([x.set_immutable() for x in new_gens])
571
new_gens = list(Set(new_gens))
572
if new_gens == []:
573
return ZZ(G.base_ring().characteristic())
574
return MatrixGroup(new_gens)
575
576