Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/modular/arithgroup/congroup_gamma.py
4079 views
1
r"""
2
Congruence Subgroup `\Gamma(N)`
3
"""
4
5
################################################################################
6
#
7
# Copyright (C) 2009, The Sage Group -- http://www.sagemath.org/
8
#
9
# Distributed under the terms of the GNU General Public License (GPL)
10
#
11
# The full text of the GPL is available at:
12
#
13
# http://www.gnu.org/licenses/
14
#
15
################################################################################
16
17
from congroup_generic import CongruenceSubgroup
18
from arithgroup_element import ArithmeticSubgroupElement
19
from sage.misc.misc import prod
20
from sage.rings.all import ZZ, Zmod, gcd, QQ
21
from sage.rings.integer import GCD_list
22
from sage.groups.matrix_gps.matrix_group import MatrixGroup
23
from sage.matrix.constructor import matrix
24
from sage.modular.cusps import Cusp
25
26
_gamma_cache = {}
27
def Gamma_constructor(N):
28
r"""
29
Return the congruence subgroup `\Gamma(N)`.
30
31
EXAMPLES::
32
33
sage: Gamma(5) # indirect doctest
34
Congruence Subgroup Gamma(5)
35
sage: G = Gamma(23)
36
sage: G is Gamma(23)
37
True
38
sage: G == loads(dumps(G))
39
True
40
sage: G is loads(dumps(G))
41
True
42
"""
43
from all import SL2Z
44
if N == 1: return SL2Z
45
try:
46
return _gamma_cache[N]
47
except KeyError:
48
_gamma_cache[N] = Gamma_class(N)
49
return _gamma_cache[N]
50
51
class Gamma_class(CongruenceSubgroup):
52
r"""
53
The principal congruence subgroup `\Gamma(N)`.
54
"""
55
56
57
def _repr_(self):
58
"""
59
Return the string representation of self.
60
61
EXAMPLES::
62
63
sage: Gamma(133)._repr_()
64
'Congruence Subgroup Gamma(133)'
65
"""
66
return "Congruence Subgroup Gamma(%s)"%self.level()
67
68
def __reduce__(self):
69
"""
70
Used for pickling self.
71
72
EXAMPLES::
73
74
sage: Gamma(5).__reduce__()
75
(<function Gamma_constructor at ...>, (5,))
76
"""
77
return Gamma_constructor, (self.level(),)
78
79
def __cmp__(self, other):
80
r"""
81
Compare self to other.
82
83
EXAMPLES::
84
85
sage: Gamma(3) == SymmetricGroup(8)
86
False
87
sage: Gamma(3) == Gamma1(3)
88
False
89
sage: Gamma(5) < Gamma(6)
90
True
91
sage: Gamma(5) == Gamma(5)
92
True
93
"""
94
if not is_Gamma(other):
95
return cmp(type(self), type(other))
96
else:
97
return cmp(self.level(), other.level())
98
99
def index(self):
100
r"""
101
Return the index of self in the full modular group. This is given by
102
103
.. math::
104
105
\prod_{\substack{p \mid N \\ \text{$p$ prime}}}\left(p^{3e}-p^{3e-2}\right).
106
107
EXAMPLE::
108
sage: [Gamma(n).index() for n in [1..19]]
109
[1, 6, 24, 48, 120, 144, 336, 384, 648, 720, 1320, 1152, 2184, 2016, 2880, 3072, 4896, 3888, 6840]
110
sage: Gamma(32041).index()
111
32893086819240
112
"""
113
return prod([p**(3*e-2)*(p*p-1) for (p,e) in self.level().factor()])
114
115
def __call__(self, x, check=True):
116
r"""
117
Create an element of this congruence subgroup from x.
118
119
If the optional flag check is True (default), check whether
120
x actually gives an element of self.
121
122
EXAMPLES::
123
124
sage: G = Gamma(5)
125
sage: G([1, 0, -10, 1])
126
[ 1 0]
127
[-10 1]
128
sage: G(matrix(ZZ, 2, [26, 5, 5, 1]))
129
[26 5]
130
[ 5 1]
131
sage: G([1, 1, 6, 7])
132
Traceback (most recent call last):
133
...
134
TypeError: matrix must have diagonal entries (=1, 7) congruent to 1
135
modulo 5, and off-diagonal entries (=1,6) divisible by 5
136
"""
137
from all import SL2Z
138
x = SL2Z(x, check)
139
if not check:
140
return x
141
142
a = x.a()
143
b = x.b()
144
c = x.c()
145
d = x.d()
146
N = self.level()
147
if (a%N == 1) and (c%N == 0) and (d%N == 1) and (b%N == 0):
148
return x
149
else:
150
raise TypeError, "matrix must have diagonal entries (=%s, %s)\
151
congruent to 1 modulo %s, and off-diagonal entries (=%s,%s)\
152
divisible by %s" %(a, d, N, b, c, N)
153
154
def ncusps(self):
155
r"""
156
Return the number of cusps of this subgroup `\Gamma(N)`.
157
158
EXAMPLES::
159
160
sage: [Gamma(n).ncusps() for n in [1..19]]
161
[1, 3, 4, 6, 12, 12, 24, 24, 36, 36, 60, 48, 84, 72, 96, 96, 144, 108, 180]
162
sage: Gamma(30030).ncusps()
163
278691840
164
sage: Gamma(2^30).ncusps()
165
432345564227567616
166
"""
167
n = self.level()
168
if n==1:
169
return ZZ(1)
170
if n==2:
171
return ZZ(3)
172
return prod([p**(2*e) - p**(2*e-2) for (p,e) in n.factor()])//2
173
174
def nirregcusps(self):
175
r"""
176
Return the number of irregular cusps of self. For principal congruence subgroups this is always 0.
177
178
EXAMPLE::
179
180
sage: Gamma(17).nirregcusps()
181
0
182
"""
183
return 0
184
185
def _find_cusps(self):
186
r"""
187
Calculate the reduced representatives of the equivalence classes of
188
cusps for this group. Adapted from code by Ron Evans.
189
190
EXAMPLE::
191
192
sage: Gamma(8).cusps() # indirect doctest
193
[0, 1/4, 1/3, 3/8, 1/2, 2/3, 3/4, 1, 4/3, 3/2, 5/3, 2, 7/3, 5/2, 8/3, 3, 7/2, 11/3, 4, 14/3, 5, 6, 7, Infinity]
194
"""
195
n = self.level()
196
C=[QQ(x) for x in xrange(n)]
197
198
n0=n//2
199
n1=(n+1)//2
200
201
for r in xrange(1, n1):
202
if r > 1 and gcd(r,n)==1:
203
C.append(ZZ(r)/ZZ(n))
204
if n0==n/2 and gcd(r,n0)==1:
205
C.append(ZZ(r)/ZZ(n0))
206
207
for s in xrange(2,n1):
208
for r in xrange(1, 1+n):
209
if GCD_list([s,r,n])==1:
210
# GCD_list is ~40x faster than gcd, since gcd wastes loads
211
# of time initialising a Sequence type.
212
u,v = _lift_pair(r,s,n)
213
C.append(ZZ(u)/ZZ(v))
214
215
return [Cusp(x) for x in sorted(C)] + [Cusp(1,0)]
216
217
def reduce_cusp(self, c):
218
r"""
219
Calculate the unique reduced representative of the equivalence of the
220
cusp `c` modulo this group. The reduced representative of an
221
equivalence class is the unique cusp in the class of the form `u/v`
222
with `u, v \ge 0` coprime, `v` minimal, and `u` minimal for that `v`.
223
224
EXAMPLES::
225
226
sage: Gamma(5).reduce_cusp(1/5)
227
Infinity
228
sage: Gamma(5).reduce_cusp(7/8)
229
3/2
230
sage: Gamma(6).reduce_cusp(4/3)
231
2/3
232
233
TESTS::
234
235
sage: G = Gamma(50); all([c == G.reduce_cusp(c) for c in G.cusps()])
236
True
237
"""
238
N = self.level()
239
c = Cusp(c)
240
u,v = c.numerator() % N, c.denominator() % N
241
if (v > N//2) or (2*v == N and u > N//2):
242
u,v = -u,-v
243
u,v = _lift_pair(u,v,N)
244
return Cusp(u,v)
245
246
def are_equivalent(self, x, y, trans=False):
247
r"""
248
Check if the cusps `x` and `y` are equivalent under the action of this group.
249
250
ALGORITHM: The cusps `u_1 / v_1` and `u_2 / v_2` are equivalent modulo
251
`\Gamma(N)` if and only if `(u_1, v_1) = \pm (u_2, v_2) \bmod N`.
252
253
EXAMPLE::
254
255
sage: Gamma(7).are_equivalent(Cusp(2/3), Cusp(5/4))
256
True
257
"""
258
if trans:
259
return CongruenceSubgroup.are_equivalent(self, x,y,trans=trans)
260
N = self.level()
261
u1,v1 = (x.numerator() % N, x.denominator() % N)
262
u2,v2 = (y.numerator(), y.denominator())
263
264
return ((u1,v1) == (u2 % N, v2 % N)) or ((u1,v1) == (-u2 % N, -v2 % N))
265
266
def nu3(self):
267
r"""
268
Return the number of elliptic points of order 3 for this arithmetic
269
subgroup. Since this subgroup is `\Gamma(N)` for `N \ge 2`, there are
270
no such points, so we return 0.
271
272
EXAMPLE::
273
274
sage: Gamma(89).nu3()
275
0
276
"""
277
return 0
278
279
# We don't need to override nu2, since the default nu2 implementation knows
280
# that nu2 = 0 for odd subgroups.
281
282
def image_mod_n(self):
283
r"""
284
Return the image of this group modulo `N`, as a subgroup of `SL(2, \ZZ
285
/ N\ZZ)`. This is just the trivial subgroup.
286
287
EXAMPLE::
288
289
sage: Gamma(3).image_mod_n()
290
Matrix group over Ring of integers modulo 3 with 1 generators:
291
[[[1, 0], [0, 1]]]
292
"""
293
return MatrixGroup([matrix(Zmod(self.level()), 2, 2, 1)])
294
295
296
def is_Gamma(x):
297
r"""
298
Return True if x is a congruence subgroup of type Gamma.
299
300
EXAMPLES::
301
302
sage: from sage.modular.arithgroup.all import is_Gamma
303
sage: is_Gamma(Gamma0(13))
304
False
305
sage: is_Gamma(Gamma(4))
306
True
307
"""
308
309
return isinstance(x, Gamma_class)
310
311
def _lift_pair(U,V,N):
312
r"""
313
Utility function. Given integers ``U, V, N``, with `N \ge 1` and `{\rm
314
gcd}(U, V, N) = 1`, return a pair `(u, v)` congruent to `(U, V) \bmod N`,
315
such that `{\rm gcd}(u,v) = 1`, `u, v \ge 0`, `v` is as small as possible,
316
and `u` is as small as possible for that `v`.
317
318
*Warning*: As this function is for internal use, it does not do a
319
preliminary sanity check on its input, for efficiency. It will recover
320
reasonably gracefully if ``(U, V, N)`` are not coprime, but only after
321
wasting quite a lot of cycles!
322
323
EXAMPLE::
324
325
sage: from sage.modular.arithgroup.congroup_gamma import _lift_pair
326
sage: _lift_pair(2,4,7)
327
(9, 4)
328
sage: _lift_pair(2,4,8) # don't do this
329
Traceback (most recent call last):
330
...
331
ValueError: (U, V, N) must be coprime
332
"""
333
u = U % N
334
v = V % N
335
if v == 0:
336
if u == 1:
337
return (1,0)
338
else:
339
v = N
340
while gcd(u, v) > 1:
341
u = u+N
342
if u > N*v: raise ValueError, "(U, V, N) must be coprime"
343
return (u, v)
344
345