Path: blob/master/src/sage/modular/arithgroup/arithgroup_perm.py
8820 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", preprint57:arxiv:`0901.1340`5859.. [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:62:arxiv:`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"6970.. [KuLo] Chris Kurth and Ling Long, "Computations with finite index subgroups71of `{\rm PSL}_2(\ZZ)` using Farey symbols", :arxiv:`0710.1835`7273.. [Ve] Helena Verrill, "Fundamental domain drawer", Java program,74http://www.math.lsu.edu/~verrill/7576TODO:7778- modular Farey symbols7980- computation of generators of a modular subgroup with a standard surface81group presentation. In other words, compute a presentation of the form8283.. MATH::8485\langle x_i,y_i,c_j |\ \prod_i [x_i,y_i] \prod_j c_j^{\nu_j} = 1\rangle8687where the elements `x_i` and `y_i` are hyperbolic and `c_j` are parabolic88(`\nu_j=\infty`) or elliptic elements (`\nu_j < \infty`).8990- computation of centralizer.9192- generation of modular (even) subgroups of fixed index.9394AUTHORS:9596- Chris Kurth (2008): created KFarey package9798- David Loeffler (2009): adapted functions from KFarey for inclusion into Sage99100- Vincent Delecroix (2010): implementation for odd groups, new design,101improvements, documentation102103- David Loeffler (2011): congruence testing for odd subgroups, enumeration of104liftings of projective subgroups105"""106107################################################################################108#109# Copyright (C) 2009, The Sage Group -- http://www.sagemath.org/110#111# Distributed under the terms of the GNU General Public License (GPL)112#113# The full text of the GPL is available at:114#115# http://www.gnu.org/licenses/116#117################################################################################118119from all import SL2Z120from arithgroup_generic import ArithmeticSubgroup121from sage.rings.all import Zmod122from sage.misc.cachefunc import cached_method123import sage.rings.arith as arith124125from sage.groups.perm_gps.permgroup_element import PermutationGroupElement126127Idm = SL2Z([1,0,0,1]) # identity128129Lm = SL2Z([1,1,0,1]) # parabolic that fixes infinity130Rm = SL2Z([1,0,1,1]) # parabolic that fixes 0131S2m = SL2Z([0,-1,1,0]) # elliptic of order 2 (fix i)132S3m = SL2Z([0,1,-1,1]) # elliptic of order 3 (fix j)133134S2mi = SL2Z([0,1,-1,0]) # the inverse of S2m in SL(2,Z)135S3mi = SL2Z([1,-1,1,0]) # the inverse of S3m in SL(2,Z)136Lmi = SL2Z([1,-1,0,1]) # the inverse of Lm in SL(2,Z)137Rmi = SL2Z([1,0,-1,1]) # the inverse of Rm in SL(2,Z)138139def sl2z_word_problem(A):140r"""141Given an element of `{\rm SL}_2(\ZZ)`, express it as a word in the generators L =142[1,1,0,1] and R = [1,0,1,1].143144The return format is a list of pairs ``(a,b)``, where ``a = 0`` or ``1``145denoting ``L`` or ``R`` respectively, and ``b`` is an integer exponent.146147See also the function :func:`eval_sl2z_word`.148149EXAMPLES::150151sage: from sage.modular.arithgroup.arithgroup_perm import eval_sl2z_word, sl2z_word_problem152sage: m = SL2Z([1,0,0,1])153sage: eval_sl2z_word(sl2z_word_problem(m)) == m154True155sage: m = SL2Z([0,-1,1,0])156sage: eval_sl2z_word(sl2z_word_problem(m)) == m157True158sage: m = SL2Z([7,8,-50,-57])159sage: eval_sl2z_word(sl2z_word_problem(m)) == m160True161"""162A = SL2Z(A)163output=[]164165## If A00 is zero166if A[0,0]==0:167c=A[1,1]168if c != 1:169A=A*Lm**(c-1)*Rm*Lmi170output.extend([(0,1-c),(1,-1),(0,1)])171else:172A=A*Rm*Lmi173output.extend([(1,-1),(0,1)])174175if A[0,0]<0: # Make sure A00 is positive176A=SL2Z(-1)*A177output.extend([(1,-1), (0,1), (1,-1), (0,1), (1,-1), (0,1)])178179if A[0,1]<0: # if A01 is negative make it positive180n=(-A[0,1]/A[0,0]).ceil() #n s.t. 0 <= A[0,1]+n*A[0,0] < A[0,0]181A=A*Lm**n182output.append((0, -n))183## At this point A00>0 and A01>=0184while not (A[0,0]==0 or A[0,1]==0):185if A[0,0]>A[0,1]:186n=(A[0,0]/A[0,1]).floor()187A=A*SL2Z([1,0,-n,1])188output.append((1, n))189190else: #A[0,0]<=A[0,1]191n=(A[0,1]/A[0,0]).floor()192A=A*SL2Z([1,-n,0,1])193output.append((0, n))194195if A==SL2Z(1):196pass #done, so don't add R^0197elif A[0,0]==0:198c=A[1,1]199if c != 1:200A=A*Lm**(c-1)*Rm*Lmi201output.extend([(0,1-c),(1,-1),(0, 1)])202else:203A=A*Rm*Lmi204output.extend([(1,-1),(0,1)])205else:206c=A[1,0]207if c:208A=A*Rm**(-c)209output.append((1,c))210211output.reverse()212return output213214def eval_sl2z_word(w):215r"""216Given a word in the format output by :func:`sl2z_word_problem`, convert it back217into an element of `{\rm SL}_2(\ZZ)`.218219EXAMPLES::220221sage: from sage.modular.arithgroup.arithgroup_perm import eval_sl2z_word222sage: eval_sl2z_word([(0, 1), (1, -1), (0, 0), (1, 3), (0, 2), (1, 9), (0, -1)])223[ 66 -59]224[ 47 -42]225"""226from sage.all import prod227mat = [Lm,Rm]228w0 = Idm229w1 = w230return w0 * prod((mat[a[0]]**a[1] for a in w1), Idm)231232def word_of_perms(w, p1, p2):233r"""234Given a word `w` as a list of 2-tuples ``(index,power)`` and permutations235``p1`` and ``p2`` return the product of ``p1`` and ``p2`` that corresponds236to ``w``.237238EXAMPLES::239240sage: import sage.modular.arithgroup.arithgroup_perm as ap241sage: S2 = SymmetricGroup(4)242sage: p1 = S2('(1,2)(3,4)')243sage: p2 = S2('(1,2,3,4)')244sage: ap.word_of_perms([(1,1),(0,1)], p1, p2) == p2 * p1245True246sage: ap.word_of_perms([(0,1),(1,1)], p1, p2) == p1 * p2247True248"""249if not isinstance(p1,PermutationGroupElement):250p1 = PermutationGroupElement(p1)251if not isinstance(p2,PermutationGroupElement):252p2 = PermutationGroupElement(p2)253254G = p1.parent()255if G != p2.parent(): # find a minimal parent256G2 = p2.parent()257if G.has_coerce_map_from(G2):258p2 = G(p2)259elif G2.has_coerce_map_from(G):260G = G2261p1 = G(p1)262else:263from sage.groups.perm_gps.all import PermutationGroup264G = PermutationGroup([p1,p2])265p1 = G(p1)266p2 = G(p2)267268M = G.identity()269p = [p1, p2]270m = [p1.order(),p2.order()]271272for i,j in w:273M *= p[i]**(j%m[i])274275return M276277def _equalize_perms(l):278r"""279Transform a list of lists into a list of lists with identical length. Each280list ``p`` in the argument is completed with ``range(len(p),n)`` where281``n`` is the maximal length of the lists in ``l``. Note that the lists are282modified in-place (rather than returning new lists).283284EXAMPLES::285286sage: from sage.modular.arithgroup.arithgroup_perm import _equalize_perms287sage: l = [[],[1,0],[3,0,1,2]]288sage: _equalize_perms(l)289sage: l290[[0, 1, 2, 3], [1, 0, 2, 3], [3, 0, 1, 2]]291"""292n=max(map(len,l))293if n ==0:294n = 1295for p in l:296p.extend(xrange(len(p),n))297298# Tedious point: in order to unpickle pickled objects from prior to patch299# #11422, this function needs to accept two non-keyword arguments, to be300# interpreted as L and R. Hence the order of the arguments is slightly301# different from the class __init__ methods.302303def ArithmeticSubgroup_Permutation(304L=None, R=None, S2=None, S3=None,305relabel=False,306check=True):307r"""308Construct a subgroup of `{\rm SL}_2(\ZZ)` from the action of generators on its309right cosets.310311Return an arithmetic subgroup knowing the action, given by permutations, of312at least two standard generators on the its cosets. The generators313considered are the following matrices:314315.. math::316317s_2 = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix},\quad318s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad319l = \begin{pmatrix} 1 & 1 \\ 0 & 1\end{pmatrix},\quad320r = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix}.321322An error will be raised if only one permutation is given. If no arguments323are given at all, the full modular group `{\rm SL}(2, \ZZ)` is returned.324325INPUT:326327- ``S2``, ``S3``, ``L``, ``R`` - permutations - action of matrices on the328right cosets (each coset is identified to an element of `\{1,\dots,n\}`329where `1` is reserved for the identity coset).330331- ``relabel`` - boolean (default: False) - if True, renumber the cosets in a332canonical way.333334- ``check`` - boolean (default: True) - check that the input is valid (it335may be time efficient but less safe to set it to False)336337EXAMPLES::338339sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,3)")340sage: G341Arithmetic subgroup with permutations of right cosets342S2=(1,2)(3,4)343S3=(1,2,3)344L=(1,4,3)345R=(2,4,3)346sage: G.index()3474348349sage: G = ArithmeticSubgroup_Permutation(); G350Arithmetic subgroup with permutations of right cosets351S2=()352S3=()353L=()354R=()355sage: G == SL2Z356True357358Some invalid inputs::359360sage: ArithmeticSubgroup_Permutation(S2="(1,2)")361Traceback (most recent call last):362...363ValueError: Need at least two generators364sage: ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(3,4,5)")365Traceback (most recent call last):366...367ValueError: Permutations do not generate a transitive group368sage: ArithmeticSubgroup_Permutation(L="(1,2)",R="(1,2,3)")369Traceback (most recent call last):370...371ValueError: Wrong relations between generators372sage: ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="()")373Traceback (most recent call last):374...375ValueError: S2^2 does not equal to S3^3376sage: ArithmeticSubgroup_Permutation(S2="(1,4,2,5,3)", S3="(1,3,5,2,4)")377Traceback (most recent call last):378...379ValueError: S2^2 = S3^3 must have order 1 or 2380381The input checks can be disabled for speed::382383sage: ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(3,4,5)", check=False) # don't do this!384Arithmetic subgroup with permutations of right cosets385S2=(1,2)386S3=(3,4,5)387L=(1,2)(3,5,4)388R=(1,2)(3,4,5)389"""390gens = filter(lambda x: x is not None, [S2,S3,L,R])391if len(gens) == 0:392S2 = S3 = L = R = ''393elif len(gens) < 2:394raise ValueError, "Need at least two generators"395396if S2 is not None:397S2 = PermutationGroupElement(S2,check=check)398if S3 is not None:399S3 = PermutationGroupElement(S3,check=check)400if L is not None:401L = PermutationGroupElement(L,check=check)402if R is not None:403R = PermutationGroupElement(R,check=check)404405if L is not None:406if R is not None: # initialize from L,R407if S2 is None:408S2 = R * ~L * R409if S3 is None:410S3 = L * ~R411elif S2 is not None: # initialize from L,S2412if S3 is None:413S3 = ~S2 * ~L414if R is None:415R = ~S2 * ~L * S2416elif S3 is not None: # initialize from L,S3417if S2 is None:418S2 = ~L * ~S3419if R is None:420R = S3 * ~L * ~S3421elif R is not None:422if S2 is not None: # initialize from R, S2423if L is None:424L = ~S2 * ~R * S2425if S3 is None:426S3 = R * ~S2427elif S3 is not None: # initialize from R, S3428if L is None:429L = ~S3 * ~R * S3430if S2 is None:431S2 = ~S3 * R432else: # intialize from S2, S3433if L is None:434L = ~S3 * ~S2435if R is None:436R = S3 * S2437438if check and (L != ~S3 * ~S2 or R != S3 * S2):439raise ValueError, "Wrong relations between generators"440441inv = S2*S2442443if check:444if inv != S3*S3*S3:445raise ValueError, "S2^2 does not equal to S3^3"446elif not (inv*inv).is_one():447raise ValueError, "S2^2 = S3^3 must have order 1 or 2"448449# Check transitivity. This is the most expensive check, so we do it450# last.451from sage.groups.perm_gps.all import PermutationGroup452453G = PermutationGroup(gens)454if not G.is_transitive():455raise ValueError, "Permutations do not generate a transitive group"456457s2 = [i-1 for i in S2.domain()]458s3 = [i-1 for i in S3.domain()]459l = [i-1 for i in L.domain()]460r = [i-1 for i in R.domain()]461_equalize_perms((s2,s3,l,r))462463if inv.is_one(): # the group is even464G = EvenArithmeticSubgroup_Permutation(s2,s3,l,r)465else: # the group is odd466G = OddArithmeticSubgroup_Permutation(s2,s3,l,r)467468if relabel:469G.relabel()470471return G472473class ArithmeticSubgroup_Permutation_class(ArithmeticSubgroup):474r"""475A subgroup of `{\rm SL}_2(\ZZ)` defined by the action of generators on its476coset graph.477478The class stores the action of generators `s_2, s_3, l, r` on right cosets479`Hg` of a finite index subgroup `H < {\rm SL}_2(\ZZ)`. In particular the action of480`{\rm SL}_2(\ZZ)` on the cosets is on right.481482.. MATH::483484s_2 = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix},\quad485s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad486l = \begin{pmatrix} 1 & 1 \\ 0 & 1\end{pmatrix},\quad487r = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix}.488489TEST::490491sage: s2 = PermutationGroupElement('(1,2)(3,4)(5,6)')492sage: s3 = PermutationGroupElement('(1,3,5)(2,4,6)')493sage: G = ArithmeticSubgroup_Permutation(S2=s2, S3=s3)494sage: G.S2() == s2495True496sage: G.S3() == s3497True498sage: G == ArithmeticSubgroup_Permutation(L=G.L(), R=G.R())499True500sage: G == ArithmeticSubgroup_Permutation(L=G.L(), S2=G.S2())501True502sage: G == ArithmeticSubgroup_Permutation(L=G.L(), S3=G.S3())503True504sage: G == ArithmeticSubgroup_Permutation(R=G.R(), S2=G.S2())505True506sage: G == ArithmeticSubgroup_Permutation(R=G.R(), S3=G.S3())507True508sage: G == ArithmeticSubgroup_Permutation(S2=G.S2(), S3=G.S3())509True510511sage: G = ArithmeticSubgroup_Permutation(S2='',S3='')512sage: TestSuite(G).run()513514sage: S2 = '(1,2)(3,4)(5,6)'515sage: S3 = '(1,2,3)(4,5,6)'516sage: G = ArithmeticSubgroup_Permutation(S2=S2, S3=S3)517sage: TestSuite(G).run()518"""519520def __cmp__(self, other):521r"""522Equality test.523524TESTS::525526sage: G2 = Gamma(2)527sage: G3 = Gamma(3)528sage: H = ArithmeticSubgroup_Permutation(S2="(1,4)(2,6)(3,5)",S3="(1,2,3)(4,5,6)")529sage: (G2 == H) and (H == G2)530True531sage: (G3 == H) or (H == G3)532False533534sage: G2 = Gamma1(2)535sage: G3 = Gamma1(3)536sage: H = ArithmeticSubgroup_Permutation(S2="(1,6,4,3)(2,7,5,8)",S3="(1,2,3,4,5,6)(7,8)")537sage: (G2 == H) or (H == G2)538False539sage: (G3 == H) and (H == G3)540True541"""542if isinstance(other, ArithmeticSubgroup_Permutation_class):543544return (cmp(self.is_odd(), other.is_odd()) or545cmp(self.index(), other.index()) or546cmp(self.relabel(inplace=False)._S2, other.relabel(inplace=False)._S2) or547cmp(self.relabel(inplace=False)._S3, other.relabel(inplace=False)._S3))548549elif isinstance(other, ArithmeticSubgroup):550return cmp(self, other.as_permutation_group())551552else:553return cmp(type(self), type(other))554555def __hash__(self):556r"""557Return a hash value.558559TESTS::560561sage: G1 = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)(5,6)',S3='(1,2,3)(4,5,6)')562sage: G2 = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)(5,6)',S3='(1,5,6)(4,2,3)')563sage: G1.__hash__() == G2.__hash__()564False565"""566return hash((tuple(self.relabel(inplace=False)._S2),tuple(self.relabel(inplace=False)._S3)))567568def _repr_(self):569r"""570String representation of self.571572EXAMPLES::573574sage: G = Gamma(2).as_permutation_group()575sage: repr(G) #indirect doctest576'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)'577sage: G = Gamma(3).as_permutation_group()578sage: repr(G) #indirect doctest579'Arithmetic subgroup of index 24'580"""581if self.index() < 20:582return "Arithmetic subgroup with permutations of right cosets\n S2=%s\n S3=%s\n L=%s\n R=%s" %(583self.S2(), self.S3(), self.L(), self.R())584585else:586return "Arithmetic subgroup of index %d" %self.index()587588#589# Attribute access590#591592def S2(self):593r"""594Return the action of the matrix `s_2` as a permutation of cosets.595596.. MATH::597598s_2 = \begin{pmatrix}0&-1\\1&0\end{pmatrix}599600EXAMPLES::601602sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")603sage: G.S2()604(1,2)605"""606return PermutationGroupElement([i+1 for i in self._S2], check=False)607608def S3(self):609r"""610Return the action of the matrix `s_3` as a permutation of cosets.611612.. MATH::613614s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad615616EXAMPLES::617618sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")619sage: G.S3()620(1,2,3)621"""622623return PermutationGroupElement([i+1 for i in self._S3], check=False)624625def L(self):626r"""627Return the action of the matrix `l` as a permutation of cosets.628629.. MATH::630631l = \begin{pmatrix}1&1\\0&1\end{pmatrix}632633EXAMPLES::634635sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")636sage: G.L()637(1,3)638"""639return PermutationGroupElement([i+1 for i in self._L], check=False)640641def R(self):642r"""643Return the action of the matrix `r` as a permutation of cosets.644645.. MATH::646647r = \begin{pmatrix}1&0\\1&1\end{pmatrix}648649EXAMPLES::650651sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")652sage: G.R()653(2,3)654"""655return PermutationGroupElement([i+1 for i in self._R], check=False)656657def perm_group(self):658r"""659Return the underlying permutation group.660661The permutation group returned is isomorphic to the action of the662generators `s_2` (element of order two), `s_3` (element of order 3), `l`663(parabolic element) and `r` (parabolic element) on right cosets (the664action is on the right).665666EXAMPLE::667668sage: import sage.modular.arithgroup.arithgroup_perm as ap669sage: ap.HsuExample10().perm_group()670Permutation 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)]671"""672from sage.groups.perm_gps.all import PermutationGroup673return PermutationGroup([self.S2(), self.S3(), self.L(), self.R()])674675def index(self):676r"""677Returns the index of this modular subgroup in the full modular group.678679EXAMPLES::680681sage: G = Gamma(2)682sage: P = G.as_permutation_group()683sage: P.index()6846685sage: G.index() == P.index()686True687688sage: G = Gamma0(8)689sage: P = G.as_permutation_group()690sage: P.index()69112692sage: G.index() == P.index()693True694695sage: G = Gamma1(6)696sage: P = G.as_permutation_group()697sage: P.index()69824699sage: G.index() == P.index()700True701"""702return len(self._S2)703704#705# Canonical renumbering706#707708def relabel(self, inplace=True):709r"""710Relabel the cosets of this modular subgroup in a canonical way.711712The implementation of modular subgroup by action of generators on cosets713depends on the choice of a numbering. This function provides714canonical labels in the sense that two equal subgroups whith different715labels are relabeled the same way. The default implementation relabels716the group itself. If you want to get a relabeled copy of your modular717subgroup, put to ``False`` the option ``inplace``.718719ALGORITHM:720721We give an overview of how the canonical labels for the modular subgroup722are built. The procedure only uses the permutations `S3` and `S2` that723define the modular subgroup and can be used to renumber any724transitive action of the symmetric group. In other words, the algorithm725construct a canonical representative of a transitive subgroup in its726conjugacy class in the full symmetric group.7277281. The identity is still numbered `0` and set the curent vertex to be729the identity.7307312. Number the cycle of `S3` the current vertex belongs to: if the732current vertex is labeled `n` then the numbering in such way that the733cycle becomes `(n, n+1, \ldots, n+k)`).7347353. Find a new current vertex using the permutation `S2`.736If all vertices are relabeled then it's done, otherwise go to step 2.737738EXAMPLES::739740sage: S2 = "(1,2)(3,4)(5,6)"; S3 = "(1,2,3)(4,5,6)"741sage: G1 = ArithmeticSubgroup_Permutation(S2=S2,S3=S3); G1742Arithmetic subgroup with permutations of right cosets743S2=(1,2)(3,4)(5,6)744S3=(1,2,3)(4,5,6)745L=(1,4,5,3)746R=(2,4,6,3)747sage: G1.relabel(); G1748Arithmetic subgroup with permutations of right cosets749S2=(1,2)(3,4)(5,6)750S3=(1,2,3)(4,5,6)751L=(1,4,5,3)752R=(2,4,6,3)753754sage: S2 = "(1,2)(3,5)(4,6)"; S3 = "(1,2,3)(4,5,6)"755sage: G2 = ArithmeticSubgroup_Permutation(S2=S2,S3=S3); G2756Arithmetic subgroup with permutations of right cosets757S2=(1,2)(3,5)(4,6)758S3=(1,2,3)(4,5,6)759L=(1,5,6,3)760R=(2,5,4,3)761sage: G2.relabel(); G2762Arithmetic subgroup with permutations of right cosets763S2=(1,2)(3,4)(5,6)764S3=(1,2,3)(4,5,6)765L=(1,4,5,3)766R=(2,4,6,3)767768sage: S2 = "(1,2)(3,6)(4,5)"; S3 = "(1,2,3)(4,5,6)"769sage: G3 = ArithmeticSubgroup_Permutation(S2=S2,S3=S3); G3770Arithmetic subgroup with permutations of right cosets771S2=(1,2)(3,6)(4,5)772S3=(1,2,3)(4,5,6)773L=(1,6,4,3)774R=(2,6,5,3)775sage: G4 = G3.relabel(inplace=False)776sage: G4777Arithmetic subgroup with permutations of right cosets778S2=(1,2)(3,4)(5,6)779S3=(1,2,3)(4,5,6)780L=(1,4,5,3)781R=(2,4,6,3)782sage: G3 is G4783False784785TESTS::786787sage: S2 = "(1,2)(3,6)(4,5)"788sage: S3 = "(1,2,3)(4,5,6)"789sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)790sage: H = G.relabel(inplace=False)791sage: G is H792False793sage: G._S2 is H._S2 or G._S3 is H._S3 or G._L is H._L or G._R is H._R794False795sage: G.relabel(inplace=False) is H796True797sage: H.relabel(inplace=False) is H798True799sage: G.relabel(); G800Arithmetic subgroup with permutations of right cosets801S2=(1,2)(3,4)(5,6)802S3=(1,2,3)(4,5,6)803L=(1,4,5,3)804R=(2,4,6,3)805sage: G.relabel(inplace=False) is G806True807"""808if hasattr(self,'_canonical_label_group'):809if inplace:810if not (self is self._canonical_label_group):811self.__dict__ = self._canonical_label_group.__dict__812self._canonical_label_group = self813else:814return self._canonical_label_group815816if inplace:817G = self818else:819from copy import deepcopy820G = deepcopy(self)821822n = G.index()823mapping = G._canonical_rooted_labels()824S2 = G._S2825S3 = G._S3826L = G._L827R = G._R828G._S2 = [None]*n829G._S3 = [None]*n830G._L = [None]*n831G._R = [None]*n832833for i in xrange(n):834G._S2[mapping[i]] = mapping[S2[i]]835G._S3[mapping[i]] = mapping[S3[i]]836G._L[mapping[i]] = mapping[L[i]]837G._R[mapping[i]] = mapping[R[i]]838839self._canonical_label_group = G840G._canonical_label_group = G841842if not inplace:843return G844845def _canonical_unrooted_labels(self):846r"""847Returns the smallest label among rooted label848849OUTPUT:850851A 3-tuple of lists corresponding to permutations. The first list is the852mapping that gives the canonical labels and the second and third one853correspond to the permutations obtained by the conjugation of ``S2`` and854``S3``.855856EXAMPLES::857858sage: S2 = "(1,2)(4,5)"859sage: S3 = "(1,3,4)(2,5,6)"860sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)861sage: s2,s3 = G._S2,G._S3862sage: m,ss2,ss3 = G._canonical_unrooted_labels()863sage: all(ss2[m[i]] == m[s2[i]] for i in xrange(6))864True865sage: all(ss3[m[i]] == m[s3[i]] for i in xrange(6))866True867868sage: S2 = "(1,2)(3,4)(5,6)"869sage: S3 = "(1,3,4)(2,5,6)"870sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)871sage: s2,s3 = G._S2,G._S3872sage: m,ss2,ss3 = G._canonical_unrooted_labels()873sage: all(ss2[m[i]] == m[s2[i]] for i in xrange(6))874True875sage: all(ss3[m[i]] == m[s3[i]] for i in xrange(6))876True877878sage: S2 = "(1,2)(3,4)(5,6)"879sage: S3 = "(1,3,5)(2,4,6)"880sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)881sage: s2,s3 = G._S2,G._S3882sage: m,ss2,ss3 = G._canonical_unrooted_labels()883sage: all(ss2[m[i]] == m[s2[i]] for i in xrange(6))884True885sage: all(ss3[m[i]] == m[s3[i]] for i in xrange(6))886True887"""888n = self.index()889S2_win = [None]*n; S3_win = [None]*n890S2_test = [None]*n; S3_test = [None]*n891892m_win = self._canonical_rooted_labels(0)893for i in xrange(n): # conjugation894S2_win[m_win[i]] = m_win[self._S2[i]]895S3_win[m_win[i]] = m_win[self._S3[i]]896897for j0 in xrange(1,self.index()):898m_test = self._canonical_rooted_labels(j0)899for i in xrange(n):900S2_test[m_test[i]] = m_test[self._S2[i]]901S3_test[m_test[i]] = m_test[self._S3[i]]902903for i in xrange(n-1):904if (S2_test[i] < S2_win[i] or905(S2_test[i] == S2_win[i] and S3_test[i] < S3_win[i])):906S2_win,S2_test = S2_test,S2_win907S3_win,S3_test = S3_test,S3_win908m_win = m_test909break910911return m_win, S2_win, S3_win912913def _canonical_rooted_labels(self, j0=0):914r"""915Return the permutation which correspond to the renumbering of self in916order to get canonical labels.917918If ``j0`` is 0 then the renumbering corresponds to the same group. If919not, the renumbering corresponds to the conjugated subgroup such that920``j0`` becomes the identity coset.921922EXAMPLES::923924sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")925sage: G._canonical_rooted_labels(0)926[0, 1, 2]927sage: G._canonical_rooted_labels(1)928[2, 0, 1]929sage: G._canonical_rooted_labels(2)930[1, 2, 0]931"""932x = self._S3933y = self._S2934n = len(x)935936k = 0937mapping = [None] * n938waiting = []939940while True:941# intialize at j0942mapping[j0] = k943waiting.append(j0)944k += 1945# complete x cycle from j0946j = x[j0]947while j != j0:948mapping[j] = k949waiting.append(j)950k += 1951j = x[j]952953# if everybody is labelled do not go further954if k == n: break955956# find another guy with y957j0 = y[waiting.pop(0)]958while mapping[j0] is not None:959j0 = y[waiting.pop(0)]960961return mapping962963#964# Contains and random element965#966967@cached_method968def _index_to_lr_cusp_width(self):969r"""970Precomputation of cusps data of self for this modular subgroup.971972This is a central precomputation for the ``.__contains__()`` method and973consists in two lists of positive integers ``lc`` and ``rc`` of length974the index of the subgroup. They are defined as follows: the number975``lc[i]`` (resp ``rc[i]``) is the lenth of the cycle of ``L`` (resp.976``R``) which contains ``i``.977978EXAMPLES::979980sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")981sage: G.L()982(1,3)983sage: G.R()984(2,3)985sage: G._index_to_lr_cusp_width()986([2, 1, 2], [1, 2, 2])987"""988G = self.relabel(inplace=False)989990l = G.L()991l_cycle_length = [None]*self.index()992for c in l.cycle_tuples(singletons=True):993for i in c:994l_cycle_length[i-1]=len(c)995996r = G.R()997r_cycle_length = [None]*self.index()998for c in r.cycle_tuples(singletons=True):999for i in c:1000r_cycle_length[i-1]=len(c)10011002return (l_cycle_length, r_cycle_length)10031004def _contains_sl2(self, a,b,c,d):1005r"""1006Test whether ``[a,b;c,d]`` is in the group or not.10071008ALGORITHM:10091010An element of `{\rm SL}_2(\ZZ)` is in a given modular subgroup if it does not1011permute the identity coset!10121013TEST::10141015sage: G = Gamma(4)1016sage: m1 = G([1,4,0,1])1017sage: m2 = G([17,4,4,1])1018sage: m3 = G([1,-4,-4,17])1019sage: m4 = SL2Z([1,2,0,1])1020sage: P = G.as_permutation_group()1021sage: m1 in P1022True1023sage: m2 in P1024True1025sage: m3 in P1026True1027sage: m4 in P1028False1029"""1030w = sl2z_word_problem([a,b,c,d])10311032perms = [self.relabel(inplace=False)._L,self.relabel(inplace=False)._R]1033widths = self._index_to_lr_cusp_width()10341035k = 01036for (i,j) in w:1037for _ in xrange(j % widths[i][k]):1038k = perms[i][k]10391040return not k10411042def random_element(self, initial_steps=30):1043r"""1044Returns a random element in this subgroup.10451046The algorithm uses a random walk on the Cayley graph of `{\rm SL}_2(\ZZ)` stopped1047at the first time it reaches the subgroup after at least1048``initial_steps`` steps.10491050INPUT:10511052- ``initial_steps`` - positive integer (default: 30)10531054EXAMPLES::10551056sage: G = ArithmeticSubgroup_Permutation(S2='(1,3)(4,5)',S3='(1,2,5)(3,4,6)')1057sage: all(G.random_element() in G for _ in xrange(10))1058True1059"""1060from sage.misc.prandom import randint10611062i = 01063m = SL2Z(1)1064for _ in xrange(initial_steps):1065j = randint(0,1)1066if j == 0:1067i = self._S2[i]1068m = m*S2m1069else:1070i = self._S3[i]1071m = m*S3m10721073while i != 0:1074j = randint(0,1)1075if j == 0:1076i = self._S2[i]1077m = m*S2m1078else:1079i = self._S3[i]1080m = m*S3m10811082return m10831084def permutation_action(self, x):1085r"""1086Given an element ``x`` of `{\rm SL}_2(\ZZ)`, compute the permutation of the1087cosets of self given by right multiplication by ``x``.10881089EXAMPLE::10901091sage: import sage.modular.arithgroup.arithgroup_perm as ap1092sage: ap.HsuExample10().permutation_action(SL2Z([32, -21, -67, 44]))1093(1,4,6,2,10,5,3,7,8,9)1094"""1095return word_of_perms(sl2z_word_problem(x), self.L(), self.R())10961097#1098# Group stuff1099#11001101def is_normal(self):1102r"""1103Test whether the group is normal11041105EXAMPLES::11061107sage: G = Gamma(2).as_permutation_group()1108sage: G.is_normal()1109True11101111sage: G = Gamma1(2).as_permutation_group()1112sage: G.is_normal()1113False1114"""1115N = self.index()1116G = self.relabel(inplace=False)1117s2 = G._S21118s3 = G._S31119ss2 = [None]*N1120ss3 = [None]*N1121for j in [s2[0],s3[0]]:1122m = G._canonical_rooted_labels(j)1123for i in xrange(N):1124ss2[m[i]] = m[s2[i]]1125ss3[m[i]] = m[s3[i]]1126if s2 != ss2 or s3 != ss3:1127return False1128return True11291130def _conjugate(self,j0):1131r"""1132Return the conjugate of self rooted at j0.11331134EXAMPLES::11351136sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)(4,5,6)')1137sage: G1138Arithmetic subgroup with permutations of right cosets1139S2=(1,2)(3,4)1140S3=(1,2,3)(4,5,6)1141L=(1,4,6,5,3)1142R=(2,4,5,6,3)1143sage: G._conjugate(0) == G1144True1145sage: G._conjugate(4)1146Arithmetic subgroup with permutations of right cosets1147S2=(3,4)(5,6)1148S3=(1,2,3)(4,5,6)1149L=(1,4,5,3,2)1150R=(1,2,4,6,3)1151"""1152N = self.index()1153s2 = self._S21154s3 = self._S31155l = self._L1156r = self._R1157ss2 = [None]*N1158ss3 = [None]*N1159ll = [None]*N1160rr = [None]*N11611162m = self._canonical_rooted_labels(j0)1163for i in xrange(N):1164ss2[m[i]] = m[s2[i]]1165ss3[m[i]] = m[s3[i]]1166ll[m[i]] = m[l[i]]1167rr[m[i]] = m[r[i]]1168return self.__class__(ss2,ss3,ll,rr,True)11691170def coset_graph(self,1171right_cosets=False,1172s2_edges=True, s3_edges=True, l_edges=False, r_edges=False,1173s2_label='s2', s3_label='s3', l_label='l', r_label='r'):1174r"""1175Return the right (or left) coset graph.11761177INPUT:11781179- ``right_cosets`` - bool (default: False) - right or left coset graph11801181- ``s2_edges`` - bool (default: True) - put edges associated to s211821183- ``s3_edges`` - bool (default: True) - put edges associated to s311841185- ``l_edges`` - bool (default: False) - put edges associated to l11861187- ``r_edges`` - bool (default: False) - put edges associated to r11881189- ``s2_label``, ``s3_label``, ``l_label``, ``r_label`` - the labels to1190put on the edges corresponding to the generators action. Use ``None``1191for no label.11921193EXAMPLES::11941195sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="()")1196sage: G1197Arithmetic subgroup with permutations of right cosets1198S2=(1,2)1199S3=()1200L=(1,2)1201R=(1,2)1202sage: G.index()120321204sage: G.coset_graph()1205Looped multi-digraph on 2 vertices1206"""1207from sage.graphs.digraph import DiGraph1208res = DiGraph(multiedges=True,loops=True)1209res.add_vertices(range(self.index()))121012111212if right_cosets: # invert the permutations1213S2 = [None]*self.index()1214S3 = [None]*self.index()1215L = [None]*self.index()1216R = [None]*self.index()1217for i in xrange(self.index()):1218S2[self._S2[i]] = i1219S3[self._S3[i]] = i1220L[self._L[i]] = i1221R[self._R[i]] = i12221223else:1224S2 = self._S21225S3 = self._S31226L = self._L1227R = self._R12281229if s2_edges:1230if s2_label is not None:1231res.add_edges((i,S2[i],s2_label) for i in xrange(self.index()))1232else:1233res.add_edges((i,S2[i]) for i in xrange(self.index()))12341235if s3_edges:1236if s3_label is not None:1237res.add_edges((i,S3[i],s3_label) for i in xrange(self.index()))1238else:1239res.add_edges((i,S3) for i in xrange(self.index()))12401241if l_edges:1242if l_label is not None:1243res.add_edges((i,L[i],l_label) for i in xrange(self.index()))1244else:1245res.add_edges((i,L[i]) for i in xrange(self.index()))12461247if r_edges:1248if r_label is not None:1249res.add_edges((i,R[i],r_label) for i in xrange(self.index()))1250else:1251res.add_edges((i,R[i]) for i in xrange(self.index()))12521253res.plot.options['color_by_label'] = True12541255if s2_label or s3_label or l_label or r_label:1256res.plot.options['edge_labels'] = True12571258return res12591260def generalised_level(self):1261r"""1262Return the generalised level of this subgroup.12631264The *generalised level* of a subgroup of the modular group is the least1265common multiple of the widths of the cusps. It was proven by Wohlfart1266that for even congruence subgroups, the (conventional) level coincides1267with the generalised level. For odd congruence subgroups the level is1268either the generalised level, or twice the generalised level [KSV11]_.12691270EXAMPLES::12711272sage: G = Gamma(2).as_permutation_group()1273sage: G.generalised_level()127421275sage: G = Gamma0(3).as_permutation_group()1276sage: G.generalised_level()127731278"""1279return arith.lcm(self.cusp_widths())12801281def congruence_closure(self):1282r"""1283Returns the smallest congruence subgroup containing self. If self is1284congruence, this is just self, but represented as a congruence subgroup1285data type. If self is not congruence, then it may be larger.12861287In practice, we use the following criterion: let `m` be the generalised1288level of self. If this subgroup is even, let `n = m`, else let `n =12892m`. Then any congruence subgroup containing self contains `\Gamma(n)`1290(a generalisation of Wohlfahrt's theorem due to Kiming, Verrill and1291Schuett). So we compute the image of self modulo `n` and return the1292preimage of that.12931294EXAMPLE::12951296sage: Gamma1(3).as_permutation_group().congruence_closure()1297Congruence subgroup of SL(2,Z) of level 3, preimage of:1298Matrix group over Ring of integers modulo 3 with 2 generators (1299[1 2] [1 1]1300[0 1], [0 1]1301)1302sage: sage.modular.arithgroup.arithgroup_perm.HsuExample10().congruence_closure() # long time (11s on sage.math, 2012)1303Modular Group SL(2,Z)1304"""1305if self.is_even():1306N = self.generalised_level()1307else:1308N = 2*self.generalised_level()13091310from congroup_generic import CongruenceSubgroup_constructor as CS1311return CS(N, [x.matrix() for x in self.gens()])13121313class OddArithmeticSubgroup_Permutation(ArithmeticSubgroup_Permutation_class):1314def __init__(self, S2, S3, L, R, canonical_labels=False):1315r"""1316TESTS::13171318sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")1319sage: G1320Arithmetic subgroup with permutations of right cosets1321S2=(1,2,3,4)1322S3=(1,3)(2,4)1323L=(1,2,3,4)1324R=(1,4,3,2)1325sage: TestSuite(G).run()1326"""1327self._S2 = S21328self._S3 = S31329self._L = L1330self._R = R1331if canonical_labels:1332self._canonical_label_group = self1333ArithmeticSubgroup_Permutation_class.__init__(self)13341335def __reduce__(self):1336r"""1337Return the data used to construct self. Used in pickling.13381339TESTS::13401341sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")1342sage: G == loads(dumps(G)) #indirect doctest1343True1344sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)",relabel=True)1345sage: GG = loads(dumps(G))1346sage: GG == G #indirect doctest1347True1348sage: GG.relabel(inplace=False) is GG1349True1350"""1351if hasattr(self,'_canonical_label_group'):1352canonical_labels = (self is self._canonical_label_group)1353else:1354canonical_labels = False1355return (OddArithmeticSubgroup_Permutation,1356(self._S2,self._S3,self._L,self._R,canonical_labels))13571358def is_odd(self):1359r"""1360Test whether the group is odd.13611362EXAMPLES::13631364sage: G = ArithmeticSubgroup_Permutation(S2="(1,6,4,3)(2,7,5,8)",S3="(1,2,3,4,5,6)(7,8)")1365sage: G.is_odd()1366True1367"""1368return True13691370def is_even(self):1371r"""1372Test whether the group is even.13731374EXAMPLES::13751376sage: G = ArithmeticSubgroup_Permutation(S2="(1,6,4,3)(2,7,5,8)",S3="(1,2,3,4,5,6)(7,8)")1377sage: G.is_even()1378False1379"""1380return False13811382def to_even_subgroup(self,relabel=True):1383r"""1384Returns the group with `-Id` added in it.13851386EXAMPLES::13871388sage: G = Gamma1(3).as_permutation_group()1389sage: G.to_even_subgroup()1390Arithmetic subgroup with permutations of right cosets1391S2=(1,3)(2,4)1392S3=(1,2,3)1393L=(2,3,4)1394R=(1,4,2)13951396sage: 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)')1397sage: G = H.to_even_subgroup(relabel=False); G1398Arithmetic subgroup with permutations of right cosets1399S2=(1,4)(2,7)(3,5)(6,9)(8,10)1400S3=(1,2,3)(4,5,6)(7,8,9)1401L=(1,5)(2,4,9,10,8)(3,7,6)1402R=(1,7,10,8,6)(2,5,9)(3,4)1403sage: H.is_subgroup(G)1404True1405"""1406N = self.index()14071408# build equivalence classes in e1409s2 = self._S21410e = []1411e2i = [None]*N1412for i in xrange(N):1413j = s2[s2[i]]1414if i < j:1415e2i[i] = e2i[j] = len(e)1416e.append((i,j))14171418# build the quotient permutations1419ss2 = [None]*(N/2)1420ss3 = [None]*(N/2)1421ll = [None]*(N/2)1422rr = [None]*(N/2)14231424s3 = self._S31425l = self._L1426r = self._R1427for (j0,j1) in e:1428ss2[e2i[j0]] = e2i[s2[j0]]1429ss3[e2i[j0]] = e2i[s3[j0]]1430ll[e2i[j0]] = e2i[l[j0]]1431rr[e2i[j0]] = e2i[r[j0]]14321433G = EvenArithmeticSubgroup_Permutation(ss2,ss3,ll,rr)1434if relabel:1435G.relabel()1436return G14371438def nu2(self):1439r"""1440Return the number of elliptic points of order 2.14411442EXAMPLES::14431444sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")1445sage: G.nu2()1446014471448sage: G = Gamma1(2).as_permutation_group()1449sage: G.nu2()145011451"""1452return sum(1 for c in self.S2().cycle_tuples() if len(c) == 2)14531454def nu3(self):1455r"""1456Return the number of elliptic points of order 3.14571458EXAMPLES::14591460sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")1461sage: G.nu3()1462214631464sage: G = Gamma1(3).as_permutation_group()1465sage: G.nu3()146611467"""1468return sum(1 for c in self.S3().cycle_tuples() if len(c) == 2)14691470def nirregcusps(self):1471r"""1472Return the number of irregular cusps.14731474The cusps are associated to cycles of the permutations `L` or `R`.1475The irregular cusps are the one which are stabilised by `-Id`.14761477EXAMPLES::14781479sage: S2 = "(1,3,2,4)(5,7,6,8)(9,11,10,12)"1480sage: S3 = "(1,3,5,2,4,6)(7,9,11,8,10,12)"1481sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)1482sage: G.nirregcusps()148331484"""1485inv = self.S2()**21486n = 01487for c in self.L().cycle_tuples(singletons=True):1488if inv(c[0]) in c:1489n += 11490return n14911492def nregcusps(self):1493r"""1494Return the number of regular cusps of the group.14951496The cusps are associated to cycles of `L` or `R`. The irregular cusps1497correspond to the ones which are not stabilised by `-Id`.14981499EXAMPLES::15001501sage: G = Gamma1(3).as_permutation_group()1502sage: G.nregcusps()150321504"""1505inv = self.S2()**21506n = 01507for c in self.L().cycle_tuples(singletons=True):1508if inv(c[0]) not in c:1509n += 11510return n//215111512def cusp_widths(self,exp=False):1513r"""1514Return the list of cusp widths.15151516INPUT:15171518``exp`` - boolean (default: False) - if True, return a dictionnary with1519keys the possible widths and with values the number of cusp with that1520width.15211522EXAMPLES::15231524sage: G = Gamma1(5).as_permutation_group()1525sage: G.cusp_widths()1526[1, 1, 5, 5]1527sage: G.cusp_widths(exp=True)1528{1: 2, 5: 2}1529"""1530inv = self.S2()**21531L = self.L()1532cusps = set(c[0] for c in L.cycle_tuples(singletons=True))1533if exp:1534widths = {}1535else:1536widths = []15371538while cusps:1539c0 = cusps.pop()1540c = L.orbit(c0)1541if inv(c0) not in c:1542c1 = min(L.orbit(inv(c0)))1543cusps.remove(c1)1544if exp:1545if not len(c) in widths:1546widths[len(c)] = 01547widths[len(c)] += 11548else:1549widths.append(len(c))1550else:1551if exp:1552if not len(c)/2 in widths:1553widths[len(c)/2] = 01554widths[len(c)/2] += 11555else:1556widths.append(len(c)/2)15571558if exp:1559return widths1560return sorted(widths)15611562def ncusps(self):1563r"""1564Returns the number of cusps.15651566EXAMPLES::15671568sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")1569sage: G.ncusps()1570115711572sage: G = Gamma1(3).as_permutation_group()1573sage: G.ncusps()157421575"""1576inv = self.S2()**21577n = 01578m = 01579for c in self.L().cycle_tuples(singletons=True):1580if inv(c[0]) in c:1581n += 11582else:1583m += 11584return n + m//215851586def is_congruence(self):1587r"""1588Test whether self is a congruence group, i.e.~whether or not it1589contains the subgroup `\Gamma(n)` for some `n`.15901591For odd groups, we first test whether the group generated by `G` and1592`-1` is congruence, and then use a theorem of Kiming, Schuett and1593Verrill [KSV11]_, which shows that an odd subgroup is congruence if and1594only if it contains `\Gamma(N)` where `N` is twice its generalised1595level (the least common multiple of its cusp widths). We can therefore1596proceed by calculating the index of the subgroup of `{\rm SL}(2, \ZZ /1597N\ZZ)` generated by the gens of self, and checking whether or not it1598has the same index as self.15991600EXAMPLES::16011602sage: GammaH(11,[4]).as_permutation_group().is_congruence()1603True16041605The following example (taken from [KSV11]_) shows that it may be the1606case that G is not congruence, even if its image in `{\rm PSL}(2,\ZZ)`1607is congruence::16081609sage: 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)"1610sage: 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)"1611sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)1612sage: G.is_congruence()1613False1614sage: G.to_even_subgroup().is_congruence()1615True16161617In fact G is a lifting to `{\rm SL}(2,\ZZ)` of the group1618`\bar{\Gamma}_0(6)`::16191620sage: G.to_even_subgroup() == Gamma0(6)1621True1622"""1623Gev = self.to_even_subgroup()1624if not Gev.is_congruence():1625return False1626else:1627N = 2*self.generalised_level()1628from sage.groups.matrix_gps.all import MatrixGroup1629H = MatrixGroup([x.matrix().change_ring(Zmod(N)) for x in self.gens()])1630from congroup_gamma import Gamma_constructor as Gamma1631return Gamma(N).index() == self.index() * H.order()16321633class EvenArithmeticSubgroup_Permutation(ArithmeticSubgroup_Permutation_class):1634r"""1635An arithmetic subgroup `\Gamma` defined by two permutations, giving the1636action of four standard generators16371638.. MATH::16391640s_2 = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix},\quad1641s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad1642l = \begin{pmatrix} 1 & 1 \\ 0 & 1\end{pmatrix},\quad1643r = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix}.16441645by right multiplication on the coset representatives `\Gamma \backslash {\rm SL}_2(\ZZ)`.164616471648EXAMPLES:16491650Construct a noncongruence subgroup of index 7 (the smallest possible)::16511652sage: a2 = SymmetricGroup(7)([(1,2),(3,4),(6,7)]); a3 = SymmetricGroup(7)([(1,2,3),(4,5,6)])1653sage: G = ArithmeticSubgroup_Permutation(S2=a2, S3=a3); G1654Arithmetic subgroup with permutations of right cosets1655S2=(1,2)(3,4)(6,7)1656S3=(1,2,3)(4,5,6)1657L=(1,4,7,6,5,3)1658R=(2,4,5,7,6,3)1659sage: G.index()166071661sage: G.dimension_cusp_forms(4)166211663sage: G.is_congruence()1664False16651666Convert some standard congruence subgroups into permutation form::16671668sage: G = Gamma0(8).as_permutation_group()1669sage: G.index()1670121671sage: G.is_congruence()1672True16731674sage: G = Gamma0(12).as_permutation_group()1675sage: print G1676Arithmetic subgroup of index 241677sage: G.is_congruence()1678True16791680The following is the unique index 2 even subgroup of `{\rm SL}_2(\ZZ)`::16811682sage: w = SymmetricGroup(2)([2,1])1683sage: G = ArithmeticSubgroup_Permutation(L=w, R=w)1684sage: G.dimension_cusp_forms(6)168511686sage: G.genus()168701688"""1689def __init__(self, S2, S3, L, R, canonical_labels=False):1690r"""1691TESTS::16921693sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)(5,6)",S3="(1,2,3)(4,5,6)")1694sage: G == loads(dumps(G))1695True1696sage: G is loads(dumps(G))1697False1698"""1699self._S2 = S21700self._S3 = S31701self._L = L1702self._R = R1703if canonical_labels:1704self._canonical_label_group = self1705ArithmeticSubgroup_Permutation_class.__init__(self)17061707def __reduce__(self):1708r"""1709Data for pickling.17101711TESTS::17121713sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,4)")1714sage: G == loads(dumps(G)) #indirect doctest1715True1716sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,4)",relabel=True)1717sage: GG = loads(dumps(G))1718sage: G == GG #indirect doctest1719True1720sage: GG.relabel(inplace=False) is GG1721True1722"""1723if hasattr(self, '_canonical_label_group'):1724canonical_labels = (self is self._canonical_label_group)1725else:1726canonical_labels = False1727return (EvenArithmeticSubgroup_Permutation,1728(self._S2, self._S3, self._L, self._R, canonical_labels))17291730def is_odd(self):1731r"""1732Returns True if this subgroup does not contain the matrix `-Id`.17331734EXAMPLES::17351736sage: G = Gamma(2).as_permutation_group()1737sage: G.is_odd()1738False1739"""1740return False17411742def is_even(self):1743r"""1744Returns True if this subgroup contains the matrix `-Id`.17451746EXAMPLES::17471748sage: G = Gamma(2).as_permutation_group()1749sage: G.is_even()1750True1751"""1752return True17531754def nu2(self):1755r"""1756Returns the number of orbits of elliptic points of order 2 for this1757arithmetic subgroup.17581759EXAMPLES::17601761sage: G = ArithmeticSubgroup_Permutation(S2="(1,4)(2)(3)",S3="(1,2,3)(4)")1762sage: G.nu2()176321764"""1765return sum(1 for i in xrange(self.index()) if self._S2[i] == i)17661767def nu3(self):1768r"""1769Returns the number of orbits of elliptic points of order 3 for this1770arithmetic subgroup.17711772EXAMPLES::17731774sage: G = ArithmeticSubgroup_Permutation(S2="(1,4)(2)(3)",S3="(1,2,3)(4)")1775sage: G.nu3()177611777"""1778return sum(1 for i in xrange(self.index()) if self._S3[i] == i)17791780def ncusps(self):1781r"""1782Return the number of cusps of this arithmetic subgroup.17831784EXAMPLES::17851786sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)(5,6)",S3="(1,2,3)(4,5,6)")1787sage: G.ncusps()178831789"""1790return len(self.L().cycle_tuples(True))17911792def _spanning_tree_kulkarni(self, root=0, on_right=True):1793r"""1794Returns a spanning tree for the coset graph of the group for the1795generators `S2` and `S3`.17961797Warning: the output is randomized in order to be able to obtain any1798spanning tree of the coset graph. The algorithm mainly follows1799Kulkarni's paper.18001801INPUT:18021803- ``on_right`` - boolean (default: True) - if False, return spanning1804tree for the left cosets.18051806OUTPUT:18071808- ``tree`` - a spanning tree of the graph associated to the action of1809``L`` and ``S2`` on the cosets18101811- ``reps`` - list of matrices in `{\rm SL}_2(\ZZ)`` - representatives of the1812cosets with respect to the spanning tree18131814- ``word_reps`` - list of lists with ``s2`` and ``s3`` - word1815representatives of the cosets with respect to the spanning tree.18161817- ``gens`` - list of 3-tuples ``(in,out,label)`` - the list of edges in1818the graph which are not in the spanning tree.18191820EXAMPLES::18211822sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)')1823sage: tree,reps,wreps,gens = G._spanning_tree_kulkarni()1824sage: tree1825Digraph on 4 vertices1826sage: for m in reps: print m,"\n****"1827[1 0]1828[0 1]1829****1830[ 0 1]1831[-1 1]1832****1833[-1 1]1834[-1 0]1835****1836[1 1]1837[0 1]1838****1839sage: for w in wreps: print ','.join(w)1840<BLANKLINE>1841s31842s3,s31843s3,s3,s21844sage: gens1845[(0, 1, 's2'), (3, 3, 's3')]1846"""1847from sage.graphs.digraph import DiGraph1848from sage.misc.prandom import randint18491850N = len(self._S2)18511852if on_right:1853s2 = self._S21854s3 = self._S318551856else:1857s2 = [None] * N1858s3 = [None] * N1859for i in xrange(N):1860s2[self._S2[i]] = i1861s3[self._S3[i]] = i18621863# the tree and the lift1864tree = DiGraph(multiedges=False,loops=False)1865gens = []18661867reps = [None]*self.index()1868word_reps = [None]*self.index()1869reps[root] = SL2Z(1)1870word_reps[root] = []18711872x0 = root1873tree.add_vertex(x0)1874l = [x0]18751876while True:1877# complete the current 3-loop in the tree1878if s3[x0] != x0: # loop of length 31879x1 = s3[x0]1880x2 = s3[x1]1881tree.add_edge(x0,x1,'s3')1882tree.add_edge(x1,x2,'s3')1883if on_right:1884reps[x1] = reps[x0] * S3m1885reps[x2] = reps[x1] * S3m1886word_reps[x1] = word_reps[x0] + ['s3']1887word_reps[x2] = word_reps[x1] + ['s3']1888else:1889reps[x1] = S3m * reps[x0]1890reps[x2] = S3m * reps[x1]1891word_reps[x1] = ['s3'] + word_reps[x0]1892word_reps[x2] = ['s3'] + word_reps[x1]1893l.append(x1)1894l.append(x2)1895else: # elliptic generator1896gens.append((x0,x0,'s3'))18971898# now perform links with s while we find another guy1899while l:1900x1 = l.pop(randint(0,len(l)-1))1901x0 = s2[x1]19021903if x1 != x0: # loop of length 21904if x0 in tree:1905gens.append((x1,x0,'s2'))1906del l[l.index(x0)] # x0 must be in l1907else:1908tree.add_edge(x1,x0,'s2')1909if on_right:1910reps[x0] = reps[x1] * S2m1911word_reps[x0] = word_reps[x1] + ['s2']1912else:1913reps[x0] = S2m * reps[x1]1914word_reps[x0] = ['s2'] + word_reps[x1]1915break1916else: # elliptic generator1917gens.append((x1,x1,'s2'))19181919else:1920break19211922return tree,reps,word_reps,gens19231924def _spanning_tree_verrill(self, root=0, on_right=True):1925r"""1926Returns a spanning tree with generators `S2` and `L`.19271928The algorithm follows the one of Helena Verrill.19291930OUTPUT:19311932- ``tree`` - a spanning tree of the graph associated to the action of1933``L`` and ``S2`` on the cosets19341935- ``reps`` - list of matrices in `{\rm SL}_2(\ZZ)` - representatives of the1936cosets with respect to the spanning tree19371938- ``word_reps`` - list of string with ``s`` and ``l`` - word1939representatives of the cosets with respect to the spanning tree.19401941- ``gens`` - list of 3-tuples ``(in,out,label)`` - the list of edges in1942the graph which are not in the spanning tree.19431944EXAMPLES::19451946sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)')1947sage: tree,reps,wreps,gens=G._spanning_tree_verrill()1948sage: tree1949Digraph on 4 vertices1950sage: for m in reps: print m, "\n****"1951[1 0]1952[0 1]1953****1954[ 0 -1]1955[ 1 0]1956****1957[1 2]1958[0 1]1959****1960[1 1]1961[0 1]1962****1963sage: wreps1964['', 's', 'll', 'l']1965sage: gens1966[(2, 0, 'l'), (1, 1, 'l'), (2, 3, 's')]19671968TODO:19691970Take care of the shape of the spanning tree as in Helena Verrill's program.1971"""1972from sage.misc.prandom import randint19731974if on_right:1975s = self._S21976l = self._L1977else:1978s = [None]*self.index()1979l = [None]*self.index()1980for i in xrange(self.index()):1981s[self._S2[i]] = i1982l[self._L[i]] = i19831984from sage.graphs.digraph import DiGraph1985tree = DiGraph(multiedges=False,loops=False)1986gens = []19871988reps = [None]*self.index()1989word_reps = [None]*self.index()1990reps[root] = SL2Z(1)1991word_reps[root] = ''19921993x0 = root1994tree.add_vertex(x0)1995waiting = [x0]19961997while True:1998# complete the current l-loop in the tree from x01999x = x02000xx = l[x]2001while xx != x0:2002tree.add_edge(x,xx,'l')2003if on_right:2004reps[xx] = reps[x] * Lm2005word_reps[xx] = word_reps[x] + 'l'2006else:2007reps[xx] = Lm * reps[x]2008word_reps[xx] = 'l' + word_reps[x]2009waiting.append(xx)2010x = xx2011xx = l[x]20122013gens.append((x,x0,'l'))20142015# now perform links with s while we find another guy which will2016# become the new x02017while waiting:2018x0 = None2019while waiting and x0 is None:2020x1 = waiting.pop(randint(0,len(waiting)-1))2021x0 = s[x1]20222023if x0 is not None:2024if x1 != x0: # loop of length 22025if x0 in tree:2026gens.append((x1,x0,'s'))2027if x0 in waiting:2028del waiting[waiting.index(x0)] # x0 must be in l2029else:2030tree.add_edge(x1,x0,'s')2031if on_right:2032reps[x0] = reps[x1] * S2m2033word_reps[x0] = word_reps[x1] + 's'2034else:2035reps[x0] = S2m * reps[x1]2036word_reps[x0] = 's' + word_reps[x1]2037break2038else: # elliptic generator2039gens.append((x1,x1,'s'))20402041else:2042break20432044return tree,reps,word_reps,gens20452046def todd_coxeter_s2_s3(self):2047r"""2048Returns a 4-tuple ``(coset_reps, gens, s2, s3)`` where ``coset_reps``2049are coset representatives of the subgroup, ``gens`` is a list of2050generators, ``s2`` and ``s3`` are the action of the matrices `S2` and2051`S3` on the list of cosets.20522053The so called *Todd-Coxeter algorithm* is a general method for coset2054enumeration for a subgroup of a group given by generators and relations.20552056EXAMPLES::20572058sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)')2059sage: G.genus()206002061sage: reps,gens,s2,s3=G.todd_coxeter_s2_s3()2062sage: g1,g2 = gens2063sage: g1 in G and g2 in G2064True2065sage: g12066[-1 0]2067[ 1 -1]2068sage: g22069[-1 3]2070[-1 2]2071sage: S2 = SL2Z([0,-1,1,0])2072sage: S3 = SL2Z([0,1,-1,1])2073sage: reps[0] == SL2Z([1,0,0,1])2074True2075sage: all(reps[i]*S2*~reps[s2[i]] in G for i in xrange(4))2076True2077sage: all(reps[i]*S3*~reps[s3[i]] in G for i in xrange(4))2078True2079"""2080tree,reps,wreps,edges = self._spanning_tree_kulkarni()20812082gens = []2083for e in edges:2084if e[2] == 's2':2085gens.append(self(reps[e[0]] * S2m * ~reps[e[1]]))2086elif e[2] == 's3':2087gens.append(self(reps[e[0]] * S3m * ~reps[e[1]]))2088else:2089raise ValueError, "this should not happen"20902091return reps, gens, self._S2[:], self._S3[:]20922093def todd_coxeter_l_s2(self):2094r"""2095Returns a 4-tuple ``(coset_reps, gens, l, s2)`` where ``coset_reps`` is2096a list of coset representatives of the subgroup, ``gens`` a list of2097generators, ``l`` and ``s2`` are list that corresponds to the action of2098the matrix `S2` and `L` on the cosets.20992100EXAMPLES::21012102sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)')2103sage: reps,gens,l,s=G.todd_coxeter_l_s2()2104sage: reps2105[2106[1 0] [ 0 -1] [1 2] [1 1]2107[0 1], [ 1 0], [0 1], [0 1]2108]2109sage: gens2110[2111[1 3] [ 1 0] [ 2 -3]2112[0 1], [-1 1], [ 1 -1]2113]2114sage: l2115[3, 1, 0, 2]2116sage: s2117[1, 0, 3, 2]2118sage: S2 = SL2Z([0,-1,1,0])2119sage: L = SL2Z([1,1,0,1])2120sage: reps[0] == SL2Z([1,0,0,1])2121True2122sage: all(reps[i]*S2*~reps[s[i]] in G for i in xrange(4))2123True2124sage: all(reps[i]*L*~reps[l[i]] in G for i in xrange(4))2125True2126"""2127tree,reps,wreps,edges = self._spanning_tree_verrill()21282129gens = []2130for e in edges:2131if e[2] == 'l':2132gens.append(self(reps[e[0]] * Lm * ~reps[e[1]]))2133elif e[2] == 's':2134gens.append(self(reps[e[0]] * S2m * ~reps[e[1]]))2135else:2136raise ValueError, "this should not happen"21372138return reps, gens, self._L[:], self._S2[:]21392140todd_coxeter = todd_coxeter_l_s221412142def coset_reps(self):2143r"""2144Return coset representatives.21452146EXAMPLES::21472148sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,3)")2149sage: c = G.coset_reps()2150sage: len(c)215142152sage: [g in G for g in c]2153[True, False, False, False]2154"""2155return self.todd_coxeter()[0]21562157def cusp_widths(self,exp=False):2158r"""2159Return the list of cusp widths of the group.21602161EXAMPLES::21622163sage: G = Gamma(2).as_permutation_group()2164sage: G.cusp_widths()2165[2, 2, 2]2166sage: G.cusp_widths(exp=True)2167{2: 3}21682169sage: S2 = "(1,2)(3,4)(5,6)"2170sage: S3 = "(1,2,3)(4,5,6)"2171sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)2172sage: G.cusp_widths()2173[1, 1, 4]2174sage: G.cusp_widths(exp=True)2175{1: 2, 4: 1}21762177sage: S2 = "(1,2)(3,4)(5,6)"2178sage: S3 = "(1,3,5)(2,4,6)"2179sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)2180sage: G.cusp_widths()2181[6]2182sage: G.cusp_widths(exp=True)2183{6: 1}2184"""2185seen = [True]*self.index()21862187if exp:2188widths = {}2189else:2190widths = []2191for i in xrange(self.index()):2192if seen[i]:2193seen[i] = False2194j = self._L[i]2195n = 12196while j != i:2197seen[j] = False2198n += 12199j = self._L[j]2200if exp:2201if n not in widths:2202widths[n] = 02203widths[n] += 12204else:2205widths.append(n)22062207if exp:2208return widths2209return sorted(widths)22102211def to_even_subgroup(self, relabel=True):2212r"""2213Return the subgroup generated by self and ``-Id``. Since self is even,2214this is just self. Provided for compatibility.22152216EXAMPLE::22172218sage: G = Gamma0(4).as_permutation_group()2219sage: H = G.to_even_subgroup()2220sage: H == G2221True2222"""2223if relabel:2224return self.relabel(inplace=False)2225else:2226return self22272228def is_congruence(self):2229r"""2230Return True if this is a congruence subgroup.22312232ALGORITHM:22332234Uses Hsu's algorithm [Hsu96]_. Adapted from Chris Kurth's2235implementation in KFarey [Kur08]_.22362237EXAMPLES:22382239Test if `{\rm SL}_2(\ZZ)` is congruence::22402241sage: a = ArithmeticSubgroup_Permutation(L='',R='')2242sage: a.index()224312244sage: a.is_congruence()2245True22462247This example is congruence -- it's Gamma0(3) in disguise: ::22482249sage: S2 = SymmetricGroup(4)2250sage: l = S2((2,3,4))2251sage: r = S2((1,3,4))2252sage: G = ArithmeticSubgroup_Permutation(L=l,R=r)2253sage: print G2254Arithmetic subgroup with permutations of right cosets2255S2=(1,2)(3,4)2256S3=(1,4,2)2257L=(2,3,4)2258R=(1,3,4)2259sage: G.is_congruence()2260True22612262This one is noncongruence: ::22632264sage: import sage.modular.arithgroup.arithgroup_perm as ap2265sage: ap.HsuExample10().is_congruence()2266False2267"""2268if self.index() == 1: # the group is SL2Z (trivial case)2269return True22702271L = self.L() # action of L2272R = self.R() # action of R22732274N = L.order() # generalised level of the group22752276# write N as N = em where e = 2^k and m odd2277e = 12278m = N2279while m%2 == 0:2280m //= 22281e *= 222822283if e==1: # N is odd2284onehalf = int(~Zmod(N)(2)) #i.e. 2^(-1) mod N2285rel = (R*R*L**(-onehalf))**32286return rel.is_one()22872288elif m==1: # N is a power of 22289onefifth=int(~Zmod(N)(5)) #i.e. 5^(-1) mod N2290S=L**20*R**onefifth*L**(-4)*~R22912292# congruence if the three below permutations are trivial2293rel=(~L*R*~L) * S * (L*~R*L) * S2294if not rel.is_one():2295return False22962297rel=~S*R*S*R**(-25)2298if not rel.is_one():2299return False23002301rel=(S*R**5*L*~R*L)**32302if not rel.is_one():2303return False23042305return True23062307else: # e>1, m>12308onehalf=int(~Zmod(m)(2)) #i.e. 2^(-1) mod m2309onefifth=int(~Zmod(e)(5)) #i.e. 5^(-1) mod e2310c=int(~Zmod(m)(e))*e #i.e. e^(-1)e mod m2311d=int(~Zmod(e)(m))*m #i.e. m^(-1)m mod e2312a=L**c2313b=R**c2314l=L**d2315r=R**d2316s=l**20*r**onefifth*l**(-4)*~r23172318#Congruence if the seven permutations below are trivial:2319rel=~a*~r*a*r2320if not rel.is_one():2321return False23222323rel=(a*~b*a)**42324if not rel.is_one():2325return False23262327rel=(a*~b*a)**2*(~a*b)**32328if not rel.is_one():2329return False23302331rel=(a*~b*a)**2*(b*b*a**(-onehalf))**(-3)2332if not rel.is_one():2333return False23342335rel=(~l*r*~l)*s*(l*~r*l)*s2336if not rel.is_one():2337return False23382339rel=~s*r*s*r**(-25)2340if not rel.is_one():2341return False23422343rel=(l*~r*l)**2*(s*r**5*l*~r*l)**(-3)2344if not rel.is_one():2345return False23462347return True23482349def one_odd_subgroup(self,random=False):2350r"""2351Return an odd subgroup of index 2 in `\Gamma`, where `\Gamma` is this2352subgroup. If the optional argument ``random`` is False (the default),2353this returns an arbitrary but consistent choice from the set of index 22354odd subgroups. If ``random`` is True, then it will choose one of these2355at random.23562357For details of the algorithm used, see the docstring for the related2358function :meth:`odd_subgroups`, which returns a list of all the2359index 2 odd subgroups.23602361EXAMPLES:23622363Starting from `\Gamma(4)` we get back `\Gamma(4)`::23642365sage: G = Gamma(4).as_permutation_group()2366sage: print G.is_odd(), G.index()2367True 482368sage: Ge = G.to_even_subgroup()2369sage: Go = Ge.one_odd_subgroup()2370sage: print Go.is_odd(), Go.index()2371True 482372sage: Go == G2373True23742375Strating from `\Gamma(6)` we get a different group::23762377sage: G = Gamma(6).as_permutation_group()2378sage: print G.is_odd(), G.index()2379True 1442380sage: Ge = G.to_even_subgroup()2381sage: Go = Ge.one_odd_subgroup()2382sage: print Go.is_odd(), Go.index()2383True 1442384sage: Go == G2385False23862387An error will be raised if there are no such subgroups, which occurs if2388and only if the group contains an element of order 4::23892390sage: Gamma0(10).as_permutation_group().one_odd_subgroup()2391Traceback (most recent call last):2392...2393ValueError: Group contains an element of order 4, hence no index 2 odd subgroups23942395Testing randomness::23962397sage: G = Gamma(6).as_permutation_group().to_even_subgroup()2398sage: G1 = G.one_odd_subgroup(random=True) # random2399sage: G1.is_subgroup(G)2400True2401"""2402if self.nu2() != 0:2403raise ValueError, "Group contains an element of order 4, hence no index 2 odd subgroups"2404n = self.index()2405s2old, s3old = self.S2(), self.S3()2406s2cycs = s2old.cycle_tuples() # no singletons can exist2407s3cycs = s3old.cycle_tuples(singletons=True)2408s2 = PermutationGroupElement([x + tuple(y + n for y in x) for x in s2cycs])2409s3 = PermutationGroupElement([x + tuple(y + n for y in x) for x in s3cycs])24102411if random is False:2412return ArithmeticSubgroup_Permutation(S2=s2,S3=s3,check=False)24132414from sage.misc.prandom import randint24152416t = []2417for i in xrange(1,n+1):2418if randint(0,1):2419t.append((i,n+i))2420t = PermutationGroupElement(t)2421return ArithmeticSubgroup_Permutation(S2=s2,S3=t*s3*t,check=False)24222423def odd_subgroups(self):2424r"""2425Return a list of the odd subgroups of index 2 in `\Gamma`, where2426`\Gamma` is this subgroup. (Equivalently, return the liftings of2427`\bar{\Gamma} \le {\rm PSL}(2, \ZZ)` to `{\rm SL}(2, \ZZ)`.)24282429.. seealso:: :meth:`one_odd_subgroup`, which returns just one of the2430odd subgroups (which is much quicker than enumerating them all).24312432ALGORITHM:24332434- If `\Gamma` has an element of order 4, then there are no index 2 odd2435subgroups, so return the empty set.24362437- If `\Gamma` has no elements of order 4, then the permutation `S_2` is2438a combination of 2-cycles with no fixed points on `\{1, \dots, N\}`.2439We construct the permutation `\tilde{S}_2` of `\{1, \dots, 2N\}`2440which has a 4-cycle `(a, b, a+N, b+N)` for each 2-cycle `(a,b)` in2441``S2``. Similarly, we construct a permutation `\tilde{S}_3` which has2442a 6-cycle `(a,b,c,a+N,b+N,c+N)` for each 3-cycle `(a,b,c)` in `S_3`,2443and a 2-cycle `(a,a+N)` for each fixed point `a` of `S_3`.24442445Then the permutations `\tilde{S}_2` and `\tilde{S}_3` satisfy2446`\tilde{S}_2^2 = \tilde{S}_3^3 = \iota` where `\iota` is the order 22447permutation interchanging `a` and `a+N` for each `a`. So the subgroup2448corresponding to these permutations is an index 2 odd subgroup of2449`\Gamma`.24502451- The other index 2 odd subgroups of `\Gamma` are obtained from the2452pairs `\tilde{S}_2, \tilde{S}_3^\sigma` where `\sigma` is an element2453of the group generated by the 2-cycles `(a, a+N)`.24542455Studying the permutations in the first example below gives a good2456illustration of the algorithm.24572458EXAMPLES::24592460sage: G = sage.modular.arithgroup.arithgroup_perm.HsuExample10()2461sage: [G.S2(), G.S3()]2462[(1,2)(3,4)(5,6)(7,8)(9,10), (1,8,3)(2,4,6)(5,7,10)]2463sage: X = G.odd_subgroups()2464sage: for u in X: print [u.S2(), u.S3()]2465[(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)]2466[(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)]2467[(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)]2468[(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)]24692470A projective congruence subgroup may have noncongruence liftings, as the example of `\bar{\Gamma}_0(6)` illustrates (see [KSV11]_)::24712472sage: X = Gamma0(6).as_permutation_group().odd_subgroups(); Sequence([[u.S2(), u.S3()] for u in X],cr=True)2473[2474[(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)],2475[(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)],2476[(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)],2477[(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)],2478[(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)],2479[(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)],2480[(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)],2481[(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)]2482]2483sage: [u.is_congruence() for u in X]2484[True, False, False, True, True, False, False, True]24852486Subgroups of large index may take a very long time::24872488sage: len(GammaH(11,[-1]).as_permutation_group().odd_subgroups()) # long time (up to 51s on sage.math, 2012)248920482490"""2491if self.nu2() != 0:2492return []2493n = self.index()2494s2old, s3old = self.S2(), self.S3()2495s2cycs = s2old.cycle_tuples() # no singletons can exist2496s3cycs = s3old.cycle_tuples(singletons=True)2497s2 = PermutationGroupElement([x + tuple(y + n for y in x) for x in s2cycs])2498s3 = PermutationGroupElement([x + tuple(y + n for y in x) for x in s3cycs])2499H = ArithmeticSubgroup_Permutation(S2=s2,S3=s3)25002501bucket = set([H])2502res = [H]2503# We use a set *and* a list since checking whether an element is in a2504# set is very fast, but on the other hand we want the order the results2505# are returned to be at least somewhat canonical.2506ts = [PermutationGroupElement(range(1,1+2*n))]25072508for i in xrange(1,n+1):25092510t = PermutationGroupElement([(i, n+i)],check=False)25112512s3c = t*s3*t25132514if s3c == s3:2515# t commutes with s3; nothing to see here.2516continue25172518HH = ArithmeticSubgroup_Permutation(S2=s2,S3=s3c,check=False)25192520if HH not in bucket:2521# Because the liftings are indexed by Hom(self, +-1) which is a2522# vector space over F2, either HH is already familiar, or all2523# the subgroups one gets by acting by t are new.25242525bucket.add(HH)2526res.append(HH)2527ts.append(t)2528for tt in ts[1:-1]:2529ts.append(tt*t)2530res.append(ArithmeticSubgroup_Permutation(S2=s2,S3=tt*s3c*tt,check=False))2531bucket.add(res[-1])25322533return res25342535def HsuExample10():2536r"""2537An example of an index 10 arithmetic subgroup studied by Tim Hsu.25382539EXAMPLE::25402541sage: import sage.modular.arithgroup.arithgroup_perm as ap2542sage: ap.HsuExample10()2543Arithmetic subgroup with permutations of right cosets2544S2=(1,2)(3,4)(5,6)(7,8)(9,10)2545S3=(1,8,3)(2,4,6)(5,7,10)2546L=(1,4)(2,5,9,10,8)(3,7,6)2547R=(1,7,9,10,6)(2,3)(4,5,8)2548"""2549return ArithmeticSubgroup_Permutation(2550L="(1,4)(2,5,9,10,8)(3,7,6)",2551R="(1,7,9,10,6)(2,3)(4,5,8)",2552relabel=False)25532554def HsuExample18():2555r"""2556An example of an index 18 arithmetic subgroup studied by Tim Hsu.25572558EXAMPLE::25592560sage: import sage.modular.arithgroup.arithgroup_perm as ap2561sage: ap.HsuExample18()2562Arithmetic subgroup with permutations of right cosets2563S2=(1,5)(2,11)(3,10)(4,15)(6,18)(7,12)(8,14)(9,16)(13,17)2564S3=(1,7,11)(2,18,5)(3,9,15)(4,14,10)(6,17,12)(8,13,16)2565L=(1,2)(3,4)(5,6,7)(8,9,10)(11,12,13,14,15,16,17,18)2566R=(1,12,18)(2,6,13,9,4,8,17,7)(3,16,14)(5,11)(10,15)2567"""2568return ArithmeticSubgroup_Permutation(2569L="(1,2)(3,4)(5,6,7)(8,9,10)(11,12,13,14,15,16,17,18)",2570R="(1,12,18)(2,6,13,9,4,8,17,7)(3,16,14)(5,11)(10,15)",2571relabel=False)257225732574