Path: blob/master/src/sage/modular/arithgroup/congroup_pyx.pyx
8820 views
"""1Cython helper functions for congruence subgroups23This file contains optimized Cython implementations of a few functions related4to the standard congruence subgroups `\Gamma_0, \Gamma_1, \Gamma_H`. These5functions are for internal use by routines elsewhere in the Sage library.6"""78################################################################################9#10# Copyright (C) 2009, The Sage Group -- http://www.sagemath.org/11#12# Distributed under the terms of the GNU General Public License (GPL)13#14# The full text of the GPL is available at:15#16# http://www.gnu.org/licenses/17#18################################################################################1920import random21from congroup_gamma1 import Gamma1_constructor as Gamma122from congroup_gamma0 import Gamma0_constructor as Gamma02324import sage.rings.arith2526cimport sage.rings.fast_arith27import sage.rings.fast_arith28cdef sage.rings.fast_arith.arith_int arith_int29arith_int = sage.rings.fast_arith.arith_int()30from sage.matrix.matrix_integer_2x2 cimport Matrix_integer_2x231from sage.modular.modsym.p1list import lift_to_sl2z3233include "sage/ext/cdefs.pxi"34include 'sage/ext/stdsage.pxi'353637# This is the C version of a function formerly implemented in python in38# sage.modular.congroup. It is orders of magnitude faster (e.g., 3039# times). The key speedup is in replacing looping through the40# elements of the Python list R with looping through the elements of a41# C-array.4243def degeneracy_coset_representatives_gamma0(int N, int M, int t):44r"""45Let `N` be a positive integer and `M` a divisor of `N`. Let `t` be a46divisor of `N/M`, and let `T` be the `2 \times 2` matrix `(1, 0; 0, t)`.47This function returns representatives for the orbit set `\Gamma_0(N)48\backslash T \Gamma_0(M)`, where `\Gamma_0(N)` acts on the left on `T49\Gamma_0(M)`.5051INPUT:5253- ``N`` -- int54- ``M`` -- int (divisor of `N`)55- ``t`` -- int (divisor of `N/M`)5657OUTPUT:5859list -- list of lists ``[a,b,c,d]``, where ``[a,b,c,d]`` should be viewed60as a 2x2 matrix.6162This function is used for computation of degeneracy maps between63spaces of modular symbols, hence its name.6465We use that `T^{-1} \cdot (a,b;c,d) \cdot T = (a,bt; c/t,d)`, that the66group `T^{-1} \Gamma_0(N) T` is contained in `\Gamma_0(M)`, and that67`\Gamma_0(N) T` is contained in `T \Gamma_0(M)`.6869ALGORITHM:70711. Compute representatives for $\Gamma_0(N/t,t)$ inside of $\Gamma_0(M)$:7273+ COSET EQUIVALENCE: Two right cosets represented by `[a,b;c,d]` and74`[a',b';c',d']` of `\Gamma_0(N/t,t)` in `{\rm SL}_2(\ZZ)` are equivalent if75and only if `(a,b)=(a',b')` as points of `\mathbf{P}^1(\ZZ/t\ZZ)`,76i.e., `ab' \cong ba' \pmod{t}`, and `(c,d) = (c',d')` as points of77`\mathbf{P}^1(\ZZ/(N/t)\ZZ)`.7879+ ALGORITHM to list all cosets:8081a) Compute the number of cosets.82b) Compute a random element `x` of `\Gamma_0(M)`.83c) Check if x is equivalent to anything generated so far; if not, add x84to the list.85d) Continue until the list is as long as the bound86computed in step (a).87882. There is a bijection between `\Gamma_0(N)\backslash T \Gamma_0(M)` and89`\Gamma_0(N/t,t) \backslash \Gamma_0(M)` given by `T r \leftrightarrow90r`. Consequently we obtain coset representatives for91`\Gamma_0(N)\backslash T \Gamma_0(M)` by left multiplying by `T` each92coset representative of `\Gamma_0(N/t,t) \backslash \Gamma_0(M)` found93in step 1.9495EXAMPLES::9697sage: from sage.modular.arithgroup.all import degeneracy_coset_representatives_gamma098sage: len(degeneracy_coset_representatives_gamma0(13, 1, 1))9914100sage: len(degeneracy_coset_representatives_gamma0(13, 13, 1))1011102sage: len(degeneracy_coset_representatives_gamma0(13, 1, 13))10314104"""105if N % M != 0:106raise ArithmeticError, "M (=%s) must be a divisor of N (=%s)"%(M,N)107108if (N/M) % t != 0:109raise ArithmeticError, "t (=%s) must be a divisor of N/M (=%s)"%(t,N/M)110111cdef int n, i, j, k, aa, bb, cc, dd, g, Ndivt, halfmax, is_new112cdef int* R113114# total number of coset representatives that we'll find115n = Gamma0(N).index() / Gamma0(M).index()116k = 0 # number found so far117Ndivt = N / t118R = <int*> sage_malloc(sizeof(int) * (4*n))119if R == <int*>0:120raise MemoryError121halfmax = 2*(n+10)122while k < n:123# try to find another coset representative.124cc = M*random.randrange(-halfmax, halfmax+1)125dd = random.randrange(-halfmax, halfmax+1)126g = arith_int.c_xgcd_int(-cc,dd,&bb,&aa)127if g == 0: continue128cc = cc / g129if cc % M != 0: continue130dd = dd / g131# Test if we've found a new coset representative.132is_new = 1133for i from 0 <= i < k:134j = 4*i135if (R[j+1]*aa - R[j]*bb)%t == 0 and \136(R[j+3]*cc - R[j+2]*dd)%Ndivt == 0:137is_new = 0138break139# If our matrix is new add it to the list.140if is_new:141R[4*k] = aa142R[4*k+1] = bb143R[4*k+2] = cc144R[4*k+3] = dd145k = k + 1146147# Return the list left multiplied by T.148S = []149for i from 0 <= i < k:150j = 4*i151S.append([R[j], R[j+1], R[j+2]*t, R[j+3]*t])152sage_free(R)153return S154155def degeneracy_coset_representatives_gamma1(int N, int M, int t):156r"""157Let `N` be a positive integer and `M` a divisor of `N`. Let `t` be a158divisor of `N/M`, and let `T` be the `2 \times 2` matrix `(1,0; 0,t)`.159This function returns representatives for the orbit set `\Gamma_1(N)160\backslash T \Gamma_1(M)`, where `\Gamma_1(N)` acts on the left on `T161\Gamma_1(M)`.162163INPUT:164165- ``N`` -- int166- ``M`` -- int (divisor of `N`)167- ``t`` -- int (divisor of `N/M`)168169OUTPUT:170171list -- list of lists ``[a,b,c,d]``, where ``[a,b,c,d]`` should be viewed172as a 2x2 matrix.173174This function is used for computation of degeneracy maps between175spaces of modular symbols, hence its name.176177ALGORITHM:178179Everything is the same as for180:func:`~degeneracy_coset_representatives_gamma0`, except for coset181equivalence. Here `\Gamma_1(N/t,t)` consists of matrices that are of the182form `(1,*; 0,1) \bmod N/t` and `(1,0; *,1) \bmod t`.183184COSET EQUIVALENCE: Two right cosets represented by `[a,b;c,d]` and185`[a',b';c',d']` of `\Gamma_1(N/t,t)` in `{\rm SL}_2(\ZZ)` are equivalent if186and only if187188.. math::189190a \cong a' \pmod{t},191b \cong b' \pmod{t},192c \cong c' \pmod{N/t},193d \cong d' \pmod{N/t}.194195EXAMPLES::196197sage: from sage.modular.arithgroup.all import degeneracy_coset_representatives_gamma1198sage: len(degeneracy_coset_representatives_gamma1(13, 1, 1))199168200sage: len(degeneracy_coset_representatives_gamma1(13, 13, 1))2011202sage: len(degeneracy_coset_representatives_gamma1(13, 1, 13))203168204"""205206if N % M != 0:207raise ArithmeticError, "M (=%s) must be a divisor of N (=%s)"%(M,N)208209if (N/M) % t != 0:210raise ArithmeticError, "t (=%s) must be a divisor of N/M (=%s)"%(t,N/M)211212cdef int d, g, i, j, k, n, aa, bb, cc, dd, Ndivt, halfmax, is_new213cdef int* R214215216# total number of coset representatives that we'll find217n = Gamma1(N).index() / Gamma1(M).index()218d = arith_int.c_gcd_int(t, N/t)219n = n / d220k = 0 # number found so far221Ndivt = N / t222R = <int*> sage_malloc(sizeof(int) * (4*n))223if R == <int*>0:224raise MemoryError225halfmax = 2*(n+10)226while k < n:227# try to find another coset representative.228cc = M*random.randrange(-halfmax, halfmax+1)229dd = 1 + M*random.randrange(-halfmax, halfmax+1)230g = arith_int.c_xgcd_int(-cc,dd,&bb,&aa)231if g == 0: continue232cc = cc / g233if cc % M != 0: continue234dd = dd / g235if M != 1 and dd % M != 1: continue236# Test if we've found a new coset representative.237is_new = 1238for i from 0 <= i < k:239j = 4*i240if (R[j] - aa)%t == 0 and \241(R[j+1] - bb)%t == 0 and \242(R[j+2] - cc)%(Ndivt) == 0 and \243(R[j+3] - dd)%(Ndivt) == 0:244is_new = 0245break246# If our matrix is new add it to the list.247if is_new:248if k > n:249sage_free(R)250raise RuntimeError, "bug!!"251R[4*k] = aa252R[4*k+1] = bb253R[4*k+2] = cc254R[4*k+3] = dd255k = k + 1256257# Return the list left multiplied by T.258S = []259for i from 0 <= i < k:260j = 4*i261S.append([R[j], R[j+1], R[j+2]*t, R[j+3]*t])262sage_free(R)263return S264265def generators_helper(coset_reps, level, Mat2Z):266r"""267Helper function for generators of Gamma0, Gamma1 and GammaH.268269These are computed using coset representatives, via an "inverse270Todd-Coxeter" algorithm, and generators for `{\rm SL}_2(\ZZ)`.271272ALGORITHM: Given coset representatives for a finite index273subgroup `G` of `{\rm SL}_2(\ZZ)` we compute generators for `G` as follows.274Let `R` be a set of coset representatives for `G`. Let `S, T \in {\rm275SL}_2(\ZZ)` be defined by `(0,-1; 1,0)` and `(1,1,0,1)`, respectively.276Define maps `s, t: R \to G` as follows. If `r \in R`, then there exists a277unique `r' \in R` such that `GrS = Gr'`. Let `s(r) = rSr'^{-1}`. Likewise,278there is a unique `r'` such that `GrT = Gr'` and we let `t(r) = rTr'^{-1}`.279Note that `s(r)` and `t(r)` are in `G` for all `r`. Then `G` is generated280by `s(R)\cup t(R)`.281282There are more sophisticated algorithms using group actions on trees (and283Farey symbols) that give smaller generating sets -- this code is now284deprecated in favour of the newer implementation based on Farey symbols.285286EXAMPLES::287288sage: Gamma0(7).generators(algorithm="todd-coxeter") # indirect doctest289[290[1 1] [-1 0] [ 1 -1] [1 0] [1 1] [-3 -1] [-2 -1] [-5 -1]291[0 1], [ 0 -1], [ 0 1], [7 1], [0 1], [ 7 2], [ 7 3], [21 4],292<BLANKLINE>293[-4 -1] [-1 0] [ 1 0]294[21 5], [ 7 -1], [-7 1]295]296"""297cdef Matrix_integer_2x2 S,T,I,x,y,z,v,vSmod,vTmod298299S = Matrix_integer_2x2(Mat2Z,[0,-1,1,0],False,True)300T = Matrix_integer_2x2(Mat2Z,[1,1,0,1],False,True)301I = Matrix_integer_2x2(Mat2Z,[1,0,0,1],False,True)302303crs = coset_reps.list()304# print [type(lift_to_sl2z(c, d, level)) for c,d in crs]305try:306reps = [Matrix_integer_2x2(Mat2Z,lift_to_sl2z(c, d, level),False,True) for c,d in crs]307except Exception:308raise ArithmeticError, "Error lifting to SL2Z: level=%s crs=%s" % (level, crs)309ans = []310for i from 0 <= i < len(crs):311x = reps[i]312v = Matrix_integer_2x2(Mat2Z,[crs[i][0],crs[i][1],0,0],False,True)313vSmod = (v*S)314vTmod = (v*T)315y_index = coset_reps.normalize(vSmod[0,0],vSmod[0,1])316z_index = coset_reps.normalize(vTmod[0,0],vTmod[0,1])317y_index = crs.index(y_index)318z_index = crs.index(z_index)319y = reps[y_index]320z = reps[z_index]321y = y._invert_unit()322z = z._invert_unit()323ans.append(x*S*y)324ans.append(x*T*z)325output = []326for x in ans:327if (x[0,0] != 1) or \328(x[0,1] != 0) or \329(x[1,0] != 0) or \330(x[1,1] != 1):331output.append(x)332# should be able to do something like:333# ans = [x for x in ans if x != I]334# however, this raises a somewhat mysterious error:335# <type 'exceptions.SystemError'>: error return without exception set336return output337338339340341