Path: blob/master/sage/modular/arithgroup/congroup_gammaH.py
4084 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 CongruenceSubgroup, is_CongruenceSubgroup24from arithgroup_element import ArithmeticSubgroupElement25from sage.modular.cusps import Cusp26from sage.misc.cachefunc import cached_method2728# Just for now until we make an SL_2 group type.29from sage.rings.integer_ring import ZZ30from sage.rings.finite_rings.integer_mod_ring import Zmod31from sage.matrix.matrix_space import MatrixSpace32from sage.groups.matrix_gps.matrix_group import MatrixGroup33from sage.matrix.constructor import matrix3435Mat2Z = MatrixSpace(ZZ,2)363738_gammaH_cache = {}39def GammaH_constructor(level, H):40r"""41Return the congruence subgroup `\Gamma_H(N)`, which is the subgroup of42`SL_2(\ZZ)` consisting of matrices of the form `\begin{pmatrix} a & b \\43c & d \end{pmatrix}` with `N | c` and `a, b \in H`, for `H` a specified44subgroup of `(\ZZ/N\ZZ)^\times`.4546INPUT:4748- level -- an integer49- H -- either 0, 1, or a list50* If H is a list, return `\Gamma_H(N)`, where `H`51is the subgroup of `(\ZZ/N\ZZ)^*` **generated** by the52elements of the list.53* If H = 0, returns `\Gamma_0(N)`.54* If H = 1, returns `\Gamma_1(N)`.5556EXAMPLES::5758sage: GammaH(11,0) # indirect doctest59Congruence Subgroup Gamma0(11)60sage: GammaH(11,1)61Congruence Subgroup Gamma1(11)62sage: GammaH(11,[10])63Congruence Subgroup Gamma_H(11) with H generated by [10]64sage: GammaH(11,[10,1])65Congruence Subgroup Gamma_H(11) with H generated by [10]66sage: GammaH(14,[10])67Traceback (most recent call last):68...69ArithmeticError: The generators [10] must be units modulo 1470"""71from all import Gamma0, Gamma1, SL2Z72if level == 1:73return SL2Z74elif H == 0:75return Gamma0(level)76elif H == 1:77return Gamma1(level)7879H = _normalize_H(H, level)80if H == []:81return Gamma1(level)8283Hlist = _list_subgroup(level, H)84if len(Hlist) == euler_phi(level):85return Gamma0(level)8687key = (level, tuple(H))88try:89return _gammaH_cache[key]90except KeyError:91_gammaH_cache[key] = GammaH_class(level, H, Hlist)92return _gammaH_cache[key]9394def is_GammaH(x):95"""96Return True if x is a congruence subgroup of type GammaH.9798EXAMPLES::99100sage: from sage.modular.arithgroup.all import is_GammaH101sage: is_GammaH(GammaH(13, [2]))102True103sage: is_GammaH(Gamma0(6))104True105sage: is_GammaH(Gamma1(6))106True107sage: is_GammaH(sage.modular.arithgroup.congroup_generic.CongruenceSubgroup(5))108False109"""110return isinstance(x, GammaH_class)111112def _normalize_H(H, level):113"""114Normalize representatives for a given subgroup H of the units115modulo level.116117NOTE: This function does *not* make any attempt to find a minimal118set of generators for H. It simply normalizes the inputs for use119in hashing.120121EXAMPLES::122123sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([23], 10)124[3]125sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([1,5], 7)126[5]127sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([4,18], 14)128Traceback (most recent call last):129...130ArithmeticError: The generators [4, 18] must be units modulo 14131sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([3,17], 14)132[3]133sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([-1,7,9], 10)134[7, 9]135"""136if not isinstance(H, list):137raise TypeError, "H must be a list."138H = [ZZ(h) for h in H]139for h in H:140if gcd(h, level) > 1:141raise ArithmeticError, 'The generators %s must be units modulo %s'%(H, level)142H = list(set([h%level for h in H]))143H.sort()144if 1 in H:145H.remove(1)146return H147148class GammaH_class(CongruenceSubgroup):149150r"""151The congruence subgroup `\Gamma_H(N)` for some subgroup `H \trianglelefteq152(\ZZ / N\ZZ)^\times`, which is the subgroup of `{\rm153SL}_2(\ZZ)` consisting of matrices of the form `\begin{pmatrix} a &154b \\ c & d \end{pmatrix}` with `N \mid c` and `a, b \in H`.155156TESTS:157158We test calculation of various invariants of the group: ::159160sage: GammaH(33,[2]).projective_index()16196162sage: GammaH(33,[2]).genus()1635164sage: GammaH(7,[2]).genus()1650166sage: GammaH(23, [1..22]).genus()1672168sage: Gamma0(23).genus()1692170sage: GammaH(23, [1]).genus()17112172sage: Gamma1(23).genus()17312174175We calculate the dimensions of some modular forms spaces: ::176177sage: GammaH(33,[2]).dimension_cusp_forms(2)1785179sage: GammaH(33,[2]).dimension_cusp_forms(3)1800181sage: GammaH(33,[2,5]).dimension_cusp_forms(2)1823183sage: GammaH(32079, [21676]).dimension_cusp_forms(20)184180266112185186We can sometimes show that there are no weight 1 cusp forms: ::187188sage: GammaH(20, [9]).dimension_cusp_forms(1)1890190191"""192193def __init__(self, level, H, Hlist=None):194r"""195The congruence subgroup `\Gamma_H(N)`. The subgroup H196must be input as a list.197198EXAMPLES::199200sage: GammaH(117, [4])201Congruence Subgroup Gamma_H(117) with H generated by [4]202sage: G = GammaH(16, [7])203sage: G == loads(dumps(G))204True205sage: G is loads(dumps(G))206True207"""208CongruenceSubgroup.__init__(self, level)209self.__H = H210if Hlist is None: Hlist = _list_subgroup(level, H)211self.__Hlist = Hlist212213def restrict(self, M):214r"""215Return the subgroup of `\Gamma_0(M)`, for `M` a divisor of `N`,216obtained by taking the image of this group under reduction modulo `N`.217218EXAMPLES::219220sage: G = GammaH(33,[2])221sage: G.restrict(11)222Congruence Subgroup Gamma0(11)223sage: G.restrict(1)224Modular Group SL(2,Z)225sage: G.restrict(15)226Traceback (most recent call last):227...228ValueError: M (=15) must be a divisor of the level (33) of self229"""230M = ZZ(M)231if self.level() % M:232raise ValueError, "M (=%s) must be a divisor of the level (%s) of self" % (M, self.level())233return self._new_group_from_level(M)234235def extend(self, M):236r"""237Return the subgroup of `\Gamma_0(M)`, for `M` a multiple of `N`,238obtained by taking the preimage of this group under the reduction map;239in other words, the intersection of this group with `\Gamma_0(M)`.240241EXAMPLES::242243sage: G = GammaH(33, [2])244sage: G.extend(99)245Congruence Subgroup Gamma_H(99) with H generated by [2, 35, 68]246sage: G.extend(11)247Traceback (most recent call last):248...249ValueError: M (=11) must be a multiple of the level (33) of self250251"""252M = ZZ(M)253if M % self.level():254raise ValueError, "M (=%s) must be a multiple of the level (%s) of self" % (M, self.level())255return self._new_group_from_level(M)256257def __reduce__(self):258"""259Used for pickling self.260261EXAMPLES::262263sage: GammaH(92,[45,47]).__reduce__()264(<function GammaH_constructor at ...>, (92, [45, 47]))265"""266return GammaH_constructor, (self.level(), self.__H)267268def divisor_subgroups(self):269r"""270Given this congruence subgroup `\Gamma_H(N)`, return all271subgroups `\Gamma_G(M)` for `M` a divisor of `N` and such that272`G` is equal to the image of `H` modulo `M`.273274EXAMPLES::275276sage: G = GammaH(33,[2]); G277Congruence Subgroup Gamma_H(33) with H generated by [2]278sage: G._list_of_elements_in_H()279[1, 2, 4, 8, 16, 17, 25, 29, 31, 32]280sage: G.divisor_subgroups()281[Modular Group SL(2,Z),282Congruence Subgroup Gamma0(3),283Congruence Subgroup Gamma0(11),284Congruence Subgroup Gamma_H(33) with H generated by [2]]285"""286v = self.__H287ans = []288for M in self.level().divisors():289w = [a % M for a in v if a%M]290ans.append(GammaH_constructor(M, w))291return ans292293def to_even_subgroup(self):294r"""295Return the smallest even subgroup of `SL(2, \ZZ)` containing self.296297EXAMPLE::298299sage: GammaH(11, [4]).to_even_subgroup()300Congruence Subgroup Gamma0(11)301sage: Gamma1(11).to_even_subgroup()302Congruence Subgroup Gamma_H(11) with H generated by [10]303304"""305if self.is_even(): return self306else:307return GammaH_constructor(self.level(), self._generators_for_H() + [-1])308309def __cmp__(self, other):310"""311Compare self to other.312313The ordering on congruence subgroups of the form GammaH(N) for314some H is first by level and then by the subgroup H. In315particular, this means that we have Gamma1(N) < GammaH(N) <316Gamma0(N) for every nontrivial subgroup H.317318EXAMPLES::319320sage: G = GammaH(86, [9])321sage: G.__cmp__(G)3220323sage: G.__cmp__(GammaH(86, [11])) is not 0324True325sage: Gamma1(11) < Gamma0(11)326True327sage: Gamma1(11) == GammaH(11, [])328True329sage: Gamma0(11) == GammaH(11, [2])330True331"""332if is_GammaH(other):333t = cmp(self.level(), other.level())334if t:335return t336else:337return cmp(self._list_of_elements_in_H(), other._list_of_elements_in_H())338else:339return CongruenceSubgroup.__cmp__(self, other)340341def _generators_for_H(self):342"""343Return generators for the subgroup H of the units mod344self.level() that defines self.345346EXAMPLES::347348sage: GammaH(17,[4])._generators_for_H()349[4]350sage: GammaH(12,[-1])._generators_for_H()351[11]352"""353return self.__H354355def _repr_(self):356"""357Return the string representation of self.358359EXAMPLES::360361sage: GammaH(123, [55])._repr_()362'Congruence Subgroup Gamma_H(123) with H generated by [55]'363"""364return "Congruence Subgroup Gamma_H(%s) with H generated by %s"%(self.level(), self.__H)365366def _latex_(self):367r"""368Return the \LaTeX representation of self.369370EXAMPLES::371372sage: GammaH(5,[4])._latex_()373'\\Gamma_H(5)'374"""375return "\\Gamma_H(%s)"%self.level()376377def _list_of_elements_in_H(self):378"""379Returns a sorted list of Python ints that are representatives380between 1 and N-1 of the elements of H.381382WARNING: Do not change this returned list.383384EXAMPLES::385386sage: G = GammaH(11,[3]); G387Congruence Subgroup Gamma_H(11) with H generated by [3]388sage: G._list_of_elements_in_H()389[1, 3, 4, 5, 9]390"""391return self.__Hlist392393def is_even(self):394"""395Return True precisely if this subgroup contains the matrix -1.396397EXAMPLES::398399sage: GammaH(10, [3]).is_even()400True401sage: GammaH(14, [1]).is_even()402False403"""404if self.level() == 1:405return True406v = self._list_of_elements_in_H()407return int(self.level() - 1) in v408409@cached_method410def generators(self, algorithm="farey"):411r"""412Return generators for this congruence subgroup. The result is cached.413414INPUT:415416- ``algorithm`` (string): either ``farey`` (default) or417``todd-coxeter``.418419If ``algorithm`` is set to ``"farey"``, then the generators will be420calculated using Farey symbols, which will always return a *minimal*421generating set. See :mod:`~sage.modular.arithgroup.farey_symbol` for422more information.423424If ``algorithm`` is set to ``"todd-coxeter"``, a simpler algorithm425based on Todd-Coxeter enumeration will be used. This tends to return426far larger sets of generators.427428EXAMPLE::429430sage: GammaH(7, [2]).generators()431[432[1 1] [ 2 -1] [ 4 -3]433[0 1], [ 7 -3], [ 7 -5]434]435sage: GammaH(7, [2]).generators(algorithm="todd-coxeter")436[437[1 1] [-90 29] [ 15 4] [-10 -3] [ 1 -1] [1 0] [1 1] [-3 -1]438[0 1], [301 -97], [-49 -13], [ 7 2], [ 0 1], [7 1], [0 1], [ 7 2],439<BLANKLINE>440[-13 4] [-5 -1] [-5 -2] [-10 3] [ 1 0] [ 9 -1] [-20 7]441[ 42 -13], [21 4], [28 11], [ 63 -19], [-7 1], [28 -3], [-63 22],442<BLANKLINE>443[1 0] [-3 -1] [ 15 -4] [ 2 -1] [ 22 -7] [-5 1] [ 8 -3]444[7 1], [ 7 2], [ 49 -13], [ 7 -3], [ 63 -20], [14 -3], [-21 8],445<BLANKLINE>446[11 5] [-13 -4]447[35 16], [-42 -13]448]449"""450if algorithm=="farey":451return self.farey_symbol().generators()452elif algorithm=="todd-coxeter":453from sage.modular.modsym.ghlist import GHlist454from congroup_pyx import generators_helper455level = self.level()456gen_list = generators_helper(GHlist(self), level, Mat2Z)457return [self(g, check=False) for g in gen_list]458else:459raise ValueError, "Unknown algorithm '%s' (should be either 'farey' or 'todd-coxeter')" % algorithm460461def _coset_reduction_data_first_coord(G):462"""463Compute data used for determining the canonical coset464representative of an element of SL_2(Z) modulo G. This465function specifically returns data needed for the first part466of the reduction step (the first coordinate).467468INPUT:469G -- a congruence subgroup Gamma_0(N), Gamma_1(N), or Gamma_H(N).470471OUTPUT:472A list v such that473v[u] = (min(u*h: h in H),474gcd(u,N) ,475an h such that h*u = min(u*h: h in H)).476477EXAMPLES::478479sage: G = Gamma0(12)480sage: sage.modular.arithgroup.congroup_gammaH.GammaH_class._coset_reduction_data_first_coord(G)481[(0, 12, 0), (1, 1, 1), (2, 2, 1), (3, 3, 1), (4, 4, 1), (1, 1, 5), (6, 6, 1),482(1, 1, 7), (4, 4, 5), (3, 3, 7), (2, 2, 5), (1, 1, 11)]483"""484H = [ int(x) for x in G._list_of_elements_in_H() ]485N = int(G.level())486487# Get some useful fast functions for inverse and gcd488inverse_mod = get_inverse_mod(N) # optimal inverse function489gcd = get_gcd(N) # optimal gcd function490491# We will be filling this list in below.492reduct_data = [0] * N493494# We can fill in 0 and all elements of H immediately495reduct_data[0] = (0,N,0)496for u in H:497reduct_data[u] = (1, 1, inverse_mod(u, N))498499# Make a table of the reduction of H (mod N/d), one for each500# divisor d.501repr_H_mod_N_over_d = {}502for d in divisors(N):503# We special-case N == d because in this case,504# 1 % N_over_d is 0505if N == d:506repr_H_mod_N_over_d[d] = [1]507break508N_over_d = N//d509# For each element of H, we look at its image mod510# N_over_d. If we haven't yet seen it, add it on to511# the end of z.512w = [0] * N_over_d513z = [1]514for x in H:515val = x%N_over_d516if not w[val]:517w[val] = 1518z.append(x)519repr_H_mod_N_over_d[d] = z520521# Compute the rest of the tuples. The values left to process522# are those where reduct_data has a 0. Note that several of523# these values are processed on each loop below, so re-index524# each time.525while True:526try:527u = reduct_data.index(0)528except ValueError:529break530d = gcd(u, N)531for x in repr_H_mod_N_over_d[d]:532reduct_data[(u*x)%N] = (u, d, inverse_mod(x,N))533534return reduct_data535536def _coset_reduction_data_second_coord(G):537"""538Compute data used for determining the canonical coset539representative of an element of SL_2(Z) modulo G. This540function specifically returns data needed for the second part541of the reduction step (the second coordinate).542543INPUT:544self545546OUTPUT:547a dictionary v with keys the divisors of N such that v[d]548is the subgroup {h in H : h = 1 (mod N/d)}.549550EXAMPLES::551552sage: G = GammaH(240,[7,239])553sage: G._coset_reduction_data_second_coord()554{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]}555sage: G = GammaH(1200,[-1,7]); G556Congruence Subgroup Gamma_H(1200) with H generated by [7, 1199]557sage: K = G._coset_reduction_data_second_coord().keys() ; K.sort()558sage: K == divisors(1200)559True560"""561H = G._list_of_elements_in_H()562N = G.level()563v = { 1: [1] , N: H }564for d in [x for x in divisors(N) if x > 1 and x < N ]:565N_over_d = N // d566v[d] = [x for x in H if x % N_over_d == 1]567return v568569@cached_method570def _coset_reduction_data(self):571"""572Compute data used for determining the canonical coset573representative of an element of SL_2(Z) modulo G.574575EXAMPLES::576577sage: G = GammaH(13, [-1]); G578Congruence Subgroup Gamma_H(13) with H generated by [12]579sage: G._coset_reduction_data()580([(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]})581"""582return (self._coset_reduction_data_first_coord(),583self._coset_reduction_data_second_coord())584585586def _reduce_coset(self, uu, vv):587r"""588Compute a canonical form for a given Manin symbol.589590INPUT:591Two integers (uu,vv) that define an element of `(Z/NZ)^2`.592uu -- an integer593vv -- an integer594595OUTPUT:596pair of integers that are equivalent to (uu,vv).597598NOTE: We do *not* require that gcd(uu,vv,N) = 1. If the gcd is599not 1, we return (0,0).600601EXAMPLES:602603An example at level 9.::604605sage: G = GammaH(9,[7]); G606Congruence Subgroup Gamma_H(9) with H generated by [7]607sage: a = []608sage: for i in range(G.level()):609... for j in range(G.level()):610... a.append(G._reduce_coset(i,j))611sage: v = list(set(a))612sage: v.sort()613sage: v614[(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)]615616An example at level 100::617618sage: G = GammaH(100,[3,7]); G619Congruence Subgroup Gamma_H(100) with H generated by [3, 7]620sage: a = []621sage: for i in range(G.level()):622... for j in range(G.level()):623... a.append(G._reduce_coset(i,j))624sage: v = list(set(a))625sage: v.sort()626sage: len(v)627361628629This demonstrates the problem underlying trac \#1220::630631sage: G = GammaH(99, [67])632sage: G._reduce_coset(11,-3)633(11, 96)634sage: G._reduce_coset(77, -3)635(11, 96)636"""637N = int(self.level())638u = uu % N639v = vv % N640first, second = self._coset_reduction_data()641642if gcd(first[u][1], first[v][1]) != 1:643return (0,0)644if not u:645return (0, first[v][0])646if not v:647return (first[u][0], 0)648649new_u = first[u][0]650d = first[u][1]651new_v = (first[u][2] * v) % N652H_ls = second[d]653if len(H_ls) > 1:654new_v = min([ (new_v * h)%N for h in H_ls ])655656return (new_u, new_v)657658def reduce_cusp(self, c):659r"""660Compute a minimal representative for the given cusp c. Returns661a cusp c' which is equivalent to the given cusp, and is in662lowest terms with minimal positive denominator, and minimal663positive numerator for that denominator.664665Two cusps `u_1/v_1` and `u_2/v_2` are equivalent modulo `\Gamma_H(N)`666if and only if667668.. math::669670v_1 = h v_2 \bmod N\quad \text{and}\quad u_1 = h^{-1} u_2 \bmod {\rm gcd}(v_1,N)671672or673674.. math::675676v_1 = -h v_2 \bmod N\quad \text{and}\quad u_1 = -h^{-1} u_2 \bmod {\rm gcd}(v_1,N)677678for some `h \in H`.679680EXAMPLES::681682sage: GammaH(6,[5]).reduce_cusp(5/3)6831/3684sage: GammaH(12,[5]).reduce_cusp(Cusp(8,9))6851/3686sage: GammaH(12,[5]).reduce_cusp(5/12)687Infinity688sage: GammaH(12,[]).reduce_cusp(Cusp(5,12))6895/12690sage: GammaH(21,[5]).reduce_cusp(Cusp(-9/14))6911/7692sage: Gamma1(5).reduce_cusp(oo)693Infinity694sage: Gamma1(5).reduce_cusp(0)6950696"""697return self._reduce_cusp(c)[0]698699def _reduce_cusp(self, c):700r"""701Compute a minimal representative for the given cusp c.702Returns a pair (c', t), where c' is the minimal representative703for the given cusp, and t is either 1 or -1, as explained704below. Largely for internal use.705706The minimal representative for a cusp is the element in `P^1(Q)`707in lowest terms with minimal positive denominator, and minimal708positive numerator for that denominator.709710Two cusps `u1/v1` and `u2/v2` are equivalent modulo `\Gamma_H(N)`711if and only if712`v1 = h*v2 (mod N)` and `u1 = h^(-1)*u2 (mod gcd(v1,N))`713or714`v1 = -h*v2 (mod N)` and `u1 = -h^(-1)*u2 (mod gcd(v1,N))`715for some `h \in H`. Then t is 1 or -1 as c and c' fall into716the first or second case, respectively.717718EXAMPLES::719720sage: GammaH(6,[5])._reduce_cusp(Cusp(5,3))721(1/3, -1)722sage: GammaH(12,[5])._reduce_cusp(Cusp(8,9))723(1/3, -1)724sage: GammaH(12,[5])._reduce_cusp(Cusp(5,12))725(Infinity, 1)726sage: GammaH(12,[])._reduce_cusp(Cusp(5,12))727(5/12, 1)728sage: GammaH(21,[5])._reduce_cusp(Cusp(-9/14))729(1/7, 1)730"""731c = Cusp(c)732N = int(self.level())733Cusps = c.parent()734v = int(c.denominator() % N)735H = self._list_of_elements_in_H()736737# First, if N | v, take care of this case. If u is in \pm H,738# then we return Infinity. If not, let u_0 be the minimum739# of \{ h*u | h \in \pm H \}. Then return u_0/N.740if not v:741u = c.numerator() % N742if u in H:743return Cusps((1,0)), 1744if (N-u) in H:745return Cusps((1,0)), -1746ls = [ (u*h)%N for h in H ]747m1 = min(ls)748m2 = N-max(ls)749if m1 < m2:750return Cusps((m1,N)), 1751else:752return Cusps((m2,N)), -1753754u = int(c.numerator() % v)755gcd = get_gcd(N)756d = gcd(v,N)757758# If (N,v) == 1, let v_0 be the minimal element759# in \{ v * h | h \in \pm H \}. Then we either return760# Infinity or 1/v_0, as v is or is not in \pm H,761# respectively.762if d == 1:763if v in H:764return Cusps((0,1)), 1765if (N-v) in H:766return Cusps((0,1)), -1767ls = [ (v*h)%N for h in H ]768m1 = min(ls)769m2 = N-max(ls)770if m1 < m2:771return Cusps((1,m1)), 1772else:773return Cusps((1,m2)), -1774775val_min = v776inv_mod = get_inverse_mod(N)777778# Now we're in the case (N,v) > 1. So we have to do several779# steps: first, compute v_0 as above. While computing this780# minimum, keep track of *all* pairs of (h,s) which give this781# value of v_0.782hs_ls = [(1,1)]783for h in H:784tmp = (v*h)%N785786if tmp < val_min:787val_min = tmp788hs_ls = [(inv_mod(h,N), 1)]789elif tmp == val_min:790hs_ls.append((inv_mod(h,N), 1))791792if (N-tmp) < val_min:793val_min = N - tmp794hs_ls = [(inv_mod(h,N), -1)]795elif (N-tmp) == val_min:796hs_ls.append((inv_mod(h,N), -1))797798# Finally, we find our minimal numerator. Let u_1 be the799# minimum of s*h^-1*u mod d as (h,s) ranges over the elements800# of hs_ls. We must find the smallest integer u_0 which is801# smaller than v_0, congruent to u_1 mod d, and coprime to802# v_0. Then u_0/v_0 is our minimal representative.803u_min = val_min804sign = None805for h_inv,s in hs_ls:806tmp = (h_inv * s * u)%d807while gcd(tmp, val_min) > 1 and tmp < u_min:808tmp += d809if tmp < u_min:810u_min = tmp811sign = s812813return Cusps((u_min, val_min)), sign814815def _find_cusps(self):816r"""817Return an ordered list of inequivalent cusps for self, i.e. a818set of representatives for the orbits of self on819`\mathbf{P}^1(\QQ)`. These are returned in a reduced820form; see self.reduce_cusp for the definition of reduced.821822ALGORITHM:823Lemma 3.2 in Cremona's 1997 book shows that for the action824of Gamma1(N) on "signed projective space"825`\Q^2 / (\Q_{\geq 0}^+)`, we have `u_1/v_1 \sim u_2 / v_2`826if and only if `v_1 = v_2 \bmod N` and `u_1 = u_2 \bmod827gcd(v_1, N)`. It follows that every orbit has a828representative `u/v` with `v \le N` and `0 \le u \le829gcd(v, N)`. We iterate through all pairs `(u,v)`830satisfying this.831832Having found a set containing at least one of every833equivalence class modulo Gamma1(N), we can be sure of834picking up every class modulo GammaH(N) since this835contains Gamma1(N); and the reduce_cusp call does the836checking to make sure we don't get any duplicates.837838EXAMPLES::839840sage: Gamma1(5)._find_cusps()841[0, 2/5, 1/2, Infinity]842sage: Gamma1(35)._find_cusps()843[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]844sage: Gamma1(24)._find_cusps() == Gamma1(24).cusps(algorithm='modsym')845True846sage: GammaH(24, [13,17])._find_cusps() == GammaH(24,[13,17]).cusps(algorithm='modsym')847True848"""849850s = []851hashes = []852N = self.level()853854for d in xrange(1, 1+N):855w = N.gcd(d)856M = int(w) if w > 1 else 2857for a in xrange(1,M):858if gcd(a, w) != 1:859continue860while gcd(a, d) != 1:861a += w862c = self.reduce_cusp(Cusp(a,d))863h = hash(c)864if not h in hashes:865hashes.append(h)866s.append(c)867return sorted(s)868869def __call__(self, x, check=True):870r"""871Create an element of this congruence subgroup from x.872873If the optional flag check is True (default), check whether874x actually gives an element of self.875876EXAMPLES::877878sage: G = GammaH(10, [3])879sage: G([1, 0, -10, 1])880[ 1 0]881[-10 1]882sage: G(matrix(ZZ, 2, [7, 1, 20, 3]))883[ 7 1]884[20 3]885sage: GammaH(10, [9])([7, 1, 20, 3])886Traceback (most recent call last):887...888TypeError: matrix must have lower right entry (=3) congruent modulo 10 to some element of H889"""890from all import SL2Z891x = SL2Z(x, check)892if not check:893return x894895c = x.c()896d = x.d()897N = self.level()898if c%N != 0:899raise TypeError, "matrix must have lower left entry (=%s) divisible by %s" %(c, N)900elif d%N in self._list_of_elements_in_H():901return x902else:903raise TypeError, "matrix must have lower right entry (=%s) congruent modulo %s to some element of H" %(d, N)904905def gamma0_coset_reps(self):906r"""907Return a set of coset representatives for self \\ Gamma0(N), where N is908the level of self.909910EXAMPLE::911912sage: GammaH(108, [1,-1]).gamma0_coset_reps()913[914[1 0] [-43 -45] [ 31 33] [-49 -54] [ 25 28] [-19 -22]915[0 1], [108 113], [108 115], [108 119], [108 121], [108 125],916<BLANKLINE>917[-17 -20] [ 47 57] [ 13 16] [ 41 52] [ 7 9] [-37 -49]918[108 127], [108 131], [108 133], [108 137], [108 139], [108 143],919<BLANKLINE>920[-35 -47] [ 29 40] [ -5 -7] [ 23 33] [-11 -16] [ 53 79]921[108 145], [108 149], [108 151], [108 155], [108 157], [108 161]922]923"""924from all import Gamma0925N = self.level()926G = Gamma0(N)927s = []928return [G(lift_to_sl2z(0, d.lift(), N)) for d in _GammaH_coset_helper(N, self._list_of_elements_in_H())]929930def coset_reps(self):931r"""932Return a set of coset representatives for self \\ SL2Z.933934EXAMPLES::935936sage: list(Gamma1(3).coset_reps())937[938[1 0] [-1 -2] [ 0 -1] [-2 1] [1 0] [-3 -2] [ 0 -1] [-2 -3]939[0 1], [ 3 5], [ 1 0], [ 5 -3], [1 1], [ 8 5], [ 1 2], [ 5 7]940]941sage: len(list(Gamma1(31).coset_reps())) == 31**2 - 1942True943"""944from all import Gamma0, SL2Z945reps1 = Gamma0(self.level()).coset_reps()946for r in reps1:947reps2 = self.gamma0_coset_reps()948for t in reps2:949yield SL2Z(t)*r950951952def is_subgroup(self, other):953r"""954Return True if self is a subgroup of right, and False955otherwise.956957EXAMPLES::958959sage: GammaH(24,[7]).is_subgroup(SL2Z)960True961sage: GammaH(24,[7]).is_subgroup(Gamma0(8))962True963sage: GammaH(24, []).is_subgroup(GammaH(24, [7]))964True965sage: GammaH(24, []).is_subgroup(Gamma1(24))966True967sage: GammaH(24, [17]).is_subgroup(GammaH(24, [7]))968False969sage: GammaH(1371, [169]).is_subgroup(GammaH(457, [169]))970True971"""972973from all import is_Gamma0, is_Gamma1974if not isinstance(other, GammaH_class):975raise NotImplementedError976977# level of self should divide level of other978if self.level() % other.level():979return False980981# easy cases982if is_Gamma0(other):983return True # recall self is a GammaH, so it's contained in Gamma0984985if is_Gamma1(other) and len(self._generators_for_H()) > 0:986return False987988else:989# difficult case990t = other._list_of_elements_in_H()991for x in self._generators_for_H():992if not (x in t):993return False994return True995996997def index(self):998r"""999Return the index of self in SL2Z.10001001EXAMPLE::10021003sage: [G.index() for G in Gamma0(40).gamma_h_subgroups()]1004[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]1005"""1006from all import Gamma11007return Gamma1(self.level()).index() / len(self._list_of_elements_in_H())10081009def nu2(self):1010r"""1011Return the number of orbits of elliptic points of order 2 for this1012group.10131014EXAMPLE::10151016sage: [H.nu2() for n in [1..10] for H in Gamma0(n).gamma_h_subgroups()]1017[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]1018sage: GammaH(33,[2]).nu2()101901020sage: GammaH(5,[2]).nu2()1021210221023AUTHORS:10241025- Jordi Quer10261027"""1028N = self.level()1029H = self._list_of_elements_in_H()1030if N % 4 == 0: return ZZ(0)1031for p, r in N.factor():1032if p % 4 == 3: return ZZ(0)1033return (euler_phi(N) // len(H))*len([x for x in H if (x**2 + 1) % N == 0])10341035def nu3(self):1036r"""1037Return the number of orbits of elliptic points of order 3 for this1038group.10391040EXAMPLE::10411042sage: [H.nu3() for n in [1..10] for H in Gamma0(n).gamma_h_subgroups()]1043[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]1044sage: GammaH(33,[2]).nu3()104501046sage: GammaH(7,[2]).nu3()1047210481049AUTHORS:10501051- Jordi Quer10521053"""1054N = self.level()1055H = self._list_of_elements_in_H()1056if N % 9 == 0: return ZZ(0)1057for p, r in N.factor():1058if p % 3 == 2: return ZZ(0)1059lenHpm = len(H)1060if N - ZZ(1) not in H: lenHpm*=21061return (euler_phi(N)//lenHpm)*len([x for x in H if (x**2+x+1) % N == 0])10621063def ncusps(self):1064r"""1065Return the number of orbits of cusps (regular or otherwise) for this subgroup.10661067EXAMPLE::10681069sage: GammaH(33,[2]).ncusps()107081071sage: GammaH(32079, [21676]).ncusps()10722880010731074AUTHORS:10751076- Jordi Quer10771078"""1079N = self.level()1080H = self._list_of_elements_in_H()1081c = ZZ(0)1082for d in [d for d in N.divisors() if d**2 <= N]:1083Nd = lcm(d,N//d)1084Hd = set([x % Nd for x in H])1085lenHd = len(Hd)1086if Nd-1 not in Hd: lenHd *= 21087summand = euler_phi(d)*euler_phi(N//d)//lenHd1088if d**2 == N:1089c = c + summand1090else:1091c = c + 2*summand1092return c10931094def nregcusps(self):1095r"""1096Return the number of orbits of regular cusps for this subgroup. A cusp is regular1097if we may find a parabolic element generating the stabiliser of that1098cusp whose eigenvalues are both +1 rather than -1. If G contains -1,1099all cusps are regular.11001101EXAMPLES::11021103sage: GammaH(20, [17]).nregcusps()110441105sage: GammaH(20, [17]).nirregcusps()110621107sage: GammaH(3212, [2045, 2773]).nregcusps()110814401109sage: GammaH(3212, [2045, 2773]).nirregcusps()111072011111112AUTHOR:11131114- Jordi Quer1115"""1116if self.is_even():1117return self.ncusps()11181119N = self.level()1120H = self._list_of_elements_in_H()11211122c = ZZ(0)1123for d in [d for d in divisors(N) if d**2 <= N]:1124Nd = lcm(d,N//d)1125Hd = set([x%Nd for x in H])1126if Nd - 1 not in Hd:1127summand = euler_phi(d)*euler_phi(N//d)//(2*len(Hd))1128if d**2==N:1129c = c + summand1130else:1131c = c + 2*summand1132return c11331134def nirregcusps(self):1135r"""1136Return the number of irregular cusps for this subgroup.11371138EXAMPLES::11391140sage: GammaH(3212, [2045, 2773]).nirregcusps()11417201142"""11431144return self.ncusps() - self.nregcusps()11451146def dimension_new_cusp_forms(self, k=2, p=0):1147r"""1148Return the dimension of the space of new (or `p`-new)1149weight `k` cusp forms for this congruence subgroup.11501151INPUT:11521153- ``k`` - an integer (default: 2), the weight. Not fully implemented for k = 1.1154- ``p`` - integer (default: 0); if nonzero, compute the `p`-new subspace.11551156OUTPUT: Integer11571158EXAMPLES::11591160sage: GammaH(33,[2]).dimension_new_cusp_forms()116131162sage: Gamma1(4*25).dimension_new_cusp_forms(2, p=5)11632251164sage: Gamma1(33).dimension_new_cusp_forms(2)1165191166sage: Gamma1(33).dimension_new_cusp_forms(2,p=11)11672111681169"""1170N = self.level()1171if p==0 or N % p != 0:1172return sum([H.dimension_cusp_forms(k) * mumu(N // H.level()) \1173for H in self.divisor_subgroups()])1174else:1175return self.dimension_cusp_forms(k) - \11762*self.restrict(N//p).dimension_new_cusp_forms(k)11771178def image_mod_n(self):1179r"""1180Return the image of this group in `SL(2, \ZZ / N\ZZ)`.11811182EXAMPLE::11831184sage: Gamma0(3).image_mod_n()1185Matrix group over Ring of integers modulo 3 with 2 generators:1186[[[2, 0], [0, 2]], [[1, 1], [0, 1]]]11871188TEST::11891190sage: for n in [2..20]:1191... for g in Gamma0(n).gamma_h_subgroups():1192... G = g.image_mod_n()1193... assert G.order() == Gamma(n).index() / g.index()1194"""1195N = self.level()1196if N == 1:1197raise NotImplementedError, "Matrix groups over ring of integers modulo 1 not implemented"1198gens = [matrix(Zmod(N), 2, 2, [x, 0, 0, Zmod(N)(1)/x]) for x in self._generators_for_H()]1199gens += [matrix(Zmod(N),2,[1,1,0,1])]1200return MatrixGroup(gens)12011202def _list_subgroup(N, gens):1203r"""1204Given an integer ``N`` and a list of integers ``gens``, return a list of1205the elements of the subgroup of `(\ZZ / N\ZZ)^\times` generated by the1206elements of ``gens``.12071208EXAMPLE::12091210sage: sage.modular.arithgroup.congroup_gammaH._list_subgroup(11, [3])1211[1, 3, 4, 5, 9]1212"""1213H = set([1])1214N = int(N)1215for g in gens:1216if gcd(g, N) != 1:1217raise ValueError, "gen (=%s) is not in (Z/%sZ)^*"%(g,N)1218gk = int(g) % N1219sbgrp = [gk]1220while not (gk in H):1221gk = (gk * g)%N1222sbgrp.append(gk)1223H = set([(x*h)%N for x in sbgrp for h in H])1224H = list(H)1225H.sort()1226return H12271228def _GammaH_coset_helper(N, H):1229r"""1230Return a list of coset representatives for H in (Z / NZ)^*.12311232EXAMPLE::12331234sage: from sage.modular.arithgroup.congroup_gammaH import _GammaH_coset_helper1235sage: _GammaH_coset_helper(108, [1, 107])1236[1, 5, 7, 11, 13, 17, 19, 23, 25, 29, 31, 35, 37, 41, 43, 47, 49, 53]1237"""1238t = [Zmod(N)(1)]1239W = [Zmod(N)(h) for h in H]1240HH = [Zmod(N)(h) for h in H]1241k = euler_phi(N)12421243for i in xrange(1, N):1244if gcd(i, N) != 1: continue1245if not i in W:1246t.append(t[0]*i)1247W = W + [i*h for h in HH]1248if len(W) == k: break1249return t12501251def mumu(N):1252"""1253Return 0 if any cube divides `N`. Otherwise return1254`(-2)^v` where `v` is the number of primes that1255exactly divide `N`.12561257This is similar to the Moebius function.12581259INPUT:126012611262- ``N`` - an integer at least 1126312641265OUTPUT: Integer12661267EXAMPLES::12681269sage: from sage.modular.arithgroup.congroup_gammaH import mumu1270sage: mumu(27)127101272sage: mumu(6*25)127341274sage: mumu(7*9*25)1275-21276sage: mumu(9*25)127711278"""1279if N < 1:1280raise ValueError, "N must be at least 1"1281p = 11282for _,r in factor(N):1283if r > 2:1284return ZZ(0)1285elif r == 1:1286p *= -21287return ZZ(p)128812891290