Path: blob/master/src/sage/modular/arithgroup/congroup_gamma1.py
8820 views
r"""1Congruence Subgroup `\Gamma_1(N)`2"""34################################################################################5#6# Copyright (C) 2009, The Sage Group -- http://www.sagemath.org/7#8# Distributed under the terms of the GNU General Public License (GPL)9#10# The full text of the GPL is available at:11#12# http://www.gnu.org/licenses/13#14################################################################################1516from sage.misc.cachefunc import cached_method1718from sage.misc.misc import prod19from congroup_gammaH import GammaH_class, is_GammaH, GammaH_constructor20from sage.rings.all import ZZ, euler_phi as phi, moebius, divisors21from sage.modular.dirichlet import DirichletGroup2223# Just for now until we make an SL_2 group type.24from sage.rings.finite_rings.integer_mod_ring import IntegerModRing25from sage.matrix.matrix_space import MatrixSpace26Mat2Z = MatrixSpace(IntegerModRing(0),2)2728def is_Gamma1(x):29"""30Return True if x is a congruence subgroup of type Gamma1.3132EXAMPLES::3334sage: from sage.modular.arithgroup.all import is_Gamma135sage: is_Gamma1(SL2Z)36False37sage: is_Gamma1(Gamma1(13))38True39sage: is_Gamma1(Gamma0(6))40False41sage: is_Gamma1(GammaH(12, [])) # trick question!42True43sage: is_Gamma1(GammaH(12, [5]))44False45"""46#from congroup_sl2z import is_SL2Z47#return (isinstance(x, Gamma1_class) or is_SL2Z(x))48return isinstance(x, Gamma1_class)4950_gamma1_cache = {}51def Gamma1_constructor(N):52r"""53Return the congruence subgroup `\Gamma_1(N)`.5455EXAMPLES::5657sage: Gamma1(5) # indirect doctest58Congruence Subgroup Gamma1(5)59sage: G = Gamma1(23)60sage: G is Gamma1(23)61True62sage: G is GammaH(23, [1])63True64sage: TestSuite(G).run()65sage: G is loads(dumps(G))66True67"""68if N == 1 or N == 2:69from congroup_gamma0 import Gamma0_constructor70return Gamma0_constructor(N)71try:72return _gamma1_cache[N]73except KeyError:74_gamma1_cache[N] = Gamma1_class(N)75return _gamma1_cache[N]7677class Gamma1_class(GammaH_class):78r"""79The congruence subgroup `\Gamma_1(N)`.8081TESTS::8283sage: [Gamma1(n).genus() for n in prime_range(2,100)]84[0, 0, 0, 0, 1, 2, 5, 7, 12, 22, 26, 40, 51, 57, 70, 92, 117, 126, 155, 176, 187, 222, 247, 287, 345]85sage: [Gamma1(n).index() for n in [1..10]]86[1, 3, 8, 12, 24, 24, 48, 48, 72, 72]8788sage: [Gamma1(n).dimension_cusp_forms() for n in [1..20]]89[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 1, 1, 2, 5, 2, 7, 3]90sage: [Gamma1(n).dimension_cusp_forms(1) for n in [1..20]]91[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]92sage: [Gamma1(4).dimension_cusp_forms(k) for k in [1..20]]93[0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8]94sage: Gamma1(23).dimension_cusp_forms(1)95Traceback (most recent call last):96...97NotImplementedError: Computation of dimensions of weight 1 cusp forms spaces not implemented in general98"""99100def __init__(self, level):101r"""102The congruence subgroup `\Gamma_1(N)`.103104EXAMPLES::105106sage: G = Gamma1(11); G107Congruence Subgroup Gamma1(11)108sage: loads(G.dumps()) == G109True110"""111GammaH_class.__init__(self, level, [])112113def _repr_(self):114"""115Return the string representation of self.116117EXAMPLES::118119sage: Gamma1(133)._repr_()120'Congruence Subgroup Gamma1(133)'121"""122return "Congruence Subgroup Gamma1(%s)"%self.level()123124def __reduce__(self):125"""126Used for pickling self.127128EXAMPLES::129130sage: Gamma1(82).__reduce__()131(<function Gamma1_constructor at ...>, (82,))132"""133return Gamma1_constructor, (self.level(),)134135def _latex_(self):136r"""137Return the \LaTeX representation of self.138139EXAMPLES::140141sage: Gamma1(3)._latex_()142'\\Gamma_1(3)'143sage: latex(Gamma1(3))144\Gamma_1(3)145"""146return "\\Gamma_1(%s)"%self.level()147148def is_even(self):149"""150Return True precisely if this subgroup contains the matrix -1.151152EXAMPLES::153154sage: Gamma1(1).is_even()155True156sage: Gamma1(2).is_even()157True158sage: Gamma1(15).is_even()159False160"""161return self.level() in [1,2]162163def is_subgroup(self, right):164"""165Return True if self is a subgroup of right.166167EXAMPLES::168169sage: Gamma1(3).is_subgroup(SL2Z)170True171sage: Gamma1(3).is_subgroup(Gamma1(5))172False173sage: Gamma1(3).is_subgroup(Gamma1(6))174False175sage: Gamma1(6).is_subgroup(Gamma1(3))176True177sage: Gamma1(6).is_subgroup(Gamma0(2))178True179sage: Gamma1(80).is_subgroup(GammaH(40, []))180True181sage: Gamma1(80).is_subgroup(GammaH(40, [21]))182True183"""184if right.level() == 1:185return True186if is_GammaH(right):187return self.level() % right.level() == 0188else:189raise NotImplementedError190191@cached_method192def generators(self, algorithm="farey"):193r"""194Return generators for this congruence subgroup. The result is cached.195196INPUT:197198- ``algorithm`` (string): either ``farey`` (default) or199``todd-coxeter``.200201If ``algorithm`` is set to ``"farey"``, then the generators will be202calculated using Farey symbols, which will always return a *minimal*203generating set. See :mod:`~sage.modular.arithgroup.farey_symbol` for204more information.205206If ``algorithm`` is set to ``"todd-coxeter"``, a simpler algorithm207based on Todd-Coxeter enumeration will be used. This tends to return208far larger sets of generators.209210EXAMPLE::211212sage: Gamma1(3).generators()213[214[1 1] [ 1 -1]215[0 1], [ 3 -2]216]217sage: Gamma1(3).generators(algorithm="todd-coxeter")218[219[1 1] [-20 9] [ 4 1] [-5 -2] [ 1 -1] [1 0] [1 1] [-5 2]220[0 1], [ 51 -23], [-9 -2], [ 3 1], [ 0 1], [3 1], [0 1], [12 -5],221<BLANKLINE>222[ 1 0] [ 4 -1] [ -5 3] [ 1 -1] [ 7 -3] [ 4 -1] [ -5 3]223[-3 1], [ 9 -2], [-12 7], [ 3 -2], [12 -5], [ 9 -2], [-12 7]224]225"""226if algorithm=="farey":227return self.farey_symbol().generators()228elif algorithm=="todd-coxeter":229from sage.modular.modsym.g1list import G1list230from congroup_pyx import generators_helper231level = self.level()232gen_list = generators_helper(G1list(level), level, Mat2Z)233return [self(g, check=False) for g in gen_list]234else:235raise ValueError, "Unknown algorithm '%s' (should be either 'farey' or 'todd-coxeter')" % algorithm236237def _contains_sl2(self, a,b,c,d):238r"""239Test whether x is an element of this group.240241EXAMPLES::242243sage: G = Gamma1(5)244sage: [1, 0, -10, 1] in G245True246sage: matrix(ZZ, 2, [6, 1, 5, 1]) in G247True248sage: SL2Z.0 in G249False250sage: G([1, 1, 6, 7]) # indirect doctest251Traceback (most recent call last):252...253TypeError: matrix [1 1]254[6 7] is not an element of Congruence Subgroup Gamma1(5)255"""256N = self.level()257# don't need to check d == 1 mod N as this is automatic from det258return ((a%N == 1) and (c%N == 0))259260def nu2(self):261r"""262Calculate the number of orbits of elliptic points of order 2 for this263subgroup `\Gamma_1(N)`. This is known to be 0 if N > 2.264265EXAMPLE::266267sage: Gamma1(2).nu2()2681269sage: Gamma1(457).nu2()2700271sage: [Gamma1(n).nu2() for n in [1..16]]272[1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]273"""274275N = self.level()276if N > 2: return 0277elif N == 2 or N == 1: return 1278279def nu3(self):280r"""281Calculate the number of orbits of elliptic points of order 3 for this282subgroup `\Gamma_1(N)`. This is known to be 0 if N > 3.283284EXAMPLE::285286sage: Gamma1(2).nu3()2870288sage: Gamma1(3).nu3()2891290sage: Gamma1(457).nu3()2910292sage: [Gamma1(n).nu3() for n in [1..10]]293[1, 0, 1, 0, 0, 0, 0, 0, 0, 0]294"""295296N = self.level()297if N > 3 or N == 2: return 0298else: return 1299300def ncusps(self):301r"""302Return the number of cusps of this subgroup `\Gamma_1(N)`.303304EXAMPLES::305306sage: [Gamma1(n).ncusps() for n in [1..15]]307[1, 2, 2, 3, 4, 4, 6, 6, 8, 8, 10, 10, 12, 12, 16]308sage: [Gamma1(n).ncusps() for n in prime_range(2, 100)]309[2, 2, 4, 6, 10, 12, 16, 18, 22, 28, 30, 36, 40, 42, 46, 52, 58, 60, 66, 70, 72, 78, 82, 88, 96]310"""311n = self.level()312if n <= 4:313return [None, 1, 2, 2, 3][n]314return ZZ(sum([phi(d)*phi(n/d)/ZZ(2) for d in n.divisors()]))315316def index(self):317r"""318Return the index of self in the full modular group. This is given by the formula319320.. math::321322N^2 \prod_{\substack{p \mid N \\ \text{$p$ prime}}} \left( 1 - \frac{1}{p^2}\right).323324EXAMPLE::325326sage: Gamma1(180).index()32720736328sage: [Gamma1(n).projective_index() for n in [1..16]]329[1, 3, 4, 6, 12, 12, 24, 24, 36, 36, 60, 48, 84, 72, 96, 96]330"""331return prod([p**(2*e) - p**(2*e-2) for (p,e) in self.level().factor()])332333##################################################################################334# Dimension formulas for Gamma1, accepting a Dirichlet character as an argument. #335##################################################################################336337def dimension_modular_forms(self, k=2, eps=None, algorithm="CohenOesterle"):338r"""339Return the dimension of the space of modular forms for self, or the340dimension of the subspace corresponding to the given character if one341is supplied.342343INPUT:344345- ``k`` - an integer (default: 2), the weight.346347- ``eps`` - either None or a Dirichlet character modulo N, where N is348the level of this group. If this is None, then the dimension of the349whole space is returned; otherwise, the dimension of the subspace of350forms of character eps.351352- ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This353specifies the method to use in the case of nontrivial character:354either the Cohen--Oesterle formula as described in Stein's book, or355by Moebius inversion using the subgroups GammaH (a method due to356Jordi Quer).357358EXAMPLES::359360sage: K = CyclotomicField(3)361sage: eps = DirichletGroup(7*43,K).0^2362sage: G = Gamma1(7*43)363364sage: G.dimension_modular_forms(2, eps)36532366sage: G.dimension_modular_forms(2, eps, algorithm="Quer")36732368"""369370return self.dimension_cusp_forms(k, eps, algorithm) + self.dimension_eis(k, eps, algorithm)371372def dimension_cusp_forms(self, k=2, eps=None, algorithm="CohenOesterle"):373r"""374Return the dimension of the space of cusp forms for self, or the375dimension of the subspace corresponding to the given character if one376is supplied.377378INPUT:379380- ``k`` - an integer (default: 2), the weight.381382- ``eps`` - either None or a Dirichlet character modulo N, where N is383the level of this group. If this is None, then the dimension of the384whole space is returned; otherwise, the dimension of the subspace of385forms of character eps.386387- ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This388specifies the method to use in the case of nontrivial character:389either the Cohen--Oesterle formula as described in Stein's book, or390by Moebius inversion using the subgroups GammaH (a method due to391Jordi Quer).392393EXAMPLES:394395We compute the same dimension in two different ways ::396397sage: K = CyclotomicField(3)398sage: eps = DirichletGroup(7*43,K).0^2399sage: G = Gamma1(7*43)400401Via Cohen--Oesterle: ::402403sage: Gamma1(7*43).dimension_cusp_forms(2, eps)40428405406Via Quer's method: ::407408sage: Gamma1(7*43).dimension_cusp_forms(2, eps, algorithm="Quer")40928410411Some more examples: ::412413sage: G.<eps> = DirichletGroup(9)414sage: [Gamma1(9).dimension_cusp_forms(k, eps) for k in [1..10]]415[0, 0, 1, 0, 3, 0, 5, 0, 7, 0]416sage: [Gamma1(9).dimension_cusp_forms(k, eps^2) for k in [1..10]]417[0, 0, 0, 2, 0, 4, 0, 6, 0, 8]418"""419420from all import Gamma0421422# first deal with special cases423424if eps is None:425return GammaH_class.dimension_cusp_forms(self, k)426427N = self.level()428if eps.base_ring().characteristic() != 0:429raise ValueError430431eps = DirichletGroup(N, eps.base_ring())(eps)432433if eps.is_trivial():434return Gamma0(N).dimension_cusp_forms(k)435436if (k <= 0) or ((k % 2) == 1 and eps.is_even()) or ((k%2) == 0 and eps.is_odd()):437return ZZ(0)438439if k == 1:440try:441n = self.dimension_cusp_forms(1)442if n == 0:443return ZZ(0)444else: # never happens at present445raise NotImplementedError, "Computations of dimensions of spaces of weight 1 cusp forms not implemented at present"446except NotImplementedError:447raise448449# now the main part450451if algorithm == "Quer":452n = eps.order()453dim = ZZ(0)454for d in n.divisors():455G = GammaH_constructor(N,(eps**d).kernel())456dim = dim + moebius(d)*G.dimension_cusp_forms(k)457return dim//phi(n)458459elif algorithm == "CohenOesterle":460K = eps.base_ring()461from sage.modular.dims import CohenOesterle462from all import Gamma0463return ZZ( K(Gamma0(N).index() * (k-1)/ZZ(12)) + CohenOesterle(eps,k) )464465else: #algorithm not in ["CohenOesterle", "Quer"]:466raise ValueError, "Unrecognised algorithm in dimension_cusp_forms"467468469def dimension_eis(self, k=2, eps=None, algorithm="CohenOesterle"):470r"""471Return the dimension of the space of Eisenstein series forms for self,472or the dimension of the subspace corresponding to the given character473if one is supplied.474475INPUT:476477- ``k`` - an integer (default: 2), the weight.478479- ``eps`` - either None or a Dirichlet character modulo N, where N is480the level of this group. If this is None, then the dimension of the481whole space is returned; otherwise, the dimension of the subspace of482Eisenstein series of character eps.483484- ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This485specifies the method to use in the case of nontrivial character:486either the Cohen--Oesterle formula as described in Stein's book, or487by Moebius inversion using the subgroups GammaH (a method due to488Jordi Quer).489490AUTHORS:491492- William Stein - Cohen--Oesterle algorithm493494- Jordi Quer - algorithm based on GammaH subgroups495496- David Loeffler (2009) - code refactoring497498EXAMPLES:499500The following two computations use different algorithms: ::501502sage: [Gamma1(36).dimension_eis(1,eps) for eps in DirichletGroup(36)]503[0, 4, 3, 0, 0, 2, 6, 0, 0, 2, 3, 0]504sage: [Gamma1(36).dimension_eis(1,eps,algorithm="Quer") for eps in DirichletGroup(36)]505[0, 4, 3, 0, 0, 2, 6, 0, 0, 2, 3, 0]506507So do these: ::508509sage: [Gamma1(48).dimension_eis(3,eps) for eps in DirichletGroup(48)]510[0, 12, 0, 4, 0, 8, 0, 4, 12, 0, 4, 0, 8, 0, 4, 0]511sage: [Gamma1(48).dimension_eis(3,eps,algorithm="Quer") for eps in DirichletGroup(48)]512[0, 12, 0, 4, 0, 8, 0, 4, 12, 0, 4, 0, 8, 0, 4, 0]513"""514from all import Gamma0515516# first deal with special cases517518if eps is None:519return GammaH_class.dimension_eis(self, k)520521N = self.level()522eps = DirichletGroup(N)(eps)523524if eps.is_trivial():525return Gamma0(N).dimension_eis(k)526527# Note case of k = 0 and trivial character already dealt with separately, so k <= 0 here is valid:528if (k <= 0) or ((k % 2) == 1 and eps.is_even()) or ((k%2) == 0 and eps.is_odd()):529return ZZ(0)530531if algorithm == "Quer":532n = eps.order()533dim = ZZ(0)534for d in n.divisors():535G = GammaH_constructor(N,(eps**d).kernel())536dim = dim + moebius(d)*G.dimension_eis(k)537return dim//phi(n)538539elif algorithm == "CohenOesterle":540from sage.modular.dims import CohenOesterle541K = eps.base_ring()542j = 2-k543# We use the Cohen-Oesterle formula in a subtle way to544# compute dim M_k(N,eps) (see Ch. 6 of William Stein's book on545# computing with modular forms).546alpha = -ZZ( K(Gamma0(N).index()*(j-1)/ZZ(12)) + CohenOesterle(eps,j) )547if k == 1:548return alpha549else:550return alpha - self.dimension_cusp_forms(k, eps)551552else: #algorithm not in ["CohenOesterle", "Quer"]:553raise ValueError, "Unrecognised algorithm in dimension_eis"554555def dimension_new_cusp_forms(self, k=2, eps=None, p=0, algorithm="CohenOesterle"):556r"""557Dimension of the new subspace (or `p`-new subspace) of cusp forms of558weight `k` and character `\varepsilon`.559560INPUT:561562- ``k`` - an integer (default: 2)563564- ``eps`` - a Dirichlet character565566- ``p`` - a prime (default: 0); just the `p`-new subspace if given567568- ``algorithm`` - either "CohenOesterle" (the default) or "Quer". This569specifies the method to use in the case of nontrivial character:570either the Cohen--Oesterle formula as described in Stein's book, or571by Moebius inversion using the subgroups GammaH (a method due to572Jordi Quer).573574EXAMPLES::575576sage: G = DirichletGroup(9)577sage: eps = G.0^3578sage: eps.conductor()5793580sage: [Gamma1(9).dimension_new_cusp_forms(k, eps) for k in [2..10]]581[0, 0, 0, 2, 0, 2, 0, 2, 0]582sage: [Gamma1(9).dimension_cusp_forms(k, eps) for k in [2..10]]583[0, 0, 0, 2, 0, 4, 0, 6, 0]584sage: [Gamma1(9).dimension_new_cusp_forms(k, eps, 3) for k in [2..10]]585[0, 0, 0, 2, 0, 2, 0, 2, 0]586587Double check using modular symbols (independent calculation)::588589sage: [ModularSymbols(eps,k,sign=1).cuspidal_subspace().new_subspace().dimension() for k in [2..10]]590[0, 0, 0, 2, 0, 2, 0, 2, 0]591sage: [ModularSymbols(eps,k,sign=1).cuspidal_subspace().new_subspace(3).dimension() for k in [2..10]]592[0, 0, 0, 2, 0, 2, 0, 2, 0]593594Another example at level 33::595596sage: G = DirichletGroup(33)597sage: eps = G.1598sage: eps.conductor()59911600sage: [Gamma1(33).dimension_new_cusp_forms(k, G.1) for k in [2..4]]601[0, 4, 0]602sage: [Gamma1(33).dimension_new_cusp_forms(k, G.1, algorithm="Quer") for k in [2..4]]603[0, 4, 0]604sage: [Gamma1(33).dimension_new_cusp_forms(k, G.1^2) for k in [2..4]]605[2, 0, 6]606sage: [Gamma1(33).dimension_new_cusp_forms(k, G.1^2, p=3) for k in [2..4]]607[2, 0, 6]608609"""610611if eps == None:612return GammaH_class.dimension_new_cusp_forms(self, k, p)613614N = self.level()615eps = DirichletGroup(N)(eps)616617from all import Gamma0618619if eps.is_trivial():620return Gamma0(N).dimension_new_cusp_forms(k, p)621622from congroup_gammaH import mumu623624if p == 0 or N%p != 0 or eps.conductor().valuation(p) == N.valuation(p):625D = [eps.conductor()*d for d in divisors(N//eps.conductor())]626return sum([Gamma1_constructor(M).dimension_cusp_forms(k, eps.restrict(M), algorithm)*mumu(N//M) for M in D])627eps_p = eps.restrict(N//p)628old = Gamma1_constructor(N//p).dimension_cusp_forms(k, eps_p, algorithm)629return self.dimension_cusp_forms(k, eps, algorithm) - 2*old630631632633634