Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/modular/arithgroup/congroup_pyx.pyx
8820 views
1
"""
2
Cython helper functions for congruence subgroups
3
4
This file contains optimized Cython implementations of a few functions related
5
to the standard congruence subgroups `\Gamma_0, \Gamma_1, \Gamma_H`. These
6
functions are for internal use by routines elsewhere in the Sage library.
7
"""
8
9
################################################################################
10
#
11
# Copyright (C) 2009, The Sage Group -- http://www.sagemath.org/
12
#
13
# Distributed under the terms of the GNU General Public License (GPL)
14
#
15
# The full text of the GPL is available at:
16
#
17
# http://www.gnu.org/licenses/
18
#
19
################################################################################
20
21
import random
22
from congroup_gamma1 import Gamma1_constructor as Gamma1
23
from congroup_gamma0 import Gamma0_constructor as Gamma0
24
25
import sage.rings.arith
26
27
cimport sage.rings.fast_arith
28
import sage.rings.fast_arith
29
cdef sage.rings.fast_arith.arith_int arith_int
30
arith_int = sage.rings.fast_arith.arith_int()
31
from sage.matrix.matrix_integer_2x2 cimport Matrix_integer_2x2
32
from sage.modular.modsym.p1list import lift_to_sl2z
33
34
include "sage/ext/cdefs.pxi"
35
include 'sage/ext/stdsage.pxi'
36
37
38
# This is the C version of a function formerly implemented in python in
39
# sage.modular.congroup. It is orders of magnitude faster (e.g., 30
40
# times). The key speedup is in replacing looping through the
41
# elements of the Python list R with looping through the elements of a
42
# C-array.
43
44
def degeneracy_coset_representatives_gamma0(int N, int M, int t):
45
r"""
46
Let `N` be a positive integer and `M` a divisor of `N`. Let `t` be a
47
divisor of `N/M`, and let `T` be the `2 \times 2` matrix `(1, 0; 0, t)`.
48
This function returns representatives for the orbit set `\Gamma_0(N)
49
\backslash T \Gamma_0(M)`, where `\Gamma_0(N)` acts on the left on `T
50
\Gamma_0(M)`.
51
52
INPUT:
53
54
- ``N`` -- int
55
- ``M`` -- int (divisor of `N`)
56
- ``t`` -- int (divisor of `N/M`)
57
58
OUTPUT:
59
60
list -- list of lists ``[a,b,c,d]``, where ``[a,b,c,d]`` should be viewed
61
as a 2x2 matrix.
62
63
This function is used for computation of degeneracy maps between
64
spaces of modular symbols, hence its name.
65
66
We use that `T^{-1} \cdot (a,b;c,d) \cdot T = (a,bt; c/t,d)`, that the
67
group `T^{-1} \Gamma_0(N) T` is contained in `\Gamma_0(M)`, and that
68
`\Gamma_0(N) T` is contained in `T \Gamma_0(M)`.
69
70
ALGORITHM:
71
72
1. Compute representatives for $\Gamma_0(N/t,t)$ inside of $\Gamma_0(M)$:
73
74
+ COSET EQUIVALENCE: Two right cosets represented by `[a,b;c,d]` and
75
`[a',b';c',d']` of `\Gamma_0(N/t,t)` in `{\rm SL}_2(\ZZ)` are equivalent if
76
and only if `(a,b)=(a',b')` as points of `\mathbf{P}^1(\ZZ/t\ZZ)`,
77
i.e., `ab' \cong ba' \pmod{t}`, and `(c,d) = (c',d')` as points of
78
`\mathbf{P}^1(\ZZ/(N/t)\ZZ)`.
79
80
+ ALGORITHM to list all cosets:
81
82
a) Compute the number of cosets.
83
b) Compute a random element `x` of `\Gamma_0(M)`.
84
c) Check if x is equivalent to anything generated so far; if not, add x
85
to the list.
86
d) Continue until the list is as long as the bound
87
computed in step (a).
88
89
2. There is a bijection between `\Gamma_0(N)\backslash T \Gamma_0(M)` and
90
`\Gamma_0(N/t,t) \backslash \Gamma_0(M)` given by `T r \leftrightarrow
91
r`. Consequently we obtain coset representatives for
92
`\Gamma_0(N)\backslash T \Gamma_0(M)` by left multiplying by `T` each
93
coset representative of `\Gamma_0(N/t,t) \backslash \Gamma_0(M)` found
94
in step 1.
95
96
EXAMPLES::
97
98
sage: from sage.modular.arithgroup.all import degeneracy_coset_representatives_gamma0
99
sage: len(degeneracy_coset_representatives_gamma0(13, 1, 1))
100
14
101
sage: len(degeneracy_coset_representatives_gamma0(13, 13, 1))
102
1
103
sage: len(degeneracy_coset_representatives_gamma0(13, 1, 13))
104
14
105
"""
106
if N % M != 0:
107
raise ArithmeticError, "M (=%s) must be a divisor of N (=%s)"%(M,N)
108
109
if (N/M) % t != 0:
110
raise ArithmeticError, "t (=%s) must be a divisor of N/M (=%s)"%(t,N/M)
111
112
cdef int n, i, j, k, aa, bb, cc, dd, g, Ndivt, halfmax, is_new
113
cdef int* R
114
115
# total number of coset representatives that we'll find
116
n = Gamma0(N).index() / Gamma0(M).index()
117
k = 0 # number found so far
118
Ndivt = N / t
119
R = <int*> sage_malloc(sizeof(int) * (4*n))
120
if R == <int*>0:
121
raise MemoryError
122
halfmax = 2*(n+10)
123
while k < n:
124
# try to find another coset representative.
125
cc = M*random.randrange(-halfmax, halfmax+1)
126
dd = random.randrange(-halfmax, halfmax+1)
127
g = arith_int.c_xgcd_int(-cc,dd,&bb,&aa)
128
if g == 0: continue
129
cc = cc / g
130
if cc % M != 0: continue
131
dd = dd / g
132
# Test if we've found a new coset representative.
133
is_new = 1
134
for i from 0 <= i < k:
135
j = 4*i
136
if (R[j+1]*aa - R[j]*bb)%t == 0 and \
137
(R[j+3]*cc - R[j+2]*dd)%Ndivt == 0:
138
is_new = 0
139
break
140
# If our matrix is new add it to the list.
141
if is_new:
142
R[4*k] = aa
143
R[4*k+1] = bb
144
R[4*k+2] = cc
145
R[4*k+3] = dd
146
k = k + 1
147
148
# Return the list left multiplied by T.
149
S = []
150
for i from 0 <= i < k:
151
j = 4*i
152
S.append([R[j], R[j+1], R[j+2]*t, R[j+3]*t])
153
sage_free(R)
154
return S
155
156
def degeneracy_coset_representatives_gamma1(int N, int M, int t):
157
r"""
158
Let `N` be a positive integer and `M` a divisor of `N`. Let `t` be a
159
divisor of `N/M`, and let `T` be the `2 \times 2` matrix `(1,0; 0,t)`.
160
This function returns representatives for the orbit set `\Gamma_1(N)
161
\backslash T \Gamma_1(M)`, where `\Gamma_1(N)` acts on the left on `T
162
\Gamma_1(M)`.
163
164
INPUT:
165
166
- ``N`` -- int
167
- ``M`` -- int (divisor of `N`)
168
- ``t`` -- int (divisor of `N/M`)
169
170
OUTPUT:
171
172
list -- list of lists ``[a,b,c,d]``, where ``[a,b,c,d]`` should be viewed
173
as a 2x2 matrix.
174
175
This function is used for computation of degeneracy maps between
176
spaces of modular symbols, hence its name.
177
178
ALGORITHM:
179
180
Everything is the same as for
181
:func:`~degeneracy_coset_representatives_gamma0`, except for coset
182
equivalence. Here `\Gamma_1(N/t,t)` consists of matrices that are of the
183
form `(1,*; 0,1) \bmod N/t` and `(1,0; *,1) \bmod t`.
184
185
COSET EQUIVALENCE: Two right cosets represented by `[a,b;c,d]` and
186
`[a',b';c',d']` of `\Gamma_1(N/t,t)` in `{\rm SL}_2(\ZZ)` are equivalent if
187
and only if
188
189
.. math::
190
191
a \cong a' \pmod{t},
192
b \cong b' \pmod{t},
193
c \cong c' \pmod{N/t},
194
d \cong d' \pmod{N/t}.
195
196
EXAMPLES::
197
198
sage: from sage.modular.arithgroup.all import degeneracy_coset_representatives_gamma1
199
sage: len(degeneracy_coset_representatives_gamma1(13, 1, 1))
200
168
201
sage: len(degeneracy_coset_representatives_gamma1(13, 13, 1))
202
1
203
sage: len(degeneracy_coset_representatives_gamma1(13, 1, 13))
204
168
205
"""
206
207
if N % M != 0:
208
raise ArithmeticError, "M (=%s) must be a divisor of N (=%s)"%(M,N)
209
210
if (N/M) % t != 0:
211
raise ArithmeticError, "t (=%s) must be a divisor of N/M (=%s)"%(t,N/M)
212
213
cdef int d, g, i, j, k, n, aa, bb, cc, dd, Ndivt, halfmax, is_new
214
cdef int* R
215
216
217
# total number of coset representatives that we'll find
218
n = Gamma1(N).index() / Gamma1(M).index()
219
d = arith_int.c_gcd_int(t, N/t)
220
n = n / d
221
k = 0 # number found so far
222
Ndivt = N / t
223
R = <int*> sage_malloc(sizeof(int) * (4*n))
224
if R == <int*>0:
225
raise MemoryError
226
halfmax = 2*(n+10)
227
while k < n:
228
# try to find another coset representative.
229
cc = M*random.randrange(-halfmax, halfmax+1)
230
dd = 1 + M*random.randrange(-halfmax, halfmax+1)
231
g = arith_int.c_xgcd_int(-cc,dd,&bb,&aa)
232
if g == 0: continue
233
cc = cc / g
234
if cc % M != 0: continue
235
dd = dd / g
236
if M != 1 and dd % M != 1: continue
237
# Test if we've found a new coset representative.
238
is_new = 1
239
for i from 0 <= i < k:
240
j = 4*i
241
if (R[j] - aa)%t == 0 and \
242
(R[j+1] - bb)%t == 0 and \
243
(R[j+2] - cc)%(Ndivt) == 0 and \
244
(R[j+3] - dd)%(Ndivt) == 0:
245
is_new = 0
246
break
247
# If our matrix is new add it to the list.
248
if is_new:
249
if k > n:
250
sage_free(R)
251
raise RuntimeError, "bug!!"
252
R[4*k] = aa
253
R[4*k+1] = bb
254
R[4*k+2] = cc
255
R[4*k+3] = dd
256
k = k + 1
257
258
# Return the list left multiplied by T.
259
S = []
260
for i from 0 <= i < k:
261
j = 4*i
262
S.append([R[j], R[j+1], R[j+2]*t, R[j+3]*t])
263
sage_free(R)
264
return S
265
266
def generators_helper(coset_reps, level, Mat2Z):
267
r"""
268
Helper function for generators of Gamma0, Gamma1 and GammaH.
269
270
These are computed using coset representatives, via an "inverse
271
Todd-Coxeter" algorithm, and generators for `{\rm SL}_2(\ZZ)`.
272
273
ALGORITHM: Given coset representatives for a finite index
274
subgroup `G` of `{\rm SL}_2(\ZZ)` we compute generators for `G` as follows.
275
Let `R` be a set of coset representatives for `G`. Let `S, T \in {\rm
276
SL}_2(\ZZ)` be defined by `(0,-1; 1,0)` and `(1,1,0,1)`, respectively.
277
Define maps `s, t: R \to G` as follows. If `r \in R`, then there exists a
278
unique `r' \in R` such that `GrS = Gr'`. Let `s(r) = rSr'^{-1}`. Likewise,
279
there is a unique `r'` such that `GrT = Gr'` and we let `t(r) = rTr'^{-1}`.
280
Note that `s(r)` and `t(r)` are in `G` for all `r`. Then `G` is generated
281
by `s(R)\cup t(R)`.
282
283
There are more sophisticated algorithms using group actions on trees (and
284
Farey symbols) that give smaller generating sets -- this code is now
285
deprecated in favour of the newer implementation based on Farey symbols.
286
287
EXAMPLES::
288
289
sage: Gamma0(7).generators(algorithm="todd-coxeter") # indirect doctest
290
[
291
[1 1] [-1 0] [ 1 -1] [1 0] [1 1] [-3 -1] [-2 -1] [-5 -1]
292
[0 1], [ 0 -1], [ 0 1], [7 1], [0 1], [ 7 2], [ 7 3], [21 4],
293
<BLANKLINE>
294
[-4 -1] [-1 0] [ 1 0]
295
[21 5], [ 7 -1], [-7 1]
296
]
297
"""
298
cdef Matrix_integer_2x2 S,T,I,x,y,z,v,vSmod,vTmod
299
300
S = Matrix_integer_2x2(Mat2Z,[0,-1,1,0],False,True)
301
T = Matrix_integer_2x2(Mat2Z,[1,1,0,1],False,True)
302
I = Matrix_integer_2x2(Mat2Z,[1,0,0,1],False,True)
303
304
crs = coset_reps.list()
305
# print [type(lift_to_sl2z(c, d, level)) for c,d in crs]
306
try:
307
reps = [Matrix_integer_2x2(Mat2Z,lift_to_sl2z(c, d, level),False,True) for c,d in crs]
308
except Exception:
309
raise ArithmeticError, "Error lifting to SL2Z: level=%s crs=%s" % (level, crs)
310
ans = []
311
for i from 0 <= i < len(crs):
312
x = reps[i]
313
v = Matrix_integer_2x2(Mat2Z,[crs[i][0],crs[i][1],0,0],False,True)
314
vSmod = (v*S)
315
vTmod = (v*T)
316
y_index = coset_reps.normalize(vSmod[0,0],vSmod[0,1])
317
z_index = coset_reps.normalize(vTmod[0,0],vTmod[0,1])
318
y_index = crs.index(y_index)
319
z_index = crs.index(z_index)
320
y = reps[y_index]
321
z = reps[z_index]
322
y = y._invert_unit()
323
z = z._invert_unit()
324
ans.append(x*S*y)
325
ans.append(x*T*z)
326
output = []
327
for x in ans:
328
if (x[0,0] != 1) or \
329
(x[0,1] != 0) or \
330
(x[1,0] != 0) or \
331
(x[1,1] != 1):
332
output.append(x)
333
# should be able to do something like:
334
# ans = [x for x in ans if x != I]
335
# however, this raises a somewhat mysterious error:
336
# <type 'exceptions.SystemError'>: error return without exception set
337
return output
338
339
340
341