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