Path: blob/master/src/sage/modular/arithgroup/congroup_gammaH.py
8820 views
r"""1Congruence Subgroup `\Gamma_H(N)`23AUTHORS:45- Jordi Quer6- David Loeffler7"""89################################################################################10#11# Copyright (C) 2009, The Sage Group -- http://www.sagemath.org/12#13# Distributed under the terms of the GNU General Public License (GPL)14#15# The full text of the GPL is available at:16#17# http://www.gnu.org/licenses/18#19################################################################################2021from sage.rings.arith import euler_phi, lcm, gcd, divisors, get_inverse_mod, get_gcd, factor22from sage.modular.modsym.p1list import lift_to_sl2z23from congroup_generic import CongruenceSubgroup24from sage.modular.cusps import Cusp25from sage.misc.cachefunc import cached_method2627# Just for now until we make an SL_2 group type.28from sage.rings.integer_ring import ZZ29from sage.rings.finite_rings.integer_mod_ring import Zmod30from sage.matrix.matrix_space import MatrixSpace31from sage.groups.matrix_gps.finitely_generated import MatrixGroup32from sage.matrix.constructor import matrix3334Mat2Z = MatrixSpace(ZZ,2)353637_gammaH_cache = {}38def GammaH_constructor(level, H):39r"""40Return the congruence subgroup `\Gamma_H(N)`, which is the subgroup of41`SL_2(\ZZ)` consisting of matrices of the form `\begin{pmatrix} a & b \\42c & d \end{pmatrix}` with `N | c` and `a, b \in H`, for `H` a specified43subgroup of `(\ZZ/N\ZZ)^\times`.4445INPUT:4647- level -- an integer48- H -- either 0, 1, or a list49* If H is a list, return `\Gamma_H(N)`, where `H`50is the subgroup of `(\ZZ/N\ZZ)^*` **generated** by the51elements of the list.52* If H = 0, returns `\Gamma_0(N)`.53* If H = 1, returns `\Gamma_1(N)`.5455EXAMPLES::5657sage: GammaH(11,0) # indirect doctest58Congruence Subgroup Gamma0(11)59sage: GammaH(11,1)60Congruence Subgroup Gamma1(11)61sage: GammaH(11,[10])62Congruence Subgroup Gamma_H(11) with H generated by [10]63sage: GammaH(11,[10,1])64Congruence Subgroup Gamma_H(11) with H generated by [10]65sage: GammaH(14,[10])66Traceback (most recent call last):67...68ArithmeticError: The generators [10] must be units modulo 1469"""70from all import Gamma0, Gamma1, SL2Z71if level == 1:72return SL2Z73elif H == 0:74return Gamma0(level)75elif H == 1:76return Gamma1(level)7778H = _normalize_H(H, level)79if H == []:80return Gamma1(level)8182Hlist = _list_subgroup(level, H)83if len(Hlist) == euler_phi(level):84return Gamma0(level)8586key = (level, tuple(H))87try:88return _gammaH_cache[key]89except KeyError:90_gammaH_cache[key] = GammaH_class(level, H, Hlist)91return _gammaH_cache[key]9293def is_GammaH(x):94"""95Return True if x is a congruence subgroup of type GammaH.9697EXAMPLES::9899sage: from sage.modular.arithgroup.all import is_GammaH100sage: is_GammaH(GammaH(13, [2]))101True102sage: is_GammaH(Gamma0(6))103True104sage: is_GammaH(Gamma1(6))105True106sage: is_GammaH(sage.modular.arithgroup.congroup_generic.CongruenceSubgroup(5))107False108"""109return isinstance(x, GammaH_class)110111def _normalize_H(H, level):112"""113Normalize representatives for a given subgroup H of the units114modulo level.115116NOTE: This function does *not* make any attempt to find a minimal117set of generators for H. It simply normalizes the inputs for use118in hashing.119120EXAMPLES::121122sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([23], 10)123[3]124sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([1,5], 7)125[5]126sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([4,18], 14)127Traceback (most recent call last):128...129ArithmeticError: The generators [4, 18] must be units modulo 14130sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([3,17], 14)131[3]132sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([-1,7,9], 10)133[7, 9]134"""135H = [ZZ(h) for h in H]136for h in H:137if gcd(h, level) > 1:138raise ArithmeticError, 'The generators %s must be units modulo %s'%(H, level)139H = list(set([h%level for h in H]))140H.sort()141if 1 in H:142H.remove(1)143return H144145class GammaH_class(CongruenceSubgroup):146147r"""148The congruence subgroup `\Gamma_H(N)` for some subgroup `H \trianglelefteq149(\ZZ / N\ZZ)^\times`, which is the subgroup of `{\rm150SL}_2(\ZZ)` consisting of matrices of the form `\begin{pmatrix} a &151b \\ c & d \end{pmatrix}` with `N \mid c` and `a, b \in H`.152153TESTS:154155We test calculation of various invariants of the group: ::156157sage: GammaH(33,[2]).projective_index()15896159sage: GammaH(33,[2]).genus()1605161sage: GammaH(7,[2]).genus()1620163sage: GammaH(23, [1..22]).genus()1642165sage: Gamma0(23).genus()1662167sage: GammaH(23, [1]).genus()16812169sage: Gamma1(23).genus()17012171172We calculate the dimensions of some modular forms spaces: ::173174sage: GammaH(33,[2]).dimension_cusp_forms(2)1755176sage: GammaH(33,[2]).dimension_cusp_forms(3)1770178sage: GammaH(33,[2,5]).dimension_cusp_forms(2)1793180sage: GammaH(32079, [21676]).dimension_cusp_forms(20)181180266112182183We can sometimes show that there are no weight 1 cusp forms: ::184185sage: GammaH(20, [9]).dimension_cusp_forms(1)1860187"""188189def __init__(self, level, H, Hlist=None):190r"""191The congruence subgroup `\Gamma_H(N)`. The subgroup H192must be input as a list.193194EXAMPLES::195196sage: GammaH(117, [4])197Congruence Subgroup Gamma_H(117) with H generated by [4]198sage: G = GammaH(16, [7])199sage: TestSuite(G).run()200sage: G is loads(dumps(G))201True202"""203CongruenceSubgroup.__init__(self, level)204self.__H = H205if Hlist is None: Hlist = _list_subgroup(level, H)206self.__Hlist = Hlist207208def restrict(self, M):209r"""210Return the subgroup of `\Gamma_0(M)`, for `M` a divisor of `N`,211obtained by taking the image of this group under reduction modulo `N`.212213EXAMPLES::214215sage: G = GammaH(33,[2])216sage: G.restrict(11)217Congruence Subgroup Gamma0(11)218sage: G.restrict(1)219Modular Group SL(2,Z)220sage: G.restrict(15)221Traceback (most recent call last):222...223ValueError: M (=15) must be a divisor of the level (33) of self224"""225M = ZZ(M)226if self.level() % M:227raise ValueError, "M (=%s) must be a divisor of the level (%s) of self" % (M, self.level())228return self._new_group_from_level(M)229230def extend(self, M):231r"""232Return the subgroup of `\Gamma_0(M)`, for `M` a multiple of `N`,233obtained by taking the preimage of this group under the reduction map;234in other words, the intersection of this group with `\Gamma_0(M)`.235236EXAMPLES::237238sage: G = GammaH(33, [2])239sage: G.extend(99)240Congruence Subgroup Gamma_H(99) with H generated by [2, 35, 68]241sage: G.extend(11)242Traceback (most recent call last):243...244ValueError: M (=11) must be a multiple of the level (33) of self245246"""247M = ZZ(M)248if M % self.level():249raise ValueError, "M (=%s) must be a multiple of the level (%s) of self" % (M, self.level())250return self._new_group_from_level(M)251252def __reduce__(self):253"""254Used for pickling self.255256EXAMPLES::257258sage: GammaH(92,[45,47]).__reduce__()259(<function GammaH_constructor at ...>, (92, [45, 47]))260"""261return GammaH_constructor, (self.level(), self.__H)262263def divisor_subgroups(self):264r"""265Given this congruence subgroup `\Gamma_H(N)`, return all266subgroups `\Gamma_G(M)` for `M` a divisor of `N` and such that267`G` is equal to the image of `H` modulo `M`.268269EXAMPLES::270271sage: G = GammaH(33,[2]); G272Congruence Subgroup Gamma_H(33) with H generated by [2]273sage: G._list_of_elements_in_H()274[1, 2, 4, 8, 16, 17, 25, 29, 31, 32]275sage: G.divisor_subgroups()276[Modular Group SL(2,Z),277Congruence Subgroup Gamma0(3),278Congruence Subgroup Gamma0(11),279Congruence Subgroup Gamma_H(33) with H generated by [2]]280"""281v = self.__H282ans = []283for M in self.level().divisors():284w = [a % M for a in v if a%M]285ans.append(GammaH_constructor(M, w))286return ans287288def to_even_subgroup(self):289r"""290Return the smallest even subgroup of `SL(2, \ZZ)` containing self.291292EXAMPLE::293294sage: GammaH(11, [4]).to_even_subgroup()295Congruence Subgroup Gamma0(11)296sage: Gamma1(11).to_even_subgroup()297Congruence Subgroup Gamma_H(11) with H generated by [10]298299"""300if self.is_even(): return self301else:302return GammaH_constructor(self.level(), self._generators_for_H() + [-1])303304def __cmp__(self, other):305"""306Compare self to other.307308The ordering on congruence subgroups of the form GammaH(N) for some H309is first by level, then by the order of H, then lexicographically by H.310In particular, this means that we have Gamma1(N) < GammaH(N) <311Gamma0(N) for every nontrivial proper subgroup H.312313EXAMPLES::314315sage: G = GammaH(86, [9])316sage: G.__cmp__(G)3170318sage: G.__cmp__(GammaH(86, [11])) is not 0319True320sage: Gamma1(11) < Gamma0(11)321True322sage: Gamma1(11) == GammaH(11, [])323True324sage: Gamma0(11) == GammaH(11, [2])325True326sage: G = Gamma0(86)327sage: G.__cmp__(G)3280329sage: G.__cmp__(GammaH(86, [11])) is not 0330True331sage: Gamma1(17) < Gamma0(17)332True333sage: Gamma0(1) == SL2Z334True335sage: Gamma0(2) == Gamma1(2)336True337338sage: [x._list_of_elements_in_H() for x in sorted(Gamma0(24).gamma_h_subgroups())]339[[1],340[1, 5],341[1, 7],342[1, 11],343[1, 13],344[1, 17],345[1, 19],346[1, 23],347[1, 5, 7, 11],348[1, 5, 13, 17],349[1, 5, 19, 23],350[1, 7, 13, 19],351[1, 7, 17, 23],352[1, 11, 13, 23],353[1, 11, 17, 19],354[1, 5, 7, 11, 13, 17, 19, 23]]355"""356if is_GammaH(other):357return (cmp(self.level(), other.level())358or -cmp(self.index(), other.index())359or cmp(self._list_of_elements_in_H(), other._list_of_elements_in_H()))360else:361return CongruenceSubgroup.__cmp__(self, other)362363def _generators_for_H(self):364"""365Return generators for the subgroup H of the units mod366self.level() that defines self.367368EXAMPLES::369370sage: GammaH(17,[4])._generators_for_H()371[4]372sage: GammaH(12,[-1])._generators_for_H()373[11]374"""375return self.__H376377def _repr_(self):378"""379Return the string representation of self.380381EXAMPLES::382383sage: GammaH(123, [55])._repr_()384'Congruence Subgroup Gamma_H(123) with H generated by [55]'385"""386return "Congruence Subgroup Gamma_H(%s) with H generated by %s"%(self.level(), self.__H)387388def _latex_(self):389r"""390Return the \LaTeX representation of self.391392EXAMPLES::393394sage: GammaH(5,[4])._latex_()395'\\Gamma_H(5, [4])'396"""397return '\\Gamma_H(%s, %s)' % (self.level(), self.__H)398399def _list_of_elements_in_H(self):400"""401Returns a sorted list of Python ints that are representatives402between 1 and N-1 of the elements of H.403404WARNING: Do not change this returned list.405406EXAMPLES::407408sage: G = GammaH(11,[3]); G409Congruence Subgroup Gamma_H(11) with H generated by [3]410sage: G._list_of_elements_in_H()411[1, 3, 4, 5, 9]412"""413return self.__Hlist414415def is_even(self):416"""417Return True precisely if this subgroup contains the matrix -1.418419EXAMPLES::420421sage: GammaH(10, [3]).is_even()422True423sage: GammaH(14, [1]).is_even()424False425"""426if self.level() == 1:427return True428v = self._list_of_elements_in_H()429return int(self.level() - 1) in v430431@cached_method432def generators(self, algorithm="farey"):433r"""434Return generators for this congruence subgroup. The result is cached.435436INPUT:437438- ``algorithm`` (string): either ``farey`` (default) or439``todd-coxeter``.440441If ``algorithm`` is set to ``"farey"``, then the generators will be442calculated using Farey symbols, which will always return a *minimal*443generating set. See :mod:`~sage.modular.arithgroup.farey_symbol` for444more information.445446If ``algorithm`` is set to ``"todd-coxeter"``, a simpler algorithm447based on Todd-Coxeter enumeration will be used. This tends to return448far larger sets of generators.449450EXAMPLE::451452sage: GammaH(7, [2]).generators()453[454[1 1] [ 2 -1] [ 4 -3]455[0 1], [ 7 -3], [ 7 -5]456]457sage: GammaH(7, [2]).generators(algorithm="todd-coxeter")458[459[1 1] [-90 29] [ 15 4] [-10 -3] [ 1 -1] [1 0] [1 1] [-3 -1]460[0 1], [301 -97], [-49 -13], [ 7 2], [ 0 1], [7 1], [0 1], [ 7 2],461<BLANKLINE>462[-13 4] [-5 -1] [-5 -2] [-10 3] [ 1 0] [ 9 -1] [-20 7]463[ 42 -13], [21 4], [28 11], [ 63 -19], [-7 1], [28 -3], [-63 22],464<BLANKLINE>465[1 0] [-3 -1] [ 15 -4] [ 2 -1] [ 22 -7] [-5 1] [ 8 -3]466[7 1], [ 7 2], [ 49 -13], [ 7 -3], [ 63 -20], [14 -3], [-21 8],467<BLANKLINE>468[11 5] [-13 -4]469[35 16], [-42 -13]470]471"""472if algorithm=="farey":473return self.farey_symbol().generators()474elif algorithm=="todd-coxeter":475from sage.modular.modsym.ghlist import GHlist476from congroup_pyx import generators_helper477level = self.level()478gen_list = generators_helper(GHlist(self), level, Mat2Z)479return [self(g, check=False) for g in gen_list]480else:481raise ValueError, "Unknown algorithm '%s' (should be either 'farey' or 'todd-coxeter')" % algorithm482483def _coset_reduction_data_first_coord(G):484"""485Compute data used for determining the canonical coset486representative of an element of SL_2(Z) modulo G. This487function specifically returns data needed for the first part488of the reduction step (the first coordinate).489490INPUT:491G -- a congruence subgroup Gamma_0(N), Gamma_1(N), or Gamma_H(N).492493OUTPUT:494A list v such that495v[u] = (min(u*h: h in H),496gcd(u,N) ,497an h such that h*u = min(u*h: h in H)).498499EXAMPLES::500501sage: G = Gamma0(12)502sage: sage.modular.arithgroup.congroup_gammaH.GammaH_class._coset_reduction_data_first_coord(G)503[(0, 12, 0), (1, 1, 1), (2, 2, 1), (3, 3, 1), (4, 4, 1), (1, 1, 5), (6, 6, 1),504(1, 1, 7), (4, 4, 5), (3, 3, 7), (2, 2, 5), (1, 1, 11)]505"""506H = [ int(x) for x in G._list_of_elements_in_H() ]507N = int(G.level())508509# Get some useful fast functions for inverse and gcd510inverse_mod = get_inverse_mod(N) # optimal inverse function511gcd = get_gcd(N) # optimal gcd function512513# We will be filling this list in below.514reduct_data = [0] * N515516# We can fill in 0 and all elements of H immediately517reduct_data[0] = (0,N,0)518for u in H:519reduct_data[u] = (1, 1, inverse_mod(u, N))520521# Make a table of the reduction of H (mod N/d), one for each522# divisor d.523repr_H_mod_N_over_d = {}524for d in divisors(N):525# We special-case N == d because in this case,526# 1 % N_over_d is 0527if N == d:528repr_H_mod_N_over_d[d] = [1]529break530N_over_d = N//d531# For each element of H, we look at its image mod532# N_over_d. If we haven't yet seen it, add it on to533# the end of z.534w = [0] * N_over_d535z = [1]536for x in H:537val = x%N_over_d538if not w[val]:539w[val] = 1540z.append(x)541repr_H_mod_N_over_d[d] = z542543# Compute the rest of the tuples. The values left to process544# are those where reduct_data has a 0. Note that several of545# these values are processed on each loop below, so re-index546# each time.547while True:548try:549u = reduct_data.index(0)550except ValueError:551break552d = gcd(u, N)553for x in repr_H_mod_N_over_d[d]:554reduct_data[(u*x)%N] = (u, d, inverse_mod(x,N))555556return reduct_data557558def _coset_reduction_data_second_coord(G):559"""560Compute data used for determining the canonical coset561representative of an element of SL_2(Z) modulo G. This562function specifically returns data needed for the second part563of the reduction step (the second coordinate).564565INPUT:566self567568OUTPUT:569a dictionary v with keys the divisors of N such that v[d]570is the subgroup {h in H : h = 1 (mod N/d)}.571572EXAMPLES::573574sage: G = GammaH(240,[7,239])575sage: G._coset_reduction_data_second_coord()576{1: [1], 2: [1], 3: [1], 4: [1], 5: [1, 49], 6: [1], 48: [1, 191], 8: [1], 80: [1, 7, 49, 103], 10: [1, 49], 12: [1], 15: [1, 49], 240: [1, 7, 49, 103, 137, 191, 233, 239], 40: [1, 7, 49, 103], 20: [1, 49], 24: [1, 191], 120: [1, 7, 49, 103, 137, 191, 233, 239], 60: [1, 49, 137, 233], 30: [1, 49, 137, 233], 16: [1]}577sage: G = GammaH(1200,[-1,7]); G578Congruence Subgroup Gamma_H(1200) with H generated by [7, 1199]579sage: K = G._coset_reduction_data_second_coord().keys() ; K.sort()580sage: K == divisors(1200)581True582"""583H = G._list_of_elements_in_H()584N = G.level()585v = { 1: [1] , N: H }586for d in [x for x in divisors(N) if x > 1 and x < N ]:587N_over_d = N // d588v[d] = [x for x in H if x % N_over_d == 1]589return v590591@cached_method592def _coset_reduction_data(self):593"""594Compute data used for determining the canonical coset595representative of an element of SL_2(Z) modulo G.596597EXAMPLES::598599sage: G = GammaH(13, [-1]); G600Congruence Subgroup Gamma_H(13) with H generated by [12]601sage: G._coset_reduction_data()602([(0, 13, 0), (1, 1, 1), (2, 1, 1), (3, 1, 1), (4, 1, 1), (5, 1, 1), (6, 1, 1), (6, 1, 12), (5, 1, 12), (4, 1, 12), (3, 1, 12), (2, 1, 12), (1, 1, 12)], {1: [1], 13: [1, 12]})603"""604return (self._coset_reduction_data_first_coord(),605self._coset_reduction_data_second_coord())606607608def _reduce_coset(self, uu, vv):609r"""610Compute a canonical form for a given Manin symbol.611612INPUT:613Two integers (uu,vv) that define an element of `(Z/NZ)^2`.614uu -- an integer615vv -- an integer616617OUTPUT:618pair of integers that are equivalent to (uu,vv).619620NOTE: We do *not* require that gcd(uu,vv,N) = 1. If the gcd is621not 1, we return (0,0).622623EXAMPLES:624625An example at level 9.::626627sage: G = GammaH(9,[7]); G628Congruence Subgroup Gamma_H(9) with H generated by [7]629sage: a = []630sage: for i in range(G.level()):631... for j in range(G.level()):632... a.append(G._reduce_coset(i,j))633sage: v = list(set(a))634sage: v.sort()635sage: v636[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (1, 7), (1, 8), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (2, 7), (2, 8), (3, 1), (3, 2), (6, 1), (6, 2)]637638An example at level 100::639640sage: G = GammaH(100,[3,7]); G641Congruence Subgroup Gamma_H(100) with H generated by [3, 7]642sage: a = []643sage: for i in range(G.level()):644... for j in range(G.level()):645... a.append(G._reduce_coset(i,j))646sage: v = list(set(a))647sage: v.sort()648sage: len(v)649361650651This demonstrates the problem underlying trac \#1220::652653sage: G = GammaH(99, [67])654sage: G._reduce_coset(11,-3)655(11, 96)656sage: G._reduce_coset(77, -3)657(11, 96)658"""659N = int(self.level())660u = uu % N661v = vv % N662first, second = self._coset_reduction_data()663664if gcd(first[u][1], first[v][1]) != 1:665return (0,0)666if not u:667return (0, first[v][0])668if not v:669return (first[u][0], 0)670671new_u = first[u][0]672d = first[u][1]673new_v = (first[u][2] * v) % N674H_ls = second[d]675if len(H_ls) > 1:676new_v = min([ (new_v * h)%N for h in H_ls ])677678return (new_u, new_v)679680def reduce_cusp(self, c):681r"""682Compute a minimal representative for the given cusp c. Returns683a cusp c' which is equivalent to the given cusp, and is in684lowest terms with minimal positive denominator, and minimal685positive numerator for that denominator.686687Two cusps `u_1/v_1` and `u_2/v_2` are equivalent modulo `\Gamma_H(N)`688if and only if689690.. math::691692v_1 = h v_2 \bmod N\quad \text{and}\quad u_1 = h^{-1} u_2 \bmod {\rm gcd}(v_1,N)693694or695696.. math::697698v_1 = -h v_2 \bmod N\quad \text{and}\quad u_1 = -h^{-1} u_2 \bmod {\rm gcd}(v_1,N)699700for some `h \in H`.701702EXAMPLES::703704sage: GammaH(6,[5]).reduce_cusp(5/3)7051/3706sage: GammaH(12,[5]).reduce_cusp(Cusp(8,9))7071/3708sage: GammaH(12,[5]).reduce_cusp(5/12)709Infinity710sage: GammaH(12,[]).reduce_cusp(Cusp(5,12))7115/12712sage: GammaH(21,[5]).reduce_cusp(Cusp(-9/14))7131/7714sage: Gamma1(5).reduce_cusp(oo)715Infinity716sage: Gamma1(5).reduce_cusp(0)7170718"""719return self._reduce_cusp(c)[0]720721def _reduce_cusp(self, c):722r"""723Compute a minimal representative for the given cusp c.724Returns a pair (c', t), where c' is the minimal representative725for the given cusp, and t is either 1 or -1, as explained726below. Largely for internal use.727728The minimal representative for a cusp is the element in `P^1(Q)`729in lowest terms with minimal positive denominator, and minimal730positive numerator for that denominator.731732Two cusps `u1/v1` and `u2/v2` are equivalent modulo `\Gamma_H(N)`733if and only if734`v1 = h*v2 (mod N)` and `u1 = h^(-1)*u2 (mod gcd(v1,N))`735or736`v1 = -h*v2 (mod N)` and `u1 = -h^(-1)*u2 (mod gcd(v1,N))`737for some `h \in H`. Then t is 1 or -1 as c and c' fall into738the first or second case, respectively.739740EXAMPLES::741742sage: GammaH(6,[5])._reduce_cusp(Cusp(5,3))743(1/3, -1)744sage: GammaH(12,[5])._reduce_cusp(Cusp(8,9))745(1/3, -1)746sage: GammaH(12,[5])._reduce_cusp(Cusp(5,12))747(Infinity, 1)748sage: GammaH(12,[])._reduce_cusp(Cusp(5,12))749(5/12, 1)750sage: GammaH(21,[5])._reduce_cusp(Cusp(-9/14))751(1/7, 1)752"""753c = Cusp(c)754N = int(self.level())755Cusps = c.parent()756v = int(c.denominator() % N)757H = self._list_of_elements_in_H()758759# First, if N | v, take care of this case. If u is in \pm H,760# then we return Infinity. If not, let u_0 be the minimum761# of \{ h*u | h \in \pm H \}. Then return u_0/N.762if not v:763u = c.numerator() % N764if u in H:765return Cusps((1,0)), 1766if (N-u) in H:767return Cusps((1,0)), -1768ls = [ (u*h)%N for h in H ]769m1 = min(ls)770m2 = N-max(ls)771if m1 < m2:772return Cusps((m1,N)), 1773else:774return Cusps((m2,N)), -1775776u = int(c.numerator() % v)777gcd = get_gcd(N)778d = gcd(v,N)779780# If (N,v) == 1, let v_0 be the minimal element781# in \{ v * h | h \in \pm H \}. Then we either return782# Infinity or 1/v_0, as v is or is not in \pm H,783# respectively.784if d == 1:785if v in H:786return Cusps((0,1)), 1787if (N-v) in H:788return Cusps((0,1)), -1789ls = [ (v*h)%N for h in H ]790m1 = min(ls)791m2 = N-max(ls)792if m1 < m2:793return Cusps((1,m1)), 1794else:795return Cusps((1,m2)), -1796797val_min = v798inv_mod = get_inverse_mod(N)799800# Now we're in the case (N,v) > 1. So we have to do several801# steps: first, compute v_0 as above. While computing this802# minimum, keep track of *all* pairs of (h,s) which give this803# value of v_0.804hs_ls = [(1,1)]805for h in H:806tmp = (v*h)%N807808if tmp < val_min:809val_min = tmp810hs_ls = [(inv_mod(h,N), 1)]811elif tmp == val_min:812hs_ls.append((inv_mod(h,N), 1))813814if (N-tmp) < val_min:815val_min = N - tmp816hs_ls = [(inv_mod(h,N), -1)]817elif (N-tmp) == val_min:818hs_ls.append((inv_mod(h,N), -1))819820# Finally, we find our minimal numerator. Let u_1 be the821# minimum of s*h^-1*u mod d as (h,s) ranges over the elements822# of hs_ls. We must find the smallest integer u_0 which is823# smaller than v_0, congruent to u_1 mod d, and coprime to824# v_0. Then u_0/v_0 is our minimal representative.825u_min = val_min826sign = None827for h_inv,s in hs_ls:828tmp = (h_inv * s * u)%d829while gcd(tmp, val_min) > 1 and tmp < u_min:830tmp += d831if tmp < u_min:832u_min = tmp833sign = s834835return Cusps((u_min, val_min)), sign836837def _find_cusps(self):838r"""839Return an ordered list of inequivalent cusps for self, i.e. a840set of representatives for the orbits of self on841`\mathbf{P}^1(\QQ)`. These are returned in a reduced842form; see self.reduce_cusp for the definition of reduced.843844ALGORITHM:845Lemma 3.2 in Cremona's 1997 book shows that for the action846of Gamma1(N) on "signed projective space"847`\Q^2 / (\Q_{\geq 0}^+)`, we have `u_1/v_1 \sim u_2 / v_2`848if and only if `v_1 = v_2 \bmod N` and `u_1 = u_2 \bmod849gcd(v_1, N)`. It follows that every orbit has a850representative `u/v` with `v \le N` and `0 \le u \le851gcd(v, N)`. We iterate through all pairs `(u,v)`852satisfying this.853854Having found a set containing at least one of every855equivalence class modulo Gamma1(N), we can be sure of856picking up every class modulo GammaH(N) since this857contains Gamma1(N); and the reduce_cusp call does the858checking to make sure we don't get any duplicates.859860EXAMPLES::861862sage: Gamma1(5)._find_cusps()863[0, 2/5, 1/2, Infinity]864sage: Gamma1(35)._find_cusps()865[0, 2/35, 1/17, 1/16, 1/15, 1/14, 1/13, 1/12, 3/35, 1/11, 1/10, 1/9, 4/35, 1/8, 2/15, 1/7, 1/6, 6/35, 1/5, 3/14, 8/35, 1/4, 9/35, 4/15, 2/7, 3/10, 11/35, 1/3, 12/35, 5/14, 13/35, 2/5, 3/7, 16/35, 17/35, 1/2, 8/15, 4/7, 3/5, 9/14, 7/10, 5/7, 11/14, 4/5, 6/7, 9/10, 13/14, Infinity]866sage: Gamma1(24)._find_cusps() == Gamma1(24).cusps(algorithm='modsym')867True868sage: GammaH(24, [13,17])._find_cusps() == GammaH(24,[13,17]).cusps(algorithm='modsym')869True870"""871872s = []873hashes = []874N = self.level()875876for d in xrange(1, 1+N):877w = N.gcd(d)878M = int(w) if w > 1 else 2879for a in xrange(1,M):880if gcd(a, w) != 1:881continue882while gcd(a, d) != 1:883a += w884c = self.reduce_cusp(Cusp(a,d))885h = hash(c)886if not h in hashes:887hashes.append(h)888s.append(c)889return sorted(s)890891def _contains_sl2(self, a,b,c,d):892r"""893Test whether [a,b,c,d] is an element of this subgroup.894895EXAMPLES::896897sage: G = GammaH(10, [3])898sage: [1, 0, -10, 1] in G899True900sage: matrix(ZZ, 2, [7, 1, 20, 3]) in G901True902sage: SL2Z.0 in G903False904sage: GammaH(10, [9])([7, 1, 20, 3]) # indirect doctest905Traceback (most recent call last):906...907TypeError: matrix [ 7 1]908[20 3] is not an element of Congruence Subgroup Gamma_H(10) with H generated by [9]909"""910N = self.level()911return ( (c%N == 0) and (d%N in self._list_of_elements_in_H()))912913def gamma0_coset_reps(self):914r"""915Return a set of coset representatives for self \\ Gamma0(N), where N is916the level of self.917918EXAMPLE::919920sage: GammaH(108, [1,-1]).gamma0_coset_reps()921[922[1 0] [-43 -45] [ 31 33] [-49 -54] [ 25 28] [-19 -22]923[0 1], [108 113], [108 115], [108 119], [108 121], [108 125],924<BLANKLINE>925[-17 -20] [ 47 57] [ 13 16] [ 41 52] [ 7 9] [-37 -49]926[108 127], [108 131], [108 133], [108 137], [108 139], [108 143],927<BLANKLINE>928[-35 -47] [ 29 40] [ -5 -7] [ 23 33] [-11 -16] [ 53 79]929[108 145], [108 149], [108 151], [108 155], [108 157], [108 161]930]931"""932from all import SL2Z933N = self.level()934return [SL2Z(lift_to_sl2z(0, d.lift(), N)) for d in _GammaH_coset_helper(N, self._list_of_elements_in_H())]935936def coset_reps(self):937r"""938Return a set of coset representatives for self \\ SL2Z.939940EXAMPLES::941942sage: list(Gamma1(3).coset_reps())943[944[1 0] [-1 -2] [ 0 -1] [-2 1] [1 0] [-3 -2] [ 0 -1] [-2 -3]945[0 1], [ 3 5], [ 1 0], [ 5 -3], [1 1], [ 8 5], [ 1 2], [ 5 7]946]947sage: len(list(Gamma1(31).coset_reps())) == 31**2 - 1948True949"""950from all import Gamma0, SL2Z951reps1 = Gamma0(self.level()).coset_reps()952for r in reps1:953reps2 = self.gamma0_coset_reps()954for t in reps2:955yield SL2Z(t)*r956957958def is_subgroup(self, other):959r"""960Return True if self is a subgroup of right, and False961otherwise.962963EXAMPLES::964965sage: GammaH(24,[7]).is_subgroup(SL2Z)966True967sage: GammaH(24,[7]).is_subgroup(Gamma0(8))968True969sage: GammaH(24, []).is_subgroup(GammaH(24, [7]))970True971sage: GammaH(24, []).is_subgroup(Gamma1(24))972True973sage: GammaH(24, [17]).is_subgroup(GammaH(24, [7]))974False975sage: GammaH(1371, [169]).is_subgroup(GammaH(457, [169]))976True977"""978979from all import is_Gamma0, is_Gamma1980if not isinstance(other, GammaH_class):981raise NotImplementedError982983# level of self should divide level of other984if self.level() % other.level():985return False986987# easy cases988if is_Gamma0(other):989return True # recall self is a GammaH, so it's contained in Gamma0990991if is_Gamma1(other) and len(self._generators_for_H()) > 0:992return False993994else:995# difficult case996t = other._list_of_elements_in_H()997for x in self._generators_for_H():998if not (x in t):999return False1000return True100110021003def index(self):1004r"""1005Return the index of self in SL2Z.10061007EXAMPLE::10081009sage: [G.index() for G in Gamma0(40).gamma_h_subgroups()]1010[72, 144, 144, 144, 144, 288, 288, 288, 288, 144, 288, 288, 576, 576, 144, 288, 288, 576, 576, 144, 288, 288, 576, 576, 288, 576, 1152]1011"""1012from all import Gamma11013return Gamma1(self.level()).index() / len(self._list_of_elements_in_H())10141015def nu2(self):1016r"""1017Return the number of orbits of elliptic points of order 2 for this1018group.10191020EXAMPLE::10211022sage: [H.nu2() for n in [1..10] for H in Gamma0(n).gamma_h_subgroups()]1023[1, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0]1024sage: GammaH(33,[2]).nu2()102501026sage: GammaH(5,[2]).nu2()1027210281029AUTHORS:10301031- Jordi Quer10321033"""1034N = self.level()1035H = self._list_of_elements_in_H()1036if N % 4 == 0: return ZZ(0)1037for p, r in N.factor():1038if p % 4 == 3: return ZZ(0)1039return (euler_phi(N) // len(H))*len([x for x in H if (x**2 + 1) % N == 0])10401041def nu3(self):1042r"""1043Return the number of orbits of elliptic points of order 3 for this1044group.10451046EXAMPLE::10471048sage: [H.nu3() for n in [1..10] for H in Gamma0(n).gamma_h_subgroups()]1049[1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]1050sage: GammaH(33,[2]).nu3()105101052sage: GammaH(7,[2]).nu3()1053210541055AUTHORS:10561057- Jordi Quer10581059"""1060N = self.level()1061H = self._list_of_elements_in_H()1062if N % 9 == 0: return ZZ(0)1063for p, r in N.factor():1064if p % 3 == 2: return ZZ(0)1065lenHpm = len(H)1066if N - ZZ(1) not in H: lenHpm*=21067return (euler_phi(N)//lenHpm)*len([x for x in H if (x**2+x+1) % N == 0])10681069def ncusps(self):1070r"""1071Return the number of orbits of cusps (regular or otherwise) for this subgroup.10721073EXAMPLE::10741075sage: GammaH(33,[2]).ncusps()107681077sage: GammaH(32079, [21676]).ncusps()10782880010791080AUTHORS:10811082- Jordi Quer10831084"""1085N = self.level()1086H = self._list_of_elements_in_H()1087c = ZZ(0)1088for d in [d for d in N.divisors() if d**2 <= N]:1089Nd = lcm(d,N//d)1090Hd = set([x % Nd for x in H])1091lenHd = len(Hd)1092if Nd-1 not in Hd: lenHd *= 21093summand = euler_phi(d)*euler_phi(N//d)//lenHd1094if d**2 == N:1095c = c + summand1096else:1097c = c + 2*summand1098return c10991100def nregcusps(self):1101r"""1102Return the number of orbits of regular cusps for this subgroup. A cusp is regular1103if we may find a parabolic element generating the stabiliser of that1104cusp whose eigenvalues are both +1 rather than -1. If G contains -1,1105all cusps are regular.11061107EXAMPLES::11081109sage: GammaH(20, [17]).nregcusps()111041111sage: GammaH(20, [17]).nirregcusps()111221113sage: GammaH(3212, [2045, 2773]).nregcusps()111414401115sage: GammaH(3212, [2045, 2773]).nirregcusps()111672011171118AUTHOR:11191120- Jordi Quer1121"""1122if self.is_even():1123return self.ncusps()11241125N = self.level()1126H = self._list_of_elements_in_H()11271128c = ZZ(0)1129for d in [d for d in divisors(N) if d**2 <= N]:1130Nd = lcm(d,N//d)1131Hd = set([x%Nd for x in H])1132if Nd - 1 not in Hd:1133summand = euler_phi(d)*euler_phi(N//d)//(2*len(Hd))1134if d**2==N:1135c = c + summand1136else:1137c = c + 2*summand1138return c11391140def nirregcusps(self):1141r"""1142Return the number of irregular cusps for this subgroup.11431144EXAMPLES::11451146sage: GammaH(3212, [2045, 2773]).nirregcusps()11477201148"""11491150return self.ncusps() - self.nregcusps()11511152def dimension_new_cusp_forms(self, k=2, p=0):1153r"""1154Return the dimension of the space of new (or `p`-new)1155weight `k` cusp forms for this congruence subgroup.11561157INPUT:11581159- ``k`` - an integer (default: 2), the weight. Not fully implemented for k = 1.1160- ``p`` - integer (default: 0); if nonzero, compute the `p`-new subspace.11611162OUTPUT: Integer11631164EXAMPLES::11651166sage: GammaH(33,[2]).dimension_new_cusp_forms()116731168sage: Gamma1(4*25).dimension_new_cusp_forms(2, p=5)11692251170sage: Gamma1(33).dimension_new_cusp_forms(2)1171191172sage: Gamma1(33).dimension_new_cusp_forms(2,p=11)11732111741175"""1176N = self.level()1177if p==0 or N % p != 0:1178return sum([H.dimension_cusp_forms(k) * mumu(N // H.level()) \1179for H in self.divisor_subgroups()])1180else:1181return self.dimension_cusp_forms(k) - \11822*self.restrict(N//p).dimension_new_cusp_forms(k)11831184def image_mod_n(self):1185r"""1186Return the image of this group in `SL(2, \ZZ / N\ZZ)`.11871188EXAMPLE::11891190sage: Gamma0(3).image_mod_n()1191Matrix group over Ring of integers modulo 3 with 2 generators (1192[2 0] [1 1]1193[0 2], [0 1]1194)11951196TEST::11971198sage: for n in [2..20]:1199... for g in Gamma0(n).gamma_h_subgroups():1200... G = g.image_mod_n()1201... assert G.order() == Gamma(n).index() / g.index()1202"""1203N = self.level()1204if N == 1:1205raise NotImplementedError, "Matrix groups over ring of integers modulo 1 not implemented"1206gens = [matrix(Zmod(N), 2, 2, [x, 0, 0, Zmod(N)(1)/x]) for x in self._generators_for_H()]1207gens += [matrix(Zmod(N),2,[1,1,0,1])]1208return MatrixGroup(gens)12091210def _list_subgroup(N, gens):1211r"""1212Given an integer ``N`` and a list of integers ``gens``, return a list of1213the elements of the subgroup of `(\ZZ / N\ZZ)^\times` generated by the1214elements of ``gens``.12151216EXAMPLE::12171218sage: sage.modular.arithgroup.congroup_gammaH._list_subgroup(11, [3])1219[1, 3, 4, 5, 9]1220"""1221H = set([1])1222N = int(N)1223for g in gens:1224if gcd(g, N) != 1:1225raise ValueError, "gen (=%s) is not in (Z/%sZ)^*"%(g,N)1226gk = int(g) % N1227sbgrp = [gk]1228while not (gk in H):1229gk = (gk * g)%N1230sbgrp.append(gk)1231H = set([(x*h)%N for x in sbgrp for h in H])1232H = list(H)1233H.sort()1234return H12351236def _GammaH_coset_helper(N, H):1237r"""1238Return a list of coset representatives for H in (Z / NZ)^*.12391240EXAMPLE::12411242sage: from sage.modular.arithgroup.congroup_gammaH import _GammaH_coset_helper1243sage: _GammaH_coset_helper(108, [1, 107])1244[1, 5, 7, 11, 13, 17, 19, 23, 25, 29, 31, 35, 37, 41, 43, 47, 49, 53]1245"""1246t = [Zmod(N)(1)]1247W = [Zmod(N)(h) for h in H]1248HH = [Zmod(N)(h) for h in H]1249k = euler_phi(N)12501251for i in xrange(1, N):1252if gcd(i, N) != 1: continue1253if not i in W:1254t.append(t[0]*i)1255W = W + [i*h for h in HH]1256if len(W) == k: break1257return t12581259def mumu(N):1260"""1261Return 0 if any cube divides `N`. Otherwise return1262`(-2)^v` where `v` is the number of primes that1263exactly divide `N`.12641265This is similar to the Moebius function.12661267INPUT:126812691270- ``N`` - an integer at least 1127112721273OUTPUT: Integer12741275EXAMPLES::12761277sage: from sage.modular.arithgroup.congroup_gammaH import mumu1278sage: mumu(27)127901280sage: mumu(6*25)128141282sage: mumu(7*9*25)1283-21284sage: mumu(9*25)128511286"""1287if N < 1:1288raise ValueError, "N must be at least 1"1289p = 11290for _,r in factor(N):1291if r > 2:1292return ZZ(0)1293elif r == 1:1294p *= -21295return ZZ(p)129612971298