Path: blob/master/sage/modular/arithgroup/arithgroup_perm.py
4057 views
r"""1Arithmetic subgroups defined by permutations of cosets23A subgroup of finite index `H` of a finitely generated group `G` is completely4described by the action of a set of generators of `G` on the right cosets `H5\backslash G = \{Hg\}_{g \in G}`. After some arbitrary choice of numbering one6can identify the action of generators as elements of a symmetric group acting7transitively (and satisfying the relations of the relators in G). As `{\rm8SL}_2(\ZZ)` has a very simple presentation as a central extension of a free9product of cyclic groups, one can easily design algorithms from this point of10view.1112The generators of `{\rm SL}_2(\ZZ)` used in this module are named as follows `s_2`,13`s_3`, `l`, `r` which are defined by1415.. MATH::1617s_2 = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix},\quad18s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad19l = \begin{pmatrix} 1 & 1 \\ 0 & 1 \end{pmatrix},\quad20r = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix}.2122Those generators satisfy the following relations2324.. MATH::2526s_2^2 = s_3^3 = -1, \quad r = s_2^{-1}\ l^{-1}\ s_2.2728In particular not all four are needed to generate the whole group `{\rm29SL}_2(\ZZ)`. Three couples which generate `{\rm SL}_2(\ZZ)` are of particular30interest:3132- `(l,r)` as they are also semigroup generators for the semigroup of matrices33in `{\rm SL}_2(\ZZ)` with non-negative entries,34- `(l,s_2)` as they are closely related to the continued fraction algorithm,35- `(s_2,s_3)` as the group `{\rm PSL}_2(\ZZ)` is the free product of the finite36cyclic groups generated by these two elements.3738Part of these functions are based on Chris Kurth's *KFarey* package [Kur08]_.39For tests see the file ``sage.modular.arithgroup.tests``.4041REFERENCES:4243.. [AtSD71] A. O. L. Atkin and H. P. F. Swinnerton-Dyer, "Modular forms on44noncongruence subgroups", Proc. Symp. Pure Math., Combinatorics (T. S. Motzkin,45ed.), vol. 19, AMS, Providence 19714647.. [Hsu96] Tim Hsu, "Identifying congruence subgroups of the modular group",48Proc. AMS 124, no. 5, 1351-1359 (1996)4950.. [Hsu97] Tim Hsu, "Permutation techniques for coset representations of modular51subgroups", in L. Schneps (ed.), Geometric Galois Actions II: Dessins52d'Enfants, Mapping Class Groups and Moduli, volume 243 of LMS Lect. Notes,5367-77, Cambridge Univ. Press (1997)5455.. [Go09] Alexey G. Gorinov, "Combinatorics of double cosets and fundamental56domains for the subgroups of the modular group", preprint57http://arxiv.org/abs/0901.13405859.. [KSV11] Ian Kiming, Matthias Schuett and Helena Verrill, "Lifts of60projective congruence groups", J. London Math. Soc. (2011) 83 (1): 96-120,61http://dx.doi.org/10.1112/jlms/jdq062. Arxiv version:62http://arxiv.org/abs/0905.4798.6364.. [Kul91] Ravi Kulkarni "An arithmetic geometric method in the study of the65subgroups of the modular group", American Journal of Mathematics 113 (1991),66no 6, 1053-11336768.. [Kur08] Chris Kurth, "K Farey package for Sage",69http://www.public.iastate.edu/~kurthc/research/index.html7071.. [KuLo] Chris Kurth and Ling Long, "Computations with finite index subgroups72of `{\rm PSL}_2(\ZZ)` using Farey symbols"7374.. [Ve] Helena Verrill, "Fundamental domain drawer", Java program,75http://www.math.lsu.edu/~verrill/7677TODO:7879- modular Farey symbols8081- computation of generators of a modular subgroup with a standard surface82group presentation. In other words, compute a presentation of the form8384.. MATH::8586\langle x_i,y_i,c_j |\ \prod_i [x_i,y_i] \prod_j c_j^{\nu_j} = 1\rangle8788where the elements `x_i` and `y_i` are hyperbolic and `c_j` are parabolic89(`\nu_j=\infty`) or elliptic elements (`\nu_j < \infty`).9091- computation of centralizer.9293- generation of modular (even) subgroups of fixed index.9495AUTHORS:9697- Chris Kurth (2008): created KFarey package9899- David Loeffler (2009): adapted functions from KFarey for inclusion into Sage100101- Vincent Delecroix (2010): implementation for odd groups, new design,102improvements, documentation103104- David Loeffler (2011): congruence testing for odd subgroups, enumeration of105liftings of projective subgroups106"""107108################################################################################109#110# Copyright (C) 2009, The Sage Group -- http://www.sagemath.org/111#112# Distributed under the terms of the GNU General Public License (GPL)113#114# The full text of the GPL is available at:115#116# http://www.gnu.org/licenses/117#118################################################################################119120from all import SL2Z121from arithgroup_generic import ArithmeticSubgroup122from arithgroup_element import ArithmeticSubgroupElement123from sage.rings.all import Zmod, ZZ124from sage.rings.infinity import Infinity125from sage.misc.cachefunc import cached_method126import sage.rings.arith as arith127128from sage.groups.perm_gps.permgroup_element import PermutationGroupElement129130131Idm = SL2Z([1,0,0,1]) # identity132133Lm = SL2Z([1,1,0,1]) # parabolic that fixes infinity134Rm = SL2Z([1,0,1,1]) # parabolic that fixes 0135S2m = SL2Z([0,-1,1,0]) # elliptic of order 2 (fix i)136S3m = SL2Z([0,1,-1,1]) # elliptic of order 3 (fix j)137138S2mi = SL2Z([0,1,-1,0]) # the inverse of S2m in SL(2,Z)139S3mi = SL2Z([1,-1,1,0]) # the inverse of S3m in SL(2,Z)140Lmi = SL2Z([1,-1,0,1]) # the inverse of Lm in SL(2,Z)141Rmi = SL2Z([1,0,-1,1]) # the inverse of Rm in SL(2,Z)142143def sl2z_word_problem(A):144r"""145Given an element of `{\rm SL}_2(\ZZ)`, express it as a word in the generators L =146[1,1,0,1] and R = [1,0,1,1].147148The return format is a list of pairs ``(a,b)``, where ``a = 0`` or ``1``149denoting ``L`` or ``R`` respectively, and ``b`` is an integer exponent.150151See also the function :func:`eval_sl2z_word`.152153EXAMPLES::154155sage: from sage.modular.arithgroup.arithgroup_perm import eval_sl2z_word, sl2z_word_problem156sage: m = SL2Z([1,0,0,1])157sage: eval_sl2z_word(sl2z_word_problem(m)) == m158True159sage: m = SL2Z([0,-1,1,0])160sage: eval_sl2z_word(sl2z_word_problem(m)) == m161True162sage: m = SL2Z([7,8,-50,-57])163sage: eval_sl2z_word(sl2z_word_problem(m)) == m164True165"""166A = SL2Z(A)167output=[]168169## If A00 is zero170if A[0,0]==0:171c=A[1,1]172if c != 1:173A=A*Lm**(c-1)*Rm*Lmi174output.extend([(0,1-c),(1,-1),(0,1)])175else:176A=A*Rm*Lmi177output.extend([(1,-1),(0,1)])178179if A[0,0]<0: # Make sure A00 is positive180A=SL2Z(-1)*A181output.extend([(1,-1), (0,1), (1,-1), (0,1), (1,-1), (0,1)])182183if A[0,1]<0: # if A01 is negative make it positive184n=(-A[0,1]/A[0,0]).ceil() #n s.t. 0 <= A[0,1]+n*A[0,0] < A[0,0]185A=A*Lm**n186output.append((0, -n))187## At this point A00>0 and A01>=0188while not (A[0,0]==0 or A[0,1]==0):189if A[0,0]>A[0,1]:190n=(A[0,0]/A[0,1]).floor()191A=A*SL2Z([1,0,-n,1])192output.append((1, n))193194else: #A[0,0]<=A[0,1]195n=(A[0,1]/A[0,0]).floor()196A=A*SL2Z([1,-n,0,1])197output.append((0, n))198199if A==SL2Z(1):200pass #done, so don't add R^0201elif A[0,0]==0:202c=A[1,1]203if c != 1:204A=A*Lm**(c-1)*Rm*Lmi205output.extend([(0,1-c),(1,-1),(0, 1)])206else:207A=A*Rm*Lmi208output.extend([(1,-1),(0,1)])209else:210c=A[1,0]211if c:212A=A*Rm**(-c)213output.append((1,c))214215output.reverse()216return output217218def eval_sl2z_word(w):219r"""220Given a word in the format output by :func:`sl2z_word_problem`, convert it back221into an element of `{\rm SL}_2(\ZZ)`.222223EXAMPLES::224225sage: from sage.modular.arithgroup.arithgroup_perm import eval_sl2z_word226sage: eval_sl2z_word([(0, 1), (1, -1), (0, 0), (1, 3), (0, 2), (1, 9), (0, -1)])227[ 66 -59]228[ 47 -42]229"""230from sage.all import prod231mat = [Lm,Rm]232w0 = Idm233w1 = w234return w0 * prod((mat[a[0]]**a[1] for a in w1), Idm)235236def word_of_perms(w, p1, p2):237r"""238Given a word `w` as a list of 2-tuples ``(index,power)`` and permutations239``p1`` and ``p2`` return the product of ``p1`` and ``p2`` that corresponds240to ``w``.241242EXAMPLES::243244sage: import sage.modular.arithgroup.arithgroup_perm as ap245sage: S2 = SymmetricGroup(4)246sage: p1 = S2('(1,2)(3,4)')247sage: p2 = S2('(1,2,3,4)')248sage: ap.word_of_perms([(1,1),(0,1)], p1, p2) == p2 * p1249True250sage: ap.word_of_perms([(0,1),(1,1)], p1, p2) == p1 * p2251True252"""253if not isinstance(p1,PermutationGroupElement):254p1 = PermutationGroupElement(p1)255if not isinstance(p2,PermutationGroupElement):256p2 = PermutationGroupElement(p2)257258G = p1.parent()259if G != p2.parent(): # find a minimal parent260G2 = p2.parent()261if G.has_coerce_map_from(G2):262p2 = G(p2)263elif G2.has_coerce_map_from(G):264G = G2265p1 = G(p1)266else:267from sage.groups.perm_gps.all import PermutationGroup268G = PermutationGroup([p1,p2])269p1 = G(p1)270p2 = G(p2)271272M = G.identity()273p = [p1, p2]274m = [p1.order(),p2.order()]275276for i,j in w:277M *= p[i]**(j%m[i])278279return M280281def _equalize_perms(l):282r"""283Transform a list of lists into a list of lists with identical length. Each284list ``p`` in the argument is completed with ``range(len(p),n)`` where285``n`` is the maximal length of the lists in ``l``. Note that the lists are286modified in-place (rather than returning new lists).287288EXAMPLES::289290sage: from sage.modular.arithgroup.arithgroup_perm import _equalize_perms291sage: l = [[],[1,0],[3,0,1,2]]292sage: _equalize_perms(l)293sage: l294[[0, 1, 2, 3], [1, 0, 2, 3], [3, 0, 1, 2]]295"""296n=max(map(len,l))297if n ==0:298n = 1299for p in l:300p.extend(xrange(len(p),n))301302# Tedious point: in order to unpickle pickled objects from prior to patch303# #11422, this function needs to accept two non-keyword arguments, to be304# interpreted as L and R. Hence the order of the arguments is slightly305# different from the class __init__ methods.306307def ArithmeticSubgroup_Permutation(308L=None, R=None, S2=None, S3=None,309relabel=False,310check=True):311r"""312Construct a subgroup of `{\rm SL}_2(\ZZ)` from the action of generators on its313right cosets.314315Return an arithmetic subgroup knowing the action, given by permutations, of316at least two standard generators on the its cosets. The generators317considered are the following matrices:318319.. math::320321s_2 = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix},\quad322s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad323l = \begin{pmatrix} 1 & 1 \\ 0 & 1\end{pmatrix},\quad324r = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix}.325326An error will be raised if only one permutation is given. If no arguments327are given at all, the full modular group `{\rm SL}(2, \ZZ)` is returned.328329INPUT:330331- ``S2``, ``S3``, ``L``, ``R`` - permutations - action of matrices on the332right cosets (each coset is identified to an element of `\{1,\dots,n\}`333where `1` is reserved for the identity coset).334335- ``relabel`` - boolean (default: False) - if True, renumber the cosets in a336canonical way.337338- ``check`` - boolean (default: True) - check that the input is valid (it339may be time efficient but less safe to set it to False)340341EXAMPLES::342343sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,3)")344sage: G345Arithmetic subgroup with permutations of right cosets346S2=(1,2)(3,4)347S3=(1,2,3)348L=(1,4,3)349R=(2,4,3)350sage: G.index()3514352353sage: G = ArithmeticSubgroup_Permutation(); G354Arithmetic subgroup with permutations of right cosets355S2=()356S3=()357L=()358R=()359sage: G == SL2Z360True361362Some invalid inputs::363364sage: ArithmeticSubgroup_Permutation(S2="(1,2)")365Traceback (most recent call last):366...367ValueError: Need at least two generators368sage: ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(3,4,5)")369Traceback (most recent call last):370...371ValueError: Permutations do not generate a transitive group372sage: ArithmeticSubgroup_Permutation(L="(1,2)",R="(1,2,3)")373Traceback (most recent call last):374...375ValueError: Wrong relations between generators376sage: ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="()")377Traceback (most recent call last):378...379ValueError: S2^2 does not equal to S3^3380sage: ArithmeticSubgroup_Permutation(S2="(1,4,2,5,3)", S3="(1,3,5,2,4)")381Traceback (most recent call last):382...383ValueError: S2^2 = S3^3 must have order 1 or 2384385The input checks can be disabled for speed::386387sage: ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(3,4,5)", check=False) # don't do this!388Arithmetic subgroup with permutations of right cosets389S2=(1,2)390S3=(3,4,5)391L=(1,2)(3,5,4)392R=(1,2)(3,4,5)393"""394gens = filter(lambda x: x is not None, [S2,S3,L,R])395if len(gens) == 0:396S2 = S3 = L = R = ''397elif len(gens) < 2:398raise ValueError, "Need at least two generators"399400if S2 is not None:401S2 = PermutationGroupElement(S2,check=check)402if S3 is not None:403S3 = PermutationGroupElement(S3,check=check)404if L is not None:405L = PermutationGroupElement(L,check=check)406if R is not None:407R = PermutationGroupElement(R,check=check)408409if L is not None:410if R is not None: # initialize from L,R411if S2 is None:412S2 = R * ~L * R413if S3 is None:414S3 = L * ~R415elif S2 is not None: # initialize from L,S2416if S3 is None:417S3 = ~S2 * ~L418if R is None:419R = ~S2 * ~L * S2420elif S3 is not None: # initialize from L,S3421if S2 is None:422S2 = ~L * ~S3423if R is None:424R = S3 * ~L * ~S3425elif R is not None:426if S2 is not None: # initialize from R, S2427if L is None:428L = ~S2 * ~R * S2429if S3 is None:430S3 = R * ~S2431elif S3 is not None: # initialize from R, S3432if L is None:433L = ~S3 * ~R * S3434if S2 is None:435S2 = ~S3 * R436else: # intialize from S2, S3437if L is None:438L = ~S3 * ~S2439if R is None:440R = S3 * S2441442if check and (L != ~S3 * ~S2 or R != S3 * S2):443raise ValueError, "Wrong relations between generators"444445inv = S2*S2446447if check:448if inv != S3*S3*S3:449raise ValueError, "S2^2 does not equal to S3^3"450elif not (inv*inv).is_one():451raise ValueError, "S2^2 = S3^3 must have order 1 or 2"452453# Check transitivity. This is the most expensive check, so we do it454# last.455from sage.groups.perm_gps.all import PermutationGroup456457G = PermutationGroup(gens)458if not G.is_transitive():459raise ValueError, "Permutations do not generate a transitive group"460461s2 = [i-1 for i in S2.list()]462s3 = [i-1 for i in S3.list()]463l = [i-1 for i in L.list()]464r = [i-1 for i in R.list()]465_equalize_perms((s2,s3,l,r))466467if inv.is_one(): # the group is even468G = EvenArithmeticSubgroup_Permutation(s2,s3,l,r)469else: # the group is odd470G = OddArithmeticSubgroup_Permutation(s2,s3,l,r)471472if relabel:473G.relabel()474475return G476477class ArithmeticSubgroup_Permutation_class(ArithmeticSubgroup):478r"""479A subgroup of `{\rm SL}_2(\ZZ)` defined by the action of generators on its480coset graph.481482The class stores the action of generators `s_2, s_3, l, r` on right cosets483`Hg` of a finite index subgroup `H < {\rm SL}_2(\ZZ)`. In particular the action of484`{\rm SL}_2(\ZZ)` on the cosets is on right.485486.. MATH::487488s_2 = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix},\quad489s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad490l = \begin{pmatrix} 1 & 1 \\ 0 & 1\end{pmatrix},\quad491r = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix}.492493TEST::494495sage: s2 = PermutationGroupElement('(1,2)(3,4)(5,6)')496sage: s3 = PermutationGroupElement('(1,3,5)(2,4,6)')497sage: G = ArithmeticSubgroup_Permutation(S2=s2, S3=s3)498sage: G.S2() == s2499True500sage: G.S3() == s3501True502sage: G == ArithmeticSubgroup_Permutation(L=G.L(), R=G.R())503True504sage: G == ArithmeticSubgroup_Permutation(L=G.L(), S2=G.S2())505True506sage: G == ArithmeticSubgroup_Permutation(L=G.L(), S3=G.S3())507True508sage: G == ArithmeticSubgroup_Permutation(R=G.R(), S2=G.S2())509True510sage: G == ArithmeticSubgroup_Permutation(R=G.R(), S3=G.S3())511True512sage: G == ArithmeticSubgroup_Permutation(S2=G.S2(), S3=G.S3())513True514515sage: G = ArithmeticSubgroup_Permutation(S2='',S3='')516sage: G == loads(dumps(G))517True518519sage: S2 = '(1,2)(3,4)(5,6)'520sage: S3 = '(1,2,3)(4,5,6)'521sage: G = ArithmeticSubgroup_Permutation(S2=S2, S3=S3)522sage: loads(dumps(G)) == G523True524"""525def __eq__(self, other):526r"""527Equality test.528529TESTS::530531sage: G2 = Gamma(2)532sage: G3 = Gamma(3)533sage: H = ArithmeticSubgroup_Permutation(S2="(1,4)(2,6)(3,5)",S3="(1,2,3)(4,5,6)")534sage: (G2 == H) and (H == G2)535True536sage: (G3 == H) or (H == G3)537False538539sage: G2 = Gamma1(2)540sage: G3 = Gamma1(3)541sage: H = ArithmeticSubgroup_Permutation(S2="(1,6,4,3)(2,7,5,8)",S3="(1,2,3,4,5,6)(7,8)")542sage: (G2 == H) or (H == G2)543False544sage: (G3 == H) and (H == G3)545True546"""547if isinstance(other, ArithmeticSubgroup_Permutation_class):548549return (self.is_odd() == other.is_odd() and550self.index() == other.index() and551self.relabel(inplace=False)._S2 == other.relabel(inplace=False)._S2 and552self.relabel(inplace=False)._S3 == other.relabel(inplace=False)._S3)553554elif isinstance(other, ArithmeticSubgroup):555return self == other.as_permutation_group()556557raise NotImplemented558559def __hash__(self):560r"""561Return a hash value.562563TESTS::564565sage: G1 = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)(5,6)',S3='(1,2,3)(4,5,6)')566sage: G2 = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)(5,6)',S3='(1,5,6)(4,2,3)')567sage: G1.__hash__() == G2.__hash__()568False569"""570return hash((tuple(self.relabel(inplace=False)._S2),tuple(self.relabel(inplace=False)._S3)))571572def _repr_(self):573r"""574String representation of self.575576EXAMPLES::577578sage: G = Gamma(2).as_permutation_group()579sage: repr(G) #indirect doctest580'Arithmetic subgroup with permutations of right cosets\n S2=(1,4)(2,6)(3,5)\n S3=(1,2,3)(4,5,6)\n L=(1,5)(2,4)(3,6)\n R=(1,6)(2,5)(3,4)'581sage: G = Gamma(3).as_permutation_group()582sage: repr(G) #indirect doctest583'Arithmetic subgroup of index 24'584"""585if self.index() < 20:586return "Arithmetic subgroup with permutations of right cosets\n S2=%s\n S3=%s\n L=%s\n R=%s" %(587self.S2(), self.S3(), self.L(), self.R())588589else:590return "Arithmetic subgroup of index %d" %self.index()591592#593# Attribute access594#595596def S2(self):597r"""598Return the action of the matrix `s_2` as a permutation of cosets.599600.. MATH::601602s_2 = \begin{pmatrix}0&-1\\1&0\end{pmatrix}603604EXAMPLES::605606sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")607sage: G.S2()608(1,2)609"""610return PermutationGroupElement([i+1 for i in self._S2], check=False)611612def S3(self):613r"""614Return the action of the matrix `s_3` as a permutation of cosets.615616.. MATH::617618s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad619620EXAMPLES::621622sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")623sage: G.S3()624(1,2,3)625"""626627return PermutationGroupElement([i+1 for i in self._S3], check=False)628629def L(self):630r"""631Return the action of the matrix `l` as a permutation of cosets.632633.. MATH::634635l = \begin{pmatrix}1&1\\0&1\end{pmatrix}636637EXAMPLES::638639sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")640sage: G.L()641(1,3)642"""643return PermutationGroupElement([i+1 for i in self._L], check=False)644645def R(self):646r"""647Return the action of the matrix `r` as a permutation of cosets.648649.. MATH::650651r = \begin{pmatrix}1&0\\1&1\end{pmatrix}652653EXAMPLES::654655sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")656sage: G.R()657(2,3)658"""659return PermutationGroupElement([i+1 for i in self._R], check=False)660661def perm_group(self):662r"""663Return the underlying permutation group.664665The permutation group returned is isomorphic to the action of the666generators `s_2` (element of order two), `s_3` (element of order 3), `l`667(parabolic element) and `r` (parabolic element) on right cosets (the668action is on the right).669670EXAMPLE::671672sage: import sage.modular.arithgroup.arithgroup_perm as ap673sage: ap.HsuExample10().perm_group()674Permutation Group with generators [(1,2)(3,4)(5,6)(7,8)(9,10), (1,4)(2,5,9,10,8)(3,7,6), (1,7,9,10,6)(2,3)(4,5,8), (1,8,3)(2,4,6)(5,7,10)]675"""676from sage.groups.perm_gps.all import PermutationGroup677return PermutationGroup([self.S2(), self.S3(), self.L(), self.R()])678679def index(self):680r"""681Returns the index of this modular subgroup in the full modular group.682683EXAMPLES::684685sage: G = Gamma(2)686sage: P = G.as_permutation_group()687sage: P.index()6886689sage: G.index() == P.index()690True691692sage: G = Gamma0(8)693sage: P = G.as_permutation_group()694sage: P.index()69512696sage: G.index() == P.index()697True698699sage: G = Gamma1(6)700sage: P = G.as_permutation_group()701sage: P.index()70224703sage: G.index() == P.index()704True705"""706return len(self._S2)707708#709# Canonical renumbering710#711712def relabel(self, inplace=True):713r"""714Relabel the cosets of this modular subgroup in a canonical way.715716The implementation of modular subgroup by action of generators on cosets717depends on the choice of a numbering. This function provides718canonical labels in the sense that two equal subgroups whith different719labels are relabeled the same way. The default implementation relabels720the group itself. If you want to get a relabeled copy of your modular721subgroup, put to ``False`` the option ``inplace``.722723ALGORITHM:724725We give an overview of how the canonical labels for the modular subgroup726are built. The procedure only uses the permutations `S3` and `S2` that727define the modular subgroup and can be used to renumber any728transitive action of the symmetric group. In other words, the algorithm729construct a canonical representative of a transitive subgroup in its730conjugacy class in the full symmetric group.7317321. The identity is still numbered `0` and set the curent vertex to be733the identity.7347352. Number the cycle of `S3` the current vertex belongs to: if the736current vertex is labeled `n` then the numbering in such way that the737cycle becomes `(n, n+1, \ldots, n+k)`).7387393. Find a new current vertex using the permutation `S2`.740If all vertices are relabeled then it's done, otherwise go to step 2.741742EXAMPLES::743744sage: S2 = "(1,2)(3,4)(5,6)"; S3 = "(1,2,3)(4,5,6)"745sage: G1 = ArithmeticSubgroup_Permutation(S2=S2,S3=S3); G1746Arithmetic subgroup with permutations of right cosets747S2=(1,2)(3,4)(5,6)748S3=(1,2,3)(4,5,6)749L=(1,4,5,3)750R=(2,4,6,3)751sage: G1.relabel(); G1752Arithmetic subgroup with permutations of right cosets753S2=(1,2)(3,4)(5,6)754S3=(1,2,3)(4,5,6)755L=(1,4,5,3)756R=(2,4,6,3)757758sage: S2 = "(1,2)(3,5)(4,6)"; S3 = "(1,2,3)(4,5,6)"759sage: G2 = ArithmeticSubgroup_Permutation(S2=S2,S3=S3); G2760Arithmetic subgroup with permutations of right cosets761S2=(1,2)(3,5)(4,6)762S3=(1,2,3)(4,5,6)763L=(1,5,6,3)764R=(2,5,4,3)765sage: G2.relabel(); G2766Arithmetic subgroup with permutations of right cosets767S2=(1,2)(3,4)(5,6)768S3=(1,2,3)(4,5,6)769L=(1,4,5,3)770R=(2,4,6,3)771772sage: S2 = "(1,2)(3,6)(4,5)"; S3 = "(1,2,3)(4,5,6)"773sage: G3 = ArithmeticSubgroup_Permutation(S2=S2,S3=S3); G3774Arithmetic subgroup with permutations of right cosets775S2=(1,2)(3,6)(4,5)776S3=(1,2,3)(4,5,6)777L=(1,6,4,3)778R=(2,6,5,3)779sage: G4 = G3.relabel(inplace=False)780sage: G4781Arithmetic subgroup with permutations of right cosets782S2=(1,2)(3,4)(5,6)783S3=(1,2,3)(4,5,6)784L=(1,4,5,3)785R=(2,4,6,3)786sage: G3 is G4787False788789TESTS::790791sage: S2 = "(1,2)(3,6)(4,5)"792sage: S3 = "(1,2,3)(4,5,6)"793sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)794sage: H = G.relabel(inplace=False)795sage: G is H796False797sage: G._S2 is H._S2 or G._S3 is H._S3 or G._L is H._L or G._R is H._R798False799sage: G.relabel(inplace=False) is H800True801sage: H.relabel(inplace=False) is H802True803sage: G.relabel(); G804Arithmetic subgroup with permutations of right cosets805S2=(1,2)(3,4)(5,6)806S3=(1,2,3)(4,5,6)807L=(1,4,5,3)808R=(2,4,6,3)809sage: G.relabel(inplace=False) is G810True811"""812if hasattr(self,'_canonical_label_group'):813if inplace:814if not (self is self._canonical_label_group):815self.__dict__ = self._canonical_label_group.__dict__816self._canonical_label_group = self817else:818return self._canonical_label_group819820if inplace:821G = self822else:823from copy import deepcopy824G = deepcopy(self)825826n = G.index()827mapping = G._canonical_rooted_labels()828S2 = G._S2829S3 = G._S3830L = G._L831R = G._R832G._S2 = [None]*n833G._S3 = [None]*n834G._L = [None]*n835G._R = [None]*n836837for i in xrange(n):838G._S2[mapping[i]] = mapping[S2[i]]839G._S3[mapping[i]] = mapping[S3[i]]840G._L[mapping[i]] = mapping[L[i]]841G._R[mapping[i]] = mapping[R[i]]842843self._canonical_label_group = G844G._canonical_label_group = G845846if not inplace:847return G848849def _canonical_unrooted_labels(self):850r"""851Returns the smallest label among rooted label852853OUTPUT:854855A 3-tuple of lists corresponding to permutations. The first list is the856mapping that gives the canonical labels and the second and third one857correspond to the permutations obtained by the conjugation of ``S2`` and858``S3``.859860EXAMPLES::861862sage: S2 = "(1,2)(4,5)"863sage: S3 = "(1,3,4)(2,5,6)"864sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)865sage: s2,s3 = G._S2,G._S3866sage: m,ss2,ss3 = G._canonical_unrooted_labels()867sage: all(ss2[m[i]] == m[s2[i]] for i in xrange(6))868True869sage: all(ss3[m[i]] == m[s3[i]] for i in xrange(6))870True871872sage: S2 = "(1,2)(3,4)(5,6)"873sage: S3 = "(1,3,4)(2,5,6)"874sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)875sage: s2,s3 = G._S2,G._S3876sage: m,ss2,ss3 = G._canonical_unrooted_labels()877sage: all(ss2[m[i]] == m[s2[i]] for i in xrange(6))878True879sage: all(ss3[m[i]] == m[s3[i]] for i in xrange(6))880True881882sage: S2 = "(1,2)(3,4)(5,6)"883sage: S3 = "(1,3,5)(2,4,6)"884sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)885sage: s2,s3 = G._S2,G._S3886sage: m,ss2,ss3 = G._canonical_unrooted_labels()887sage: all(ss2[m[i]] == m[s2[i]] for i in xrange(6))888True889sage: all(ss3[m[i]] == m[s3[i]] for i in xrange(6))890True891"""892n = self.index()893S2_win = [None]*n; S3_win = [None]*n894S2_test = [None]*n; S3_test = [None]*n895896m_win = self._canonical_rooted_labels(0)897for i in xrange(n): # conjugation898S2_win[m_win[i]] = m_win[self._S2[i]]899S3_win[m_win[i]] = m_win[self._S3[i]]900901for j0 in xrange(1,self.index()):902m_test = self._canonical_rooted_labels(j0)903for i in xrange(n):904S2_test[m_test[i]] = m_test[self._S2[i]]905S3_test[m_test[i]] = m_test[self._S3[i]]906907for i in xrange(n-1):908if (S2_test[i] < S2_win[i] or909(S2_test[i] == S2_win[i] and S3_test[i] < S3_win[i])):910S2_win,S2_test = S2_test,S2_win911S3_win,S3_test = S3_test,S3_win912m_win = m_test913break914915return m_win, S2_win, S3_win916917def _canonical_rooted_labels(self, j0=0):918r"""919Return the permutation which correspond to the renumbering of self in920order to get canonical labels.921922If ``j0`` is 0 then the renumbering corresponds to the same group. If923not, the renumbering corresponds to the conjugated subgroup such that924``j0`` becomes the identity coset.925926EXAMPLES::927928sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")929sage: G._canonical_rooted_labels(0)930[0, 1, 2]931sage: G._canonical_rooted_labels(1)932[2, 0, 1]933sage: G._canonical_rooted_labels(2)934[1, 2, 0]935"""936x = self._S3937y = self._S2938n = len(x)939940k = 0941seen = [True] * n942mapping = [None] * n943waiting = []944945while True:946# intialize at j0947mapping[j0] = k948waiting.append(j0)949k += 1950# complete x cycle from j0951j = x[j0]952while j != j0:953mapping[j] = k954waiting.append(j)955k += 1956j = x[j]957958# if everybody is labelled do not go further959if k == n: break960961# find another guy with y962j0 = y[waiting.pop(0)]963while mapping[j0] is not None:964j0 = y[waiting.pop(0)]965966return mapping967968#969# Contains and random element970#971972@cached_method973def _index_to_lr_cusp_width(self):974r"""975Precomputation of cusps data of self for this modular subgroup.976977This is a central precomputation for the ``.__contains__()`` method and978consists in two lists of positive integers ``lc`` and ``rc`` of length979the index of the subgroup. They are defined as follows: the number980``lc[i]`` (resp ``rc[i]``) is the lenth of the cycle of ``L`` (resp.981``R``) which contains ``i``.982983EXAMPLES::984985sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")986sage: G.L()987(1,3)988sage: G.R()989(2,3)990sage: G._index_to_lr_cusp_width()991([2, 1, 2], [1, 2, 2])992"""993G = self.relabel(inplace=False)994995l = G.L()996l_cycle_length = [None]*self.index()997for c in l.cycle_tuples(singletons=True):998for i in c:999l_cycle_length[i-1]=len(c)10001001r = G.R()1002r_cycle_length = [None]*self.index()1003for c in r.cycle_tuples(singletons=True):1004for i in c:1005r_cycle_length[i-1]=len(c)10061007return (l_cycle_length, r_cycle_length)10081009def __contains__(self, x):1010r"""1011Test whether ``x`` is in the group or not.10121013ALGORITHM:10141015An element of `{\rm SL}_2(\ZZ)` is in a given modular subgroup if it does not1016permute the identity coset!10171018TEST::10191020sage: G = Gamma(4)1021sage: m1 = G([1,4,0,1])1022sage: m2 = G([17,4,4,1])1023sage: m3 = G([1,-4,-4,17])1024sage: m4 = SL2Z([1,2,0,1])1025sage: P = G.as_permutation_group()1026sage: m1 in P1027True1028sage: m2 in P1029True1030sage: m3 in P1031True1032sage: m4 in P1033False1034"""1035#if x.parent() is self or x.parent() == self: return True1036if x not in SL2Z: return False10371038w = sl2z_word_problem(x)10391040perms = [self.relabel(inplace=False)._L,self.relabel(inplace=False)._R]1041widths = self._index_to_lr_cusp_width()10421043k = 01044for (i,j) in w:1045for _ in xrange(j % widths[i][k]):1046k = perms[i][k]10471048return not k10491050def random_element(self, initial_steps=30):1051r"""1052Returns a random element in this subgroup.10531054The algorithm uses a random walk on the Cayley graph of `{\rm SL}_2(\ZZ)` stopped1055at the first time it reaches the subgroup after at least1056``initial_steps`` steps.10571058INPUT:10591060- ``initial_steps`` - positive integer (default: 30)10611062EXAMPLES::10631064sage: G = ArithmeticSubgroup_Permutation(S2='(1,3)(4,5)',S3='(1,2,5)(3,4,6)')1065sage: all(G.random_element() in G for _ in xrange(10))1066True1067"""1068from sage.misc.prandom import randint10691070i = 01071m = SL2Z(1)1072for _ in xrange(initial_steps):1073j = randint(0,1)1074if j == 0:1075i = self._S2[i]1076m = m*S2m1077else:1078i = self._S3[i]1079m = m*S3m10801081while i != 0:1082j = randint(0,1)1083if j == 0:1084i = self._S2[i]1085m = m*S2m1086else:1087i = self._S3[i]1088m = m*S3m10891090return m10911092def permutation_action(self, x):1093r"""1094Given an element ``x`` of `{\rm SL}_2(\ZZ)`, compute the permutation of the1095cosets of self given by right multiplication by ``x``.10961097EXAMPLE::10981099sage: import sage.modular.arithgroup.arithgroup_perm as ap1100sage: ap.HsuExample10().permutation_action(SL2Z([32, -21, -67, 44]))1101(1,4,6,2,10,5,3,7,8,9)1102"""1103return word_of_perms(sl2z_word_problem(x), self.L(), self.R())11041105def __call__(self, g, check=True):1106r"""1107Create an element of this group from ``g``. If check=True (the default),1108perform the (possibly rather computationally-intensive) check to make1109sure ``g`` is in this group.11101111EXAMPLE::11121113sage: import sage.modular.arithgroup.arithgroup_perm as ap1114sage: m = SL2Z([1,1,0,1])1115sage: m in ap.HsuExample10()1116False1117sage: ap.HsuExample10()(m)1118Traceback (most recent call last):1119...1120TypeError: The element is not in group1121sage: ap.HsuExample10()(m, check=False)1122[1 1]1123[0 1]1124"""1125g = SL2Z(g, check=check)1126if not check or g in self:1127return g1128raise TypeError, "The element is not in group"11291130#1131# Group stuff1132#11331134def is_normal(self):1135r"""1136Test whether the group is normal11371138EXAMPLES::11391140sage: G = Gamma(2).as_permutation_group()1141sage: G.is_normal()1142True11431144sage: G = Gamma1(2).as_permutation_group()1145sage: G.is_normal()1146False1147"""1148N = self.index()1149G = self.relabel(inplace=False)1150s2 = G._S21151s3 = G._S31152ss2 = [None]*N1153ss3 = [None]*N1154for j in [s2[0],s3[0]]:1155m = G._canonical_rooted_labels(j)1156for i in xrange(N):1157ss2[m[i]] = m[s2[i]]1158ss3[m[i]] = m[s3[i]]1159if s2 != ss2 or s3 != ss3:1160return False1161return True11621163def _conjugate(self,j0):1164r"""1165Return the conjugate of self rooted at j0.11661167EXAMPLES::11681169sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)(4,5,6)')1170sage: G1171Arithmetic subgroup with permutations of right cosets1172S2=(1,2)(3,4)1173S3=(1,2,3)(4,5,6)1174L=(1,4,6,5,3)1175R=(2,4,5,6,3)1176sage: G._conjugate(0) == G1177True1178sage: G._conjugate(4)1179Arithmetic subgroup with permutations of right cosets1180S2=(3,4)(5,6)1181S3=(1,2,3)(4,5,6)1182L=(1,4,5,3,2)1183R=(1,2,4,6,3)1184"""1185N = self.index()1186s2 = self._S21187s3 = self._S31188l = self._L1189r = self._R1190ss2 = [None]*N1191ss3 = [None]*N1192ll = [None]*N1193rr = [None]*N11941195m = self._canonical_rooted_labels(j0)1196for i in xrange(N):1197ss2[m[i]] = m[s2[i]]1198ss3[m[i]] = m[s3[i]]1199ll[m[i]] = m[l[i]]1200rr[m[i]] = m[r[i]]1201return self.__class__(ss2,ss3,ll,rr,True)12021203def coset_graph(self,1204right_cosets=False,1205s2_edges=True, s3_edges=True, l_edges=False, r_edges=False,1206s2_label='s2', s3_label='s3', l_label='l', r_label='r'):1207r"""1208Return the right (or left) coset graph.12091210INPUT:12111212- ``right_cosets`` - bool (default: False) - right or left coset graph12131214- ``s2_edges`` - bool (default: True) - put edges associated to s212151216- ``s3_edges`` - bool (default: True) - put edges associated to s312171218- ``l_edges`` - bool (default: False) - put edges associated to l12191220- ``r_edges`` - bool (default: False) - put edges associated to r12211222- ``s2_label``, ``s3_label``, ``l_label``, ``r_label`` - the labels to1223put on the edges corresponding to the generators action. Use ``None``1224for no label.12251226EXAMPLES::12271228sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="()")1229sage: G1230Arithmetic subgroup with permutations of right cosets1231S2=(1,2)1232S3=()1233L=(1,2)1234R=(1,2)1235sage: G.index()123621237sage: G.coset_graph()1238Looped multi-digraph on 2 vertices1239"""1240from sage.graphs.digraph import DiGraph1241res = DiGraph(multiedges=True,loops=True)1242res.add_vertices(range(self.index()))124312441245if right_cosets: # invert the permutations1246S2 = [None]*self.index()1247S3 = [None]*self.index()1248L = [None]*self.index()1249R = [None]*self.index()1250for i in xrange(self.index()):1251S2[self._S2[i]] = i1252S3[self._S3[i]] = i1253L[self._L[i]] = i1254R[self._R[i]] = i12551256else:1257S2 = self._S21258S3 = self._S31259L = self._L1260R = self._R12611262if s2_edges:1263if s2_label is not None:1264res.add_edges((i,S2[i],s2_label) for i in xrange(self.index()))1265else:1266res.add_edges((i,S2[i]) for i in xrange(self.index()))12671268if s3_edges:1269if s3_label is not None:1270res.add_edges((i,S3[i],s3_label) for i in xrange(self.index()))1271else:1272res.add_edges((i,S3) for i in xrange(self.index()))12731274if l_edges:1275if l_label is not None:1276res.add_edges((i,L[i],l_label) for i in xrange(self.index()))1277else:1278res.add_edges((i,L[i]) for i in xrange(self.index()))12791280if r_edges:1281if r_label is not None:1282res.add_edges((i,R[i],r_label) for i in xrange(self.index()))1283else:1284res.add_edges((i,R[i]) for i in xrange(self.index()))12851286res.plot.options['color_by_label'] = True12871288if s2_label or s3_label or l_label or r_label:1289res.plot.options['edge_labels'] = True12901291return res12921293def generalised_level(self):1294r"""1295Return the generalised level of this subgroup.12961297The *generalised level* of a subgroup of the modular group is the least1298common multiple of the widths of the cusps. It was proven by Wohlfart1299that for even congruence subgroups, the (conventional) level coincides1300with the generalised level. For odd congruence subgroups the level is1301either the generalised level, or twice the generalised level [KSV11]_.13021303EXAMPLES::13041305sage: G = Gamma(2).as_permutation_group()1306sage: G.generalised_level()130721308sage: G = Gamma0(3).as_permutation_group()1309sage: G.generalised_level()131031311"""1312return arith.lcm(self.cusp_widths())13131314def congruence_closure(self):1315r"""1316Returns the smallest congruence subgroup containing self. If self is1317congruence, this is just self, but represented as a congruence subgroup1318data type. If self is not congruence, then it may be larger.13191320In practice, we use the following criterion: let `m` be the generalised1321level of self. If this subgroup is even, let `n = m`, else let `n =13222m`. Then any congruence subgroup containing self contains `\Gamma(n)`1323(a generalisation of Wohlfahrt's theorem due to Kiming, Verrill and1324Schuett). So we compute the image of self modulo `n` and return the1325preimage of that.13261327EXAMPLE::13281329sage: Gamma1(3).as_permutation_group().congruence_closure()1330Congruence subgroup of SL(2,Z) of level 3, preimage of:1331Matrix group over Ring of integers modulo 3 with 2 generators:1332[[[1, 2], [0, 1]], [[1, 1], [0, 1]]]1333sage: sage.modular.arithgroup.arithgroup_perm.HsuExample10().congruence_closure() # long time (40 sec)1334Modular Group SL(2,Z)1335"""1336if self.is_even():1337N = self.generalised_level()1338else:1339N = 2*self.generalised_level()13401341from congroup_generic import CongruenceSubgroup_constructor as CS1342return CS(N, [x.matrix() for x in self.gens()])13431344class OddArithmeticSubgroup_Permutation(ArithmeticSubgroup_Permutation_class):1345def __init__(self, S2, S3, L, R, canonical_labels=False):1346r"""1347TESTS::13481349sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")1350sage: G1351Arithmetic subgroup with permutations of right cosets1352S2=(1,2,3,4)1353S3=(1,3)(2,4)1354L=(1,2,3,4)1355R=(1,4,3,2)1356sage: loads(dumps(G)) == G1357True1358"""1359self._S2 = S21360self._S3 = S31361self._L = L1362self._R = R1363if canonical_labels:1364self._canonical_label_group = self13651366def __reduce__(self):1367r"""1368Return the data used to construct self. Used in pickling.13691370TESTS::13711372sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")1373sage: G == loads(dumps(G)) #indirect doctest1374True1375sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)",relabel=True)1376sage: GG = loads(dumps(G))1377sage: GG == G #indirect doctest1378True1379sage: GG.relabel(inplace=False) is GG1380True1381"""1382if hasattr(self,'_canonical_label_group'):1383canonical_labels = (self is self._canonical_label_group)1384else:1385canonical_labels = False1386return (OddArithmeticSubgroup_Permutation,1387(self._S2,self._S3,self._L,self._R,canonical_labels))13881389def is_odd(self):1390r"""1391Test whether the group is odd.13921393EXAMPLES::13941395sage: G = ArithmeticSubgroup_Permutation(S2="(1,6,4,3)(2,7,5,8)",S3="(1,2,3,4,5,6)(7,8)")1396sage: G.is_odd()1397True1398"""1399return True14001401def is_even(self):1402r"""1403Test whether the group is even.14041405EXAMPLES::14061407sage: G = ArithmeticSubgroup_Permutation(S2="(1,6,4,3)(2,7,5,8)",S3="(1,2,3,4,5,6)(7,8)")1408sage: G.is_even()1409False1410"""1411return False14121413def to_even_subgroup(self,relabel=True):1414r"""1415Returns the group with `-Id` added in it.14161417EXAMPLES::14181419sage: G = Gamma1(3).as_permutation_group()1420sage: G.to_even_subgroup()1421Arithmetic subgroup with permutations of right cosets1422S2=(1,3)(2,4)1423S3=(1,2,3)1424L=(2,3,4)1425R=(1,4,2)14261427sage: H = ArithmeticSubgroup_Permutation(S2 = '(1,4,11,14)(2,7,12,17)(3,5,13,15)(6,9,16,19)(8,10,18,20)', S3 = '(1,2,3,11,12,13)(4,5,6,14,15,16)(7,8,9,17,18,19)(10,20)')1428sage: G = H.to_even_subgroup(relabel=False); G1429Arithmetic subgroup with permutations of right cosets1430S2=(1,4)(2,7)(3,5)(6,9)(8,10)1431S3=(1,2,3)(4,5,6)(7,8,9)1432L=(1,5)(2,4,9,10,8)(3,7,6)1433R=(1,7,10,8,6)(2,5,9)(3,4)1434sage: H.is_subgroup(G)1435True1436"""1437N = self.index()14381439# build equivalence classes in e1440s2 = self._S21441e = []1442e2i = [None]*N1443for i in xrange(N):1444j = s2[s2[i]]1445if i < j:1446e2i[i] = e2i[j] = len(e)1447e.append((i,j))14481449# build the quotient permutations1450ss2 = [None]*(N/2)1451ss3 = [None]*(N/2)1452ll = [None]*(N/2)1453rr = [None]*(N/2)14541455s3 = self._S31456l = self._L1457r = self._R1458for (j0,j1) in e:1459ss2[e2i[j0]] = e2i[s2[j0]]1460ss3[e2i[j0]] = e2i[s3[j0]]1461ll[e2i[j0]] = e2i[l[j0]]1462rr[e2i[j0]] = e2i[r[j0]]14631464G = EvenArithmeticSubgroup_Permutation(ss2,ss3,ll,rr)1465if relabel:1466G.relabel()1467return G14681469def nu2(self):1470r"""1471Return the number of elliptic points of order 2.14721473EXAMPLES::14741475sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")1476sage: G.nu2()1477014781479sage: G = Gamma1(2).as_permutation_group()1480sage: G.nu2()148111482"""1483return sum(1 for c in self.S2().cycle_tuples() if len(c) == 2)14841485def nu3(self):1486r"""1487Return the number of elliptic points of order 3.14881489EXAMPLES::14901491sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")1492sage: G.nu3()1493214941495sage: G = Gamma1(3).as_permutation_group()1496sage: G.nu3()149711498"""1499return sum(1 for c in self.S3().cycle_tuples() if len(c) == 2)15001501def nirregcusps(self):1502r"""1503Return the number of irregular cusps.15041505The cusps are associated to cycles of the permutations `L` or `R`.1506The irregular cusps are the one which are stabilised by `-Id`.15071508EXAMPLES::15091510sage: S2 = "(1,3,2,4)(5,7,6,8)(9,11,10,12)"1511sage: S3 = "(1,3,5,2,4,6)(7,9,11,8,10,12)"1512sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)1513sage: G.nirregcusps()151431515"""1516inv = self.S2()**21517n = 01518for c in self.L().cycle_tuples(singletons=True):1519if inv(c[0]) in c:1520n += 11521return n15221523def nregcusps(self):1524r"""1525Return the number of regular cusps of the group.15261527The cusps are associated to cycles of `L` or `R`. The irregular cusps1528correspond to the ones which are not stabilised by `-Id`.15291530EXAMPLES::15311532sage: G = Gamma1(3).as_permutation_group()1533sage: G.nregcusps()153421535"""1536inv = self.S2()**21537n = 01538for c in self.L().cycle_tuples(singletons=True):1539if inv(c[0]) not in c:1540n += 11541return n//215421543def cusp_widths(self,exp=False):1544r"""1545Return the list of cusp widths.15461547INPUT:15481549``exp`` - boolean (default: False) - if True, return a dictionnary with1550keys the possible widths and with values the number of cusp with that1551width.15521553EXAMPLES::15541555sage: G = Gamma1(5).as_permutation_group()1556sage: G.cusp_widths()1557[1, 1, 5, 5]1558sage: G.cusp_widths(exp=True)1559{1: 2, 5: 2}1560"""1561inv = self.S2()**21562L = self.L()1563cusps = set(c[0] for c in L.cycle_tuples(singletons=True))1564if exp:1565widths = {}1566else:1567widths = []15681569while cusps:1570c0 = cusps.pop()1571c = L.orbit(c0)1572if inv(c0) not in c:1573c1 = min(L.orbit(inv(c0)))1574cusps.remove(c1)1575if exp:1576if not len(c) in widths:1577widths[len(c)] = 01578widths[len(c)] += 11579else:1580widths.append(len(c))1581else:1582if exp:1583if not len(c)/2 in widths:1584widths[len(c)/2] = 01585widths[len(c)/2] += 11586else:1587widths.append(len(c)/2)15881589if exp:1590return widths1591return sorted(widths)15921593def ncusps(self):1594r"""1595Returns the number of cusps.15961597EXAMPLES::15981599sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")1600sage: G.ncusps()1601116021603sage: G = Gamma1(3).as_permutation_group()1604sage: G.ncusps()160521606"""1607inv = self.S2()**21608n = 01609m = 01610for c in self.L().cycle_tuples(singletons=True):1611if inv(c[0]) in c:1612n += 11613else:1614m += 11615return n + m//216161617def is_congruence(self):1618r"""1619Test whether self is a congruence group, i.e.~whether or not it1620contains the subgroup `\Gamma(n)` for some `n`.16211622For odd groups, we first test whether the group generated by `G` and1623`-1` is congruence, and then use a theorem of Kiming, Schuett and1624Verrill [KSV11]_, which shows that an odd subgroup is congruence if and1625only if it contains `\Gamma(N)` where `N` is twice its generalised1626level (the least common multiple of its cusp widths). We can therefore1627proceed by calculating the index of the subgroup of `{\rm SL}(2, \ZZ /1628N\ZZ)` generated by the gens of self, and checking whether or not it1629has the same index as self.16301631EXAMPLES::16321633sage: GammaH(11,[4]).as_permutation_group().is_congruence()1634True16351636The following example (taken from [KSV11]_) shows that it may be the1637case that G is not congruence, even if its image in `{\rm PSL}(2,\ZZ)`1638is congruence::16391640sage: S2 = "(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23)"1641sage: S3 = "(1,14,15,13,2,3)(4,5,6,16,17,18)(7,8,9,19,20,21)(10,11,12,22,23,24)"1642sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)1643sage: G.is_congruence()1644False1645sage: G.to_even_subgroup().is_congruence()1646True16471648In fact G is a lifting to `{\rm SL}(2,\ZZ)` of the group1649`\bar{\Gamma}_0(6)`::16501651sage: G.to_even_subgroup() == Gamma0(6)1652True1653"""1654Gev = self.to_even_subgroup()1655if not Gev.is_congruence():1656return False1657else:1658N = 2*self.generalised_level()1659from sage.groups.matrix_gps.matrix_group import MatrixGroup1660H = MatrixGroup([x.matrix().change_ring(Zmod(N)) for x in self.gens()])1661from congroup_gamma import Gamma_constructor as Gamma1662return Gamma(N).index() == self.index() * H.order()16631664class EvenArithmeticSubgroup_Permutation(ArithmeticSubgroup_Permutation_class):1665r"""1666An arithmetic subgroup `\Gamma` defined by two permutations, giving the1667action of four standard generators16681669.. MATH::16701671s_2 = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix},\quad1672s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad1673l = \begin{pmatrix} 1 & 1 \\ 0 & 1\end{pmatrix},\quad1674r = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix}.16751676by right multiplication on the coset representatives `\Gamma \backslash {\rm SL}_2(\ZZ)`.167716781679EXAMPLES:16801681Construct a noncongruence subgroup of index 7 (the smallest possible)::16821683sage: a2 = SymmetricGroup(7)([(1,2),(3,4),(6,7)]); a3 = SymmetricGroup(7)([(1,2,3),(4,5,6)])1684sage: G = ArithmeticSubgroup_Permutation(S2=a2, S3=a3); G1685Arithmetic subgroup with permutations of right cosets1686S2=(1,2)(3,4)(6,7)1687S3=(1,2,3)(4,5,6)1688L=(1,4,7,6,5,3)1689R=(2,4,5,7,6,3)1690sage: G.index()169171692sage: G.dimension_cusp_forms(4)169311694sage: G.is_congruence()1695False16961697Convert some standard congruence subgroups into permutation form::16981699sage: G = Gamma0(8).as_permutation_group()1700sage: G.index()1701121702sage: G.is_congruence()1703True17041705sage: G = Gamma0(12).as_permutation_group()1706sage: print G1707Arithmetic subgroup of index 241708sage: G.is_congruence()1709True17101711The following is the unique index 2 even subgroup of `{\rm SL}_2(\ZZ)`::17121713sage: w = SymmetricGroup(2)([2,1])1714sage: G = ArithmeticSubgroup_Permutation(L=w, R=w)1715sage: G.dimension_cusp_forms(6)171611717sage: G.genus()171801719"""1720def __init__(self, S2, S3, L, R, canonical_labels=False):1721r"""1722TESTS::17231724sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)(5,6)",S3="(1,2,3)(4,5,6)")1725sage: G == loads(dumps(G))1726True1727sage: G is loads(dumps(G))1728False1729"""1730self._S2 = S21731self._S3 = S31732self._L = L1733self._R = R1734if canonical_labels:1735self._canonical_label_group = self17361737def __reduce__(self):1738r"""1739Data for pickling.17401741TESTS::17421743sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,4)")1744sage: G == loads(dumps(G)) #indirect doctest1745True1746sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,4)",relabel=True)1747sage: GG = loads(dumps(G))1748sage: G == GG #indirect doctest1749True1750sage: GG.relabel(inplace=False) is GG1751True1752"""1753if hasattr(self, '_canonical_label_group'):1754canonical_labels = (self is self._canonical_label_group)1755else:1756canonical_labels = False1757return (EvenArithmeticSubgroup_Permutation,1758(self._S2, self._S3, self._L, self._R, canonical_labels))17591760def is_odd(self):1761r"""1762Returns True if this subgroup does not contain the matrix `-Id`.17631764EXAMPLES::17651766sage: G = Gamma(2).as_permutation_group()1767sage: G.is_odd()1768False1769"""1770return False17711772def is_even(self):1773r"""1774Returns True if this subgroup contains the matrix `-Id`.17751776EXAMPLES::17771778sage: G = Gamma(2).as_permutation_group()1779sage: G.is_even()1780True1781"""1782return True17831784def nu2(self):1785r"""1786Returns the number of orbits of elliptic points of order 2 for this1787arithmetic subgroup.17881789EXAMPLES::17901791sage: G = ArithmeticSubgroup_Permutation(S2="(1,4)(2)(3)",S3="(1,2,3)(4)")1792sage: G.nu2()179321794"""1795return sum(1 for i in xrange(self.index()) if self._S2[i] == i)17961797def nu3(self):1798r"""1799Returns the number of orbits of elliptic points of order 3 for this1800arithmetic subgroup.18011802EXAMPLES::18031804sage: G = ArithmeticSubgroup_Permutation(S2="(1,4)(2)(3)",S3="(1,2,3)(4)")1805sage: G.nu3()180611807"""1808return sum(1 for i in xrange(self.index()) if self._S3[i] == i)18091810def ncusps(self):1811r"""1812Return the number of cusps of this arithmetic subgroup.18131814EXAMPLES::18151816sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)(5,6)",S3="(1,2,3)(4,5,6)")1817sage: G.ncusps()181831819"""1820return len(self.L().cycle_tuples(True))18211822def _spanning_tree_kulkarni(self, root=0, on_right=True):1823r"""1824Returns a spanning tree for the coset graph of the group for the1825generators `S2` and `S3`.18261827Warning: the output is randomized in order to be able to obtain any1828spanning tree of the coset graph. The algorithm mainly follows1829Kulkarni's paper.18301831INPUT:18321833- ``on_right`` - boolean (default: True) - if False, return spanning1834tree for the left cosets.18351836OUTPUT:18371838- ``tree`` - a spanning tree of the graph associated to the action of1839``L`` and ``S2`` on the cosets18401841- ``reps`` - list of matrices in `{\rm SL}_2(\ZZ)`` - representatives of the1842cosets with respect to the spanning tree18431844- ``word_reps`` - list of lists with ``s2`` and ``s3`` - word1845representatives of the cosets with respect to the spanning tree.18461847- ``gens`` - list of 3-tuples ``(in,out,label)`` - the list of edges in1848the graph which are not in the spanning tree.18491850EXAMPLES::18511852sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)')1853sage: tree,reps,wreps,gens = G._spanning_tree_kulkarni()1854sage: tree1855Digraph on 4 vertices1856sage: for m in reps: print m,"\n****"1857[1 0]1858[0 1]1859****1860[ 0 1]1861[-1 1]1862****1863[-1 1]1864[-1 0]1865****1866[1 1]1867[0 1]1868****1869sage: for w in wreps: print ','.join(w)1870<BLANKLINE>1871s31872s3,s31873s3,s3,s21874sage: gens1875[(0, 1, 's2'), (3, 3, 's3')]1876"""1877from sage.graphs.digraph import DiGraph1878from sage.misc.prandom import randint18791880N = len(self._S2)18811882if on_right:1883s2 = self._S21884s3 = self._S318851886else:1887s2 = [None] * N1888s3 = [None] * N1889for i in xrange(N):1890s2[self._S2[i]] = i1891s3[self._S3[i]] = i18921893# the tree and the lift1894tree = DiGraph(multiedges=False,loops=False)1895gens = []18961897reps = [None]*self.index()1898word_reps = [None]*self.index()1899reps[root] = SL2Z(1)1900word_reps[root] = []19011902x0 = root1903tree.add_vertex(x0)1904l = [x0]19051906while True:1907# complete the current 3-loop in the tree1908if s3[x0] != x0: # loop of length 31909x1 = s3[x0]1910x2 = s3[x1]1911tree.add_edge(x0,x1,'s3')1912tree.add_edge(x1,x2,'s3')1913if on_right:1914reps[x1] = reps[x0] * S3m1915reps[x2] = reps[x1] * S3m1916word_reps[x1] = word_reps[x0] + ['s3']1917word_reps[x2] = word_reps[x1] + ['s3']1918else:1919reps[x1] = S3m * reps[x0]1920reps[x2] = S3m * reps[x1]1921word_reps[x1] = ['s3'] + word_reps[x0]1922word_reps[x2] = ['s3'] + word_reps[x1]1923l.append(x1)1924l.append(x2)1925else: # elliptic generator1926gens.append((x0,x0,'s3'))19271928# now perform links with s while we find another guy1929while l:1930x1 = l.pop(randint(0,len(l)-1))1931x0 = s2[x1]19321933if x1 != x0: # loop of length 21934if x0 in tree:1935gens.append((x1,x0,'s2'))1936del l[l.index(x0)] # x0 must be in l1937else:1938tree.add_edge(x1,x0,'s2')1939if on_right:1940reps[x0] = reps[x1] * S2m1941word_reps[x0] = word_reps[x1] + ['s2']1942else:1943reps[x0] = S2m * reps[x1]1944word_reps[x0] = ['s2'] + word_reps[x1]1945break1946else: # elliptic generator1947gens.append((x1,x1,'s2'))19481949else:1950break19511952return tree,reps,word_reps,gens19531954def _spanning_tree_verrill(self, root=0, on_right=True):1955r"""1956Returns a spanning tree with generators `S2` and `L`.19571958The algorithm follows the one of Helena Verrill.19591960OUTPUT:19611962- ``tree`` - a spanning tree of the graph associated to the action of1963``L`` and ``S2`` on the cosets19641965- ``reps`` - list of matrices in `{\rm SL}_2(\ZZ)` - representatives of the1966cosets with respect to the spanning tree19671968- ``word_reps`` - list of string with ``s`` and ``l`` - word1969representatives of the cosets with respect to the spanning tree.19701971- ``gens`` - list of 3-tuples ``(in,out,label)`` - the list of edges in1972the graph which are not in the spanning tree.19731974EXAMPLES::19751976sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)')1977sage: tree,reps,wreps,gens=G._spanning_tree_verrill()1978sage: tree1979Digraph on 4 vertices1980sage: for m in reps: print m, "\n****"1981[1 0]1982[0 1]1983****1984[ 0 -1]1985[ 1 0]1986****1987[1 2]1988[0 1]1989****1990[1 1]1991[0 1]1992****1993sage: wreps1994['', 's', 'll', 'l']1995sage: gens1996[(2, 0, 'l'), (1, 1, 'l'), (2, 3, 's')]19971998TODO:19992000Take care of the shape of the spanning tree as in Helena Verrill's program.2001"""2002from sage.misc.prandom import randint2003from copy import copy20042005if on_right:2006s = self._S22007l = self._L2008else:2009s = [None]*self.index()2010l = [None]*self.index()2011for i in xrange(self.index()):2012s[self._S2[i]] = i2013l[self._L[i]] = i20142015from sage.graphs.digraph import DiGraph2016tree = DiGraph(multiedges=False,loops=False)2017gens = []20182019reps = [None]*self.index()2020word_reps = [None]*self.index()2021reps[root] = SL2Z(1)2022word_reps[root] = ''20232024x0 = root2025tree.add_vertex(x0)2026waiting = [x0]20272028while True:2029# complete the current l-loop in the tree from x02030x = x02031xx = l[x]2032while xx != x0:2033tree.add_edge(x,xx,'l')2034if on_right:2035reps[xx] = reps[x] * Lm2036word_reps[xx] = word_reps[x] + 'l'2037else:2038reps[xx] = Lm * reps[x]2039word_reps[xx] = 'l' + word_reps[x]2040waiting.append(xx)2041x = xx2042xx = l[x]20432044gens.append((x,x0,'l'))20452046# now perform links with s while we find another guy which will2047# become the new x02048while waiting:2049x0 = None2050while waiting and x0 is None:2051x1 = waiting.pop(randint(0,len(waiting)-1))2052x0 = s[x1]20532054if x0 is not None:2055if x1 != x0: # loop of length 22056if x0 in tree:2057gens.append((x1,x0,'s'))2058if x0 in waiting:2059del waiting[waiting.index(x0)] # x0 must be in l2060else:2061tree.add_edge(x1,x0,'s')2062if on_right:2063reps[x0] = reps[x1] * S2m2064word_reps[x0] = word_reps[x1] + 's'2065else:2066reps[x0] = S2m * reps[x1]2067word_reps[x0] = 's' + word_reps[x1]2068break2069else: # elliptic generator2070gens.append((x1,x1,'s'))20712072else:2073break20742075return tree,reps,word_reps,gens20762077def todd_coxeter_s2_s3(self):2078r"""2079Returns a 4-tuple ``(coset_reps, gens, s2, s3)`` where ``coset_reps``2080are coset representatives of the subgroup, ``gens`` is a list of2081generators, ``s2`` and ``s3`` are the action of the matrices `S2` and2082`S3` on the list of cosets.20832084The so called *Todd-Coxeter algorithm* is a general method for coset2085enumeration for a subgroup of a group given by generators and relations.20862087EXAMPLES::20882089sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)')2090sage: G.genus()209102092sage: reps,gens,s2,s3=G.todd_coxeter_s2_s3()2093sage: g1,g2 = gens2094sage: g1 in G and g2 in G2095True2096sage: g12097[-1 0]2098[ 1 -1]2099sage: g22100[-1 3]2101[-1 2]2102sage: S2 = SL2Z([0,-1,1,0])2103sage: S3 = SL2Z([0,1,-1,1])2104sage: reps[0] == SL2Z([1,0,0,1])2105True2106sage: all(reps[i]*S2*~reps[s2[i]] in G for i in xrange(4))2107True2108sage: all(reps[i]*S3*~reps[s3[i]] in G for i in xrange(4))2109True2110"""2111tree,reps,wreps,edges = self._spanning_tree_kulkarni()21122113gens = []2114for e in edges:2115if e[2] == 's2':2116gens.append(self(reps[e[0]] * S2m * ~reps[e[1]]))2117elif e[2] == 's3':2118gens.append(self(reps[e[0]] * S3m * ~reps[e[1]]))2119else:2120raise ValueError, "this should not happen"21212122return reps, gens, self._S2[:], self._S3[:]21232124def todd_coxeter_l_s2(self):2125r"""2126Returns a 4-tuple ``(coset_reps, gens, l, s2)`` where ``coset_reps`` is2127a list of coset representatives of the subgroup, ``gens`` a list of2128generators, ``l`` and ``s2`` are list that corresponds to the action of2129the matrix `S2` and `L` on the cosets.21302131EXAMPLES::21322133sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)')2134sage: reps,gens,l,s=G.todd_coxeter_l_s2()2135sage: reps2136[2137[1 0] [ 0 -1] [1 2] [1 1]2138[0 1], [ 1 0], [0 1], [0 1]2139]2140sage: gens2141[2142[1 3] [ 1 0] [ 2 -3]2143[0 1], [-1 1], [ 1 -1]2144]2145sage: l2146[3, 1, 0, 2]2147sage: s2148[1, 0, 3, 2]2149sage: S2 = SL2Z([0,-1,1,0])2150sage: L = SL2Z([1,1,0,1])2151sage: reps[0] == SL2Z([1,0,0,1])2152True2153sage: all(reps[i]*S2*~reps[s[i]] in G for i in xrange(4))2154True2155sage: all(reps[i]*L*~reps[l[i]] in G for i in xrange(4))2156True2157"""2158tree,reps,wreps,edges = self._spanning_tree_verrill()21592160gens = []2161for e in edges:2162if e[2] == 'l':2163gens.append(self(reps[e[0]] * Lm * ~reps[e[1]]))2164elif e[2] == 's':2165gens.append(self(reps[e[0]] * S2m * ~reps[e[1]]))2166else:2167raise ValueError, "this should not happen"21682169return reps, gens, self._L[:], self._S2[:]21702171todd_coxeter = todd_coxeter_l_s221722173def coset_reps(self):2174r"""2175Return coset representatives.21762177EXAMPLES::21782179sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,3)")2180sage: c = G.coset_reps()2181sage: len(c)218242183sage: [g in G for g in c]2184[True, False, False, False]2185"""2186return self.todd_coxeter()[0]21872188def cusp_widths(self,exp=False):2189r"""2190Return the list of cusp widths of the group.21912192EXAMPLES::21932194sage: G = Gamma(2).as_permutation_group()2195sage: G.cusp_widths()2196[2, 2, 2]2197sage: G.cusp_widths(exp=True)2198{2: 3}21992200sage: S2 = "(1,2)(3,4)(5,6)"2201sage: S3 = "(1,2,3)(4,5,6)"2202sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)2203sage: G.cusp_widths()2204[1, 1, 4]2205sage: G.cusp_widths(exp=True)2206{1: 2, 4: 1}22072208sage: S2 = "(1,2)(3,4)(5,6)"2209sage: S3 = "(1,3,5)(2,4,6)"2210sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)2211sage: G.cusp_widths()2212[6]2213sage: G.cusp_widths(exp=True)2214{6: 1}2215"""2216seen = [True]*self.index()22172218if exp:2219widths = {}2220else:2221widths = []2222for i in xrange(self.index()):2223if seen[i]:2224seen[i] = False2225j = self._L[i]2226n = 12227while j != i:2228seen[j] = False2229n += 12230j = self._L[j]2231if exp:2232if n not in widths:2233widths[n] = 02234widths[n] += 12235else:2236widths.append(n)22372238if exp:2239return widths2240return sorted(widths)22412242def to_even_subgroup(self, relabel=True):2243r"""2244Return the subgroup generated by self and ``-Id``. Since self is even,2245this is just self. Provided for compatibility.22462247EXAMPLE::22482249sage: G = Gamma0(4).as_permutation_group()2250sage: H = G.to_even_subgroup()2251sage: H == G2252True2253"""2254if relabel:2255return self.relabel(inplace=False)2256else:2257return self22582259def is_congruence(self):2260r"""2261Return True if this is a congruence subgroup.22622263ALGORITHM:22642265Uses Hsu's algorithm [Hsu96]_. Adapted from Chris Kurth's2266implementation in KFarey [Kur08]_.22672268EXAMPLES:22692270Test if `{\rm SL}_2(\ZZ)` is congruence::22712272sage: a = ArithmeticSubgroup_Permutation(L='',R='')2273sage: a.index()227412275sage: a.is_congruence()2276True22772278This example is congruence -- it's Gamma0(3) in disguise: ::22792280sage: S2 = SymmetricGroup(4)2281sage: l = S2((2,3,4))2282sage: r = S2((1,3,4))2283sage: G = ArithmeticSubgroup_Permutation(L=l,R=r)2284sage: print G2285Arithmetic subgroup with permutations of right cosets2286S2=(1,2)(3,4)2287S3=(1,4,2)2288L=(2,3,4)2289R=(1,3,4)2290sage: G.is_congruence()2291True22922293This one is noncongruence: ::22942295sage: import sage.modular.arithgroup.arithgroup_perm as ap2296sage: ap.HsuExample10().is_congruence()2297False2298"""2299if self.index() == 1: # the group is SL2Z (trivial case)2300return True23012302L = self.L() # action of L2303R = self.R() # action of R23042305N = L.order() # generalised level of the group23062307# write N as N = em where e = 2^k and m odd2308e = 12309m = N2310while m%2 == 0:2311m //= 22312e *= 223132314if e==1: # N is odd2315onehalf = int(~Zmod(N)(2)) #i.e. 2^(-1) mod N2316rel = (R*R*L**(-onehalf))**32317return rel.is_one()23182319elif m==1: # N is a power of 22320onefifth=int(~Zmod(N)(5)) #i.e. 5^(-1) mod N2321S=L**20*R**onefifth*L**(-4)*~R23222323# congruence if the three below permutations are trivial2324rel=(~L*R*~L) * S * (L*~R*L) * S2325if not rel.is_one():2326return False23272328rel=~S*R*S*R**(-25)2329if not rel.is_one():2330return False23312332rel=(S*R**5*L*~R*L)**32333if not rel.is_one():2334return False23352336return True23372338else: # e>1, m>12339onehalf=int(~Zmod(m)(2)) #i.e. 2^(-1) mod m2340onefifth=int(~Zmod(e)(5)) #i.e. 5^(-1) mod e2341c=int(~Zmod(m)(e))*e #i.e. e^(-1)e mod m2342d=int(~Zmod(e)(m))*m #i.e. m^(-1)m mod e2343a=L**c2344b=R**c2345l=L**d2346r=R**d2347s=l**20*r**onefifth*l**(-4)*~r23482349#Congruence if the seven permutations below are trivial:2350rel=~a*~r*a*r2351if not rel.is_one():2352return False23532354rel=(a*~b*a)**42355if not rel.is_one():2356return False23572358rel=(a*~b*a)**2*(~a*b)**32359if not rel.is_one():2360return False23612362rel=(a*~b*a)**2*(b*b*a**(-onehalf))**(-3)2363if not rel.is_one():2364return False23652366rel=(~l*r*~l)*s*(l*~r*l)*s2367if not rel.is_one():2368return False23692370rel=~s*r*s*r**(-25)2371if not rel.is_one():2372return False23732374rel=(l*~r*l)**2*(s*r**5*l*~r*l)**(-3)2375if not rel.is_one():2376return False23772378return True23792380def one_odd_subgroup(self,random=False):2381r"""2382Return an odd subgroup of index 2 in `\Gamma`, where `\Gamma` is this2383subgroup. If the optional argument ``random`` is False (the default),2384this returns an arbitrary but consistent choice from the set of index 22385odd subgroups. If ``random`` is True, then it will choose one of these2386at random.23872388For details of the algorithm used, see the docstring for the related2389function :meth:`odd_subgroups`, which returns a list of all the2390index 2 odd subgroups.23912392EXAMPLES:23932394Starting from `\Gamma(4)` we get back `\Gamma(4)`::23952396sage: G = Gamma(4).as_permutation_group()2397sage: print G.is_odd(), G.index()2398True 482399sage: Ge = G.to_even_subgroup()2400sage: Go = Ge.one_odd_subgroup()2401sage: print Go.is_odd(), Go.index()2402True 482403sage: Go == G2404True24052406Strating from `\Gamma(6)` we get a different group::24072408sage: G = Gamma(6).as_permutation_group()2409sage: print G.is_odd(), G.index()2410True 1442411sage: Ge = G.to_even_subgroup()2412sage: Go = Ge.one_odd_subgroup()2413sage: print Go.is_odd(), Go.index()2414True 1442415sage: Go == G2416False24172418An error will be raised if there are no such subgroups, which occurs if2419and only if the group contains an element of order 4::24202421sage: Gamma0(10).as_permutation_group().one_odd_subgroup()2422Traceback (most recent call last):2423...2424ValueError: Group contains an element of order 4, hence no index 2 odd subgroups24252426Testing randomness::24272428sage: G = Gamma(6).as_permutation_group().to_even_subgroup()2429sage: G1 = G.one_odd_subgroup(random=True) # random2430sage: G1.is_subgroup(G)2431True2432"""2433if self.nu2() != 0:2434raise ValueError, "Group contains an element of order 4, hence no index 2 odd subgroups"2435n = self.index()2436s2old, s3old = self.S2(), self.S3()2437s2cycs = s2old.cycle_tuples() # no singletons can exist2438s3cycs = s3old.cycle_tuples(singletons=True)2439s2 = PermutationGroupElement([x + tuple(y + n for y in x) for x in s2cycs])2440s3 = PermutationGroupElement([x + tuple(y + n for y in x) for x in s3cycs])24412442if random is False:2443return ArithmeticSubgroup_Permutation(S2=s2,S3=s3,check=False)24442445from sage.misc.prandom import randint24462447t = []2448for i in xrange(1,n+1):2449if randint(0,1):2450t.append((i,n+i))2451t = PermutationGroupElement(t)2452return ArithmeticSubgroup_Permutation(S2=s2,S3=t*s3*t,check=False)24532454def odd_subgroups(self):2455r"""2456Return a list of the odd subgroups of index 2 in `\Gamma`, where2457`\Gamma` is this subgroup. (Equivalently, return the liftings of2458`\bar{\Gamma} \le {\rm PSL}(2, \ZZ)` to `{\rm SL}(2, \ZZ)`.)24592460.. seealso:: :meth:`one_odd_subgroup`, which returns just one of the2461odd subgroups (which is much quicker than enumerating them all).24622463ALGORITHM:24642465- If `\Gamma` has an element of order 4, then there are no index 2 odd2466subgroups, so return the empty set.24672468- If `\Gamma` has no elements of order 4, then the permutation `S_2` is2469a combination of 2-cycles with no fixed points on `\{1, \dots, N\}`.2470We construct the permutation `\tilde{S}_2` of `\{1, \dots, 2N\}`2471which has a 4-cycle `(a, b, a+N, b+N)` for each 2-cycle `(a,b)` in2472``S2``. Similarly, we construct a permutation `\tilde{S}_3` which has2473a 6-cycle `(a,b,c,a+N,b+N,c+N)` for each 3-cycle `(a,b,c)` in `S_3`,2474and a 2-cycle `(a,a+N)` for each fixed point `a` of `S_3`.24752476Then the permutations `\tilde{S}_2` and `\tilde{S}_3` satisfy2477`\tilde{S}_2^2 = \tilde{S}_3^3 = \iota` where `\iota` is the order 22478permutation interchanging `a` and `a+N` for each `a`. So the subgroup2479corresponding to these permutations is an index 2 odd subgroup of2480`\Gamma`.24812482- The other index 2 odd subgroups of `\Gamma` are obtained from the2483pairs `\tilde{S}_2, \tilde{S}_3^\sigma` where `\sigma` is an element2484of the group generated by the 2-cycles `(a, a+N)`.24852486Studying the permutations in the first example below gives a good2487illustration of the algorithm.24882489EXAMPLES::24902491sage: G = sage.modular.arithgroup.arithgroup_perm.HsuExample10()2492sage: [G.S2(), G.S3()]2493[(1,2)(3,4)(5,6)(7,8)(9,10), (1,8,3)(2,4,6)(5,7,10)]2494sage: X = G.odd_subgroups()2495sage: for u in X: print [u.S2(), u.S3()]2496[(1,2,11,12)(3,4,13,14)(5,6,15,16)(7,8,17,18)(9,10,19,20), (1,8,3,11,18,13)(2,4,6,12,14,16)(5,7,10,15,17,20)(9,19)]2497[(1,2,11,12)(3,4,13,14)(5,6,15,16)(7,8,17,18)(9,10,19,20), (1,18,13,11,8,3)(2,4,6,12,14,16)(5,7,10,15,17,20)(9,19)]2498[(1,2,11,12)(3,4,13,14)(5,6,15,16)(7,8,17,18)(9,10,19,20), (1,8,13,11,18,3)(2,4,6,12,14,16)(5,7,10,15,17,20)(9,19)]2499[(1,2,11,12)(3,4,13,14)(5,6,15,16)(7,8,17,18)(9,10,19,20), (1,18,3,11,8,13)(2,4,6,12,14,16)(5,7,10,15,17,20)(9,19)]25002501A projective congruence subgroup may have noncongruence liftings, as the example of `\bar{\Gamma}_0(6)` illustrates (see [KSV11]_)::25022503sage: X = Gamma0(6).as_permutation_group().odd_subgroups(); Sequence([[u.S2(), u.S3()] for u in X],cr=True)2504[2505[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23), (1,2,3,13,14,15)(4,5,6,16,17,18)(7,8,9,19,20,21)(10,11,12,22,23,24)],2506[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23), (1,14,15,13,2,3)(4,5,6,16,17,18)(7,8,9,19,20,21)(10,11,12,22,23,24)],2507[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23), (1,2,3,13,14,15)(4,17,6,16,5,18)(7,8,9,19,20,21)(10,11,12,22,23,24)],2508[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23), (1,14,15,13,2,3)(4,17,6,16,5,18)(7,8,9,19,20,21)(10,11,12,22,23,24)],2509[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23), (1,2,3,13,14,15)(4,5,6,16,17,18)(7,20,9,19,8,21)(10,11,12,22,23,24)],2510[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23), (1,14,15,13,2,3)(4,5,6,16,17,18)(7,20,9,19,8,21)(10,11,12,22,23,24)],2511[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23), (1,2,3,13,14,15)(4,17,6,16,5,18)(7,20,9,19,8,21)(10,11,12,22,23,24)],2512[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23), (1,14,15,13,2,3)(4,17,6,16,5,18)(7,20,9,19,8,21)(10,11,12,22,23,24)]2513]2514sage: [u.is_congruence() for u in X]2515[True, False, False, True, True, False, False, True]25162517Subgroups of large index may take a very long time::25182519sage: len(GammaH(11,[-1]).as_permutation_group().odd_subgroups()) # long time (15s)252020482521"""2522if self.nu2() != 0:2523return []2524n = self.index()2525s2old, s3old = self.S2(), self.S3()2526s2cycs = s2old.cycle_tuples() # no singletons can exist2527s3cycs = s3old.cycle_tuples(singletons=True)2528s2 = PermutationGroupElement([x + tuple(y + n for y in x) for x in s2cycs])2529s3 = PermutationGroupElement([x + tuple(y + n for y in x) for x in s3cycs])2530H = ArithmeticSubgroup_Permutation(S2=s2,S3=s3)25312532bucket = set([H])2533res = [H]2534# We use a set *and* a list since checking whether an element is in a2535# set is very fast, but on the other hand we want the order the results2536# are returned to be at least somewhat canonical.2537ts = [PermutationGroupElement(range(1,1+2*n))]25382539for i in xrange(1,n+1):25402541t = PermutationGroupElement([(i, n+i)],check=False)25422543s3c = t*s3*t25442545if s3c == s3:2546# t commutes with s3; nothing to see here.2547continue25482549HH = ArithmeticSubgroup_Permutation(S2=s2,S3=s3c,check=False)25502551if HH not in bucket:2552# Because the liftings are indexed by Hom(self, +-1) which is a2553# vector space over F2, either HH is already familiar, or all2554# the subgroups one gets by acting by t are new.25552556bucket.add(HH)2557res.append(HH)2558ts.append(t)2559for tt in ts[1:-1]:2560ts.append(tt*t)2561res.append(ArithmeticSubgroup_Permutation(S2=s2,S3=tt*s3c*tt,check=False))2562bucket.add(res[-1])25632564return res25652566def HsuExample10():2567r"""2568An example of an index 10 arithmetic subgroup studied by Tim Hsu.25692570EXAMPLE::25712572sage: import sage.modular.arithgroup.arithgroup_perm as ap2573sage: ap.HsuExample10()2574Arithmetic subgroup with permutations of right cosets2575S2=(1,2)(3,4)(5,6)(7,8)(9,10)2576S3=(1,8,3)(2,4,6)(5,7,10)2577L=(1,4)(2,5,9,10,8)(3,7,6)2578R=(1,7,9,10,6)(2,3)(4,5,8)2579"""2580return ArithmeticSubgroup_Permutation(2581L="(1,4)(2,5,9,10,8)(3,7,6)",2582R="(1,7,9,10,6)(2,3)(4,5,8)",2583relabel=False)25842585def HsuExample18():2586r"""2587An example of an index 18 arithmetic subgroup studied by Tim Hsu.25882589EXAMPLE::25902591sage: import sage.modular.arithgroup.arithgroup_perm as ap2592sage: ap.HsuExample18()2593Arithmetic subgroup with permutations of right cosets2594S2=(1,5)(2,11)(3,10)(4,15)(6,18)(7,12)(8,14)(9,16)(13,17)2595S3=(1,7,11)(2,18,5)(3,9,15)(4,14,10)(6,17,12)(8,13,16)2596L=(1,2)(3,4)(5,6,7)(8,9,10)(11,12,13,14,15,16,17,18)2597R=(1,12,18)(2,6,13,9,4,8,17,7)(3,16,14)(5,11)(10,15)2598"""2599return ArithmeticSubgroup_Permutation(2600L="(1,2)(3,4)(5,6,7)(8,9,10)(11,12,13,14,15,16,17,18)",2601R="(1,12,18)(2,6,13,9,4,8,17,7)(3,16,14)(5,11)(10,15)",2602relabel=False)260326042605