Path: blob/master/sage/modular/arithgroup/congroup_gamma1.py
4091 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_generic import is_CongruenceSubgroup20from congroup_gammaH import GammaH_class, is_GammaH, GammaH_constructor21#from congroup_gamma0 import Gamma0_constructor -- circular!22from arithgroup_element import ArithmeticSubgroupElement23from sage.rings.all import ZZ, euler_phi as phi, moebius, divisors24from sage.modular.dirichlet import DirichletGroup2526# Just for now until we make an SL_2 group type.27from sage.rings.finite_rings.integer_mod_ring import IntegerModRing28from sage.matrix.matrix_space import MatrixSpace29Mat2Z = MatrixSpace(IntegerModRing(0),2)3031def is_Gamma1(x):32"""33Return True if x is a congruence subgroup of type Gamma1.3435EXAMPLES::3637sage: from sage.modular.arithgroup.all import is_Gamma138sage: is_Gamma1(SL2Z)39False40sage: is_Gamma1(Gamma1(13))41True42sage: is_Gamma1(Gamma0(6))43False44sage: is_Gamma1(GammaH(12, [])) # trick question!45True46sage: is_Gamma1(GammaH(12, [5]))47False48"""49#from congroup_sl2z import is_SL2Z50#return (isinstance(x, Gamma1_class) or is_SL2Z(x))51return isinstance(x, Gamma1_class)5253_gamma1_cache = {}54def Gamma1_constructor(N):55r"""56Return the congruence subgroup `\Gamma_1(N)`.5758EXAMPLES::5960sage: Gamma1(5) # indirect doctest61Congruence Subgroup Gamma1(5)62sage: G = Gamma1(23)63sage: G is Gamma1(23)64True65sage: G == loads(dumps(G))66True67sage: G is loads(dumps(G))68True69"""70if N == 1 or N == 2:71from congroup_gamma0 import Gamma0_constructor72return Gamma0_constructor(N)73try:74return _gamma1_cache[N]75except KeyError:76_gamma1_cache[N] = Gamma1_class(N)77return _gamma1_cache[N]7879class Gamma1_class(GammaH_class):80r"""81The congruence subgroup `\Gamma_1(N)`.8283TESTS::8485sage: [Gamma1(n).genus() for n in prime_range(2,100)]86[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]87sage: [Gamma1(n).index() for n in [1..10]]88[1, 3, 8, 12, 24, 24, 48, 48, 72, 72]8990sage: [Gamma1(n).dimension_cusp_forms() for n in [1..20]]91[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 1, 1, 2, 5, 2, 7, 3]92sage: [Gamma1(n).dimension_cusp_forms(1) for n in [1..20]]93[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]94sage: [Gamma1(4).dimension_cusp_forms(k) for k in [1..20]]95[0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8]96sage: Gamma1(23).dimension_cusp_forms(1)97Traceback (most recent call last):98...99NotImplementedError: Computation of dimensions of weight 1 cusp forms spaces not implemented in general100"""101102def __init__(self, level):103r"""104The congruence subgroup `\Gamma_1(N)`.105106EXAMPLES::107108sage: G = Gamma1(11); G109Congruence Subgroup Gamma1(11)110sage: loads(G.dumps()) == G111True112"""113GammaH_class.__init__(self, level, [])114115def __cmp__(self, other):116"""117Compare self to other.118119The ordering on congruence subgroups of the form `\Gamma_H(N)` for120some H is first by level and then by the subgroup H. In121particular, this means that we have `\Gamma_1(N) < \Gamma_H(N) <122\Gamma_0(N)` for every nontrivial subgroup H.123124EXAMPLES::125126sage: G = Gamma1(86)127sage: G.__cmp__(G)1280129sage: G.__cmp__(GammaH(86, [11])) is not 0130True131sage: Gamma1(12) < Gamma0(12)132True133sage: Gamma1(10) < Gamma1(12)134True135sage: Gamma1(11) == GammaH(11, [])136True137sage: Gamma1(1) == SL2Z138True139sage: Gamma1(2) == Gamma0(2)140True141"""142from all import is_GammaH143if not is_CongruenceSubgroup(other):144return cmp(type(self), type(other))145146c = cmp(self.level(), other.level())147if c: return c148149# Since Gamma1(N) is GammaH(N) for H all of (Z/N)^\times,150# we know how to compare it to any other GammaH without having151# to look at self._list_of_elements_in_H().152if is_GammaH(other):153if is_Gamma1(other):154return 0155else:156H = other._generators_for_H()157return cmp(0,len(H))158return cmp(type(self), type(other))159160def _repr_(self):161"""162Return the string representation of self.163164EXAMPLES::165166sage: Gamma1(133)._repr_()167'Congruence Subgroup Gamma1(133)'168"""169return "Congruence Subgroup Gamma1(%s)"%self.level()170171def __reduce__(self):172"""173Used for pickling self.174175EXAMPLES::176177sage: Gamma1(82).__reduce__()178(<function Gamma1_constructor at ...>, (82,))179"""180return Gamma1_constructor, (self.level(),)181182def _latex_(self):183r"""184Return the \LaTeX representation of self.185186EXAMPLES::187188sage: Gamma1(3)._latex_()189'\\Gamma_1(3)'190sage: latex(Gamma1(3))191\Gamma_1(3)192"""193return "\\Gamma_1(%s)"%self.level()194195def is_even(self):196"""197Return True precisely if this subgroup contains the matrix -1.198199EXAMPLES::200201sage: Gamma1(1).is_even()202True203sage: Gamma1(2).is_even()204True205sage: Gamma1(15).is_even()206False207"""208return self.level() in [1,2]209210def is_subgroup(self, right):211"""212Return True if self is a subgroup of right.213214EXAMPLES::215216sage: Gamma1(3).is_subgroup(SL2Z)217True218sage: Gamma1(3).is_subgroup(Gamma1(5))219False220sage: Gamma1(3).is_subgroup(Gamma1(6))221False222sage: Gamma1(6).is_subgroup(Gamma1(3))223True224sage: Gamma1(6).is_subgroup(Gamma0(2))225True226sage: Gamma1(80).is_subgroup(GammaH(40, []))227True228sage: Gamma1(80).is_subgroup(GammaH(40, [21]))229True230"""231if right.level() == 1:232return True233if is_GammaH(right):234return self.level() % right.level() == 0235else:236raise NotImplementedError237238@cached_method239def generators(self, algorithm="farey"):240r"""241Return generators for this congruence subgroup. The result is cached.242243INPUT:244245- ``algorithm`` (string): either ``farey`` (default) or246``todd-coxeter``.247248If ``algorithm`` is set to ``"farey"``, then the generators will be249calculated using Farey symbols, which will always return a *minimal*250generating set. See :mod:`~sage.modular.arithgroup.farey_symbol` for251more information.252253If ``algorithm`` is set to ``"todd-coxeter"``, a simpler algorithm254based on Todd-Coxeter enumeration will be used. This tends to return255far larger sets of generators.256257EXAMPLE::258259sage: Gamma1(3).generators()260[261[1 1] [ 1 -1]262[0 1], [ 3 -2]263]264sage: Gamma1(3).generators(algorithm="todd-coxeter")265[266[1 1] [-20 9] [ 4 1] [-5 -2] [ 1 -1] [1 0] [1 1] [-5 2]267[0 1], [ 51 -23], [-9 -2], [ 3 1], [ 0 1], [3 1], [0 1], [12 -5],268<BLANKLINE>269[ 1 0] [ 4 -1] [ -5 3] [ 1 -1] [ 7 -3] [ 4 -1] [ -5 3]270[-3 1], [ 9 -2], [-12 7], [ 3 -2], [12 -5], [ 9 -2], [-12 7]271]272"""273if algorithm=="farey":274return self.farey_symbol().generators()275elif algorithm=="todd-coxeter":276from sage.modular.modsym.g1list import G1list277from congroup_pyx import generators_helper278level = self.level()279gen_list = generators_helper(G1list(level), level, Mat2Z)280return [self(g, check=False) for g in gen_list]281else:282raise ValueError, "Unknown algorithm '%s' (should be either 'farey' or 'todd-coxeter')" % algorithm283284def __call__(self, x, check=True):285r"""286Create an element of this congruence subgroup from x.287288If the optional flag check is True (default), check whether289x actually gives an element of self.290291EXAMPLES::292293sage: G = Gamma1(5)294sage: G([1, 0, -10, 1])295[ 1 0]296[-10 1]297sage: G(matrix(ZZ, 2, [6, 1, 5, 1]))298[6 1]299[5 1]300sage: G([1, 1, 6, 7])301Traceback (most recent call last):302...303TypeError: matrix must have diagonal entries (=1, 7) congruent to 1 modulo 5, and lower left entry (=6) divisible by 5304"""305from all import SL2Z306x = SL2Z(x, check)307if not check:308return x309310a = x.a()311c = x.c()312d = x.d()313N = self.level()314if (a%N == 1) and (c%N == 0):315return x316# don't need to check d == 1 mod N as this is automatic from det317else:318raise TypeError, "matrix must have diagonal entries (=%s, %s) congruent to 1 modulo %s, and lower left entry (=%s) divisible by %s" %(a, d, N, c, N)319320321def nu2(self):322r"""323Calculate the number of orbits of elliptic points of order 2 for this324subgroup `\Gamma_1(N)`. This is known to be 0 if N > 2.325326EXAMPLE::327328sage: Gamma1(2).nu2()3291330sage: Gamma1(457).nu2()3310332sage: [Gamma1(n).nu2() for n in [1..16]]333[1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]334"""335336N = self.level()337if N > 2: return 0338elif N == 2 or N == 1: return 1339340def nu3(self):341r"""342Calculate the number of orbits of elliptic points of order 3 for this343subgroup `\Gamma_1(N)`. This is known to be 0 if N > 3.344345EXAMPLE::346347sage: Gamma1(2).nu3()3480349sage: Gamma1(3).nu3()3501351sage: Gamma1(457).nu3()3520353sage: [Gamma1(n).nu3() for n in [1..10]]354[1, 0, 1, 0, 0, 0, 0, 0, 0, 0]355"""356357N = self.level()358if N > 3 or N == 2: return 0359else: return 1360361def ncusps(self):362r"""363Return the number of cusps of this subgroup `\Gamma_1(N)`.364365EXAMPLES::366367sage: [Gamma1(n).ncusps() for n in [1..15]]368[1, 2, 2, 3, 4, 4, 6, 6, 8, 8, 10, 10, 12, 12, 16]369sage: [Gamma1(n).ncusps() for n in prime_range(2, 100)]370[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]371"""372n = self.level()373if n <= 4:374return [None, 1, 2, 2, 3][n]375return ZZ(sum([phi(d)*phi(n/d)/ZZ(2) for d in n.divisors()]))376377def index(self):378r"""379Return the index of self in the full modular group. This is given by the formula380381.. math::382383N^2 \prod_{\substack{p \mid N \\ \text{$p$ prime}}} \left( 1 - \frac{1}{p^2}\right).384385EXAMPLE::386387sage: Gamma1(180).index()38820736389sage: [Gamma1(n).projective_index() for n in [1..16]]390[1, 3, 4, 6, 12, 12, 24, 24, 36, 36, 60, 48, 84, 72, 96, 96]391"""392return prod([p**(2*e) - p**(2*e-2) for (p,e) in self.level().factor()])393394##################################################################################395# Dimension formulas for Gamma1, accepting a Dirichlet character as an argument. #396##################################################################################397398def dimension_modular_forms(self, k=2, eps=None, algorithm="CohenOesterle"):399r"""400Return the dimension of the space of modular forms for self, or the401dimension of the subspace corresponding to the given character if one402is supplied.403404INPUT:405406- ``k`` - an integer (default: 2), the weight.407408- ``eps`` - either None or a Dirichlet character modulo N, where N is409the level of this group. If this is None, then the dimension of the410whole space is returned; otherwise, the dimension of the subspace of411forms of character eps.412413- ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This414specifies the method to use in the case of nontrivial character:415either the Cohen--Oesterle formula as described in Stein's book, or416by Moebius inversion using the subgroups GammaH (a method due to417Jordi Quer).418419EXAMPLES::420421sage: K = CyclotomicField(3)422sage: eps = DirichletGroup(7*43,K).0^2423sage: G = Gamma1(7*43)424425sage: G.dimension_modular_forms(2, eps)42632427sage: G.dimension_modular_forms(2, eps, algorithm="Quer")42832429"""430431return self.dimension_cusp_forms(k, eps, algorithm) + self.dimension_eis(k, eps, algorithm)432433def dimension_cusp_forms(self, k=2, eps=None, algorithm="CohenOesterle"):434r"""435Return the dimension of the space of cusp forms for self, or the436dimension of the subspace corresponding to the given character if one437is supplied.438439INPUT:440441- ``k`` - an integer (default: 2), the weight.442443- ``eps`` - either None or a Dirichlet character modulo N, where N is444the level of this group. If this is None, then the dimension of the445whole space is returned; otherwise, the dimension of the subspace of446forms of character eps.447448- ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This449specifies the method to use in the case of nontrivial character:450either the Cohen--Oesterle formula as described in Stein's book, or451by Moebius inversion using the subgroups GammaH (a method due to452Jordi Quer).453454EXAMPLES:455456We compute the same dimension in two different ways ::457458sage: K = CyclotomicField(3)459sage: eps = DirichletGroup(7*43,K).0^2460sage: G = Gamma1(7*43)461462Via Cohen--Oesterle: ::463464sage: Gamma1(7*43).dimension_cusp_forms(2, eps)46528466467Via Quer's method: ::468469sage: Gamma1(7*43).dimension_cusp_forms(2, eps, algorithm="Quer")47028471472Some more examples: ::473474sage: G.<eps> = DirichletGroup(9)475sage: [Gamma1(9).dimension_cusp_forms(k, eps) for k in [1..10]]476[0, 0, 1, 0, 3, 0, 5, 0, 7, 0]477sage: [Gamma1(9).dimension_cusp_forms(k, eps^2) for k in [1..10]]478[0, 0, 0, 2, 0, 4, 0, 6, 0, 8]479"""480481from all import Gamma0482483# first deal with special cases484485if eps is None:486return GammaH_class.dimension_cusp_forms(self, k)487488N = self.level()489if eps.base_ring().characteristic() != 0:490raise ValueError491492eps = DirichletGroup(N, eps.base_ring())(eps)493494if eps.is_trivial():495return Gamma0(N).dimension_cusp_forms(k)496497if (k <= 0) or ((k % 2) == 1 and eps.is_even()) or ((k%2) == 0 and eps.is_odd()):498return ZZ(0)499500if k == 1:501try:502n = self.dimension_cusp_forms(1)503if n == 0:504return ZZ(0)505else: # never happens at present506raise NotImplementedError, "Computations of dimensions of spaces of weight 1 cusp forms not implemented at present"507except NotImplementedError:508raise509510# now the main part511512if algorithm == "Quer":513n = eps.order()514dim = ZZ(0)515for d in n.divisors():516G = GammaH_constructor(N,(eps**d).kernel())517dim = dim + moebius(d)*G.dimension_cusp_forms(k)518return dim//phi(n)519520elif algorithm == "CohenOesterle":521K = eps.base_ring()522from sage.modular.dims import CohenOesterle523from all import Gamma0524return ZZ( K(Gamma0(N).index() * (k-1)/ZZ(12)) + CohenOesterle(eps,k) )525526else: #algorithm not in ["CohenOesterle", "Quer"]:527raise ValueError, "Unrecognised algorithm in dimension_cusp_forms"528529530def dimension_eis(self, k=2, eps=None, algorithm="CohenOesterle"):531r"""532Return the dimension of the space of Eisenstein series forms for self,533or the dimension of the subspace corresponding to the given character534if one is supplied.535536INPUT:537538- ``k`` - an integer (default: 2), the weight.539540- ``eps`` - either None or a Dirichlet character modulo N, where N is541the level of this group. If this is None, then the dimension of the542whole space is returned; otherwise, the dimension of the subspace of543Eisenstein series of character eps.544545- ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This546specifies the method to use in the case of nontrivial character:547either the Cohen--Oesterle formula as described in Stein's book, or548by Moebius inversion using the subgroups GammaH (a method due to549Jordi Quer).550551AUTHORS:552553- William Stein - Cohen--Oesterle algorithm554555- Jordi Quer - algorithm based on GammaH subgroups556557- David Loeffler (2009) - code refactoring558559EXAMPLES:560561The following two computations use different algorithms: ::562563sage: [Gamma1(36).dimension_eis(1,eps) for eps in DirichletGroup(36)]564[0, 4, 3, 0, 0, 2, 6, 0, 0, 2, 3, 0]565sage: [Gamma1(36).dimension_eis(1,eps,algorithm="Quer") for eps in DirichletGroup(36)]566[0, 4, 3, 0, 0, 2, 6, 0, 0, 2, 3, 0]567568So do these: ::569570sage: [Gamma1(48).dimension_eis(3,eps) for eps in DirichletGroup(48)]571[0, 12, 0, 4, 0, 8, 0, 4, 12, 0, 4, 0, 8, 0, 4, 0]572sage: [Gamma1(48).dimension_eis(3,eps,algorithm="Quer") for eps in DirichletGroup(48)]573[0, 12, 0, 4, 0, 8, 0, 4, 12, 0, 4, 0, 8, 0, 4, 0]574"""575from all import Gamma0576577# first deal with special cases578579if eps is None:580return GammaH_class.dimension_eis(self, k)581582N = self.level()583eps = DirichletGroup(N)(eps)584585if eps.is_trivial():586return Gamma0(N).dimension_eis(k)587588# Note case of k = 0 and trivial character already dealt with separately, so k <= 0 here is valid:589if (k <= 0) or ((k % 2) == 1 and eps.is_even()) or ((k%2) == 0 and eps.is_odd()):590return ZZ(0)591592if algorithm == "Quer":593n = eps.order()594dim = ZZ(0)595for d in n.divisors():596G = GammaH_constructor(N,(eps**d).kernel())597dim = dim + moebius(d)*G.dimension_eis(k)598return dim//phi(n)599600elif algorithm == "CohenOesterle":601from sage.modular.dims import CohenOesterle602K = eps.base_ring()603j = 2-k604# We use the Cohen-Oesterle formula in a subtle way to605# compute dim M_k(N,eps) (see Ch. 6 of William Stein's book on606# computing with modular forms).607alpha = -ZZ( K(Gamma0(N).index()*(j-1)/ZZ(12)) + CohenOesterle(eps,j) )608if k == 1:609return alpha610else:611return alpha - self.dimension_cusp_forms(k, eps)612613else: #algorithm not in ["CohenOesterle", "Quer"]:614raise ValueError, "Unrecognised algorithm in dimension_eis"615616def dimension_new_cusp_forms(self, k=2, eps=None, p=0, algorithm="CohenOesterle"):617r"""618Dimension of the new subspace (or `p`-new subspace) of cusp forms of619weight `k` and character `\varepsilon`.620621INPUT:622623- ``k`` - an integer (default: 2)624625- ``eps`` - a Dirichlet character626627- ``p`` - a prime (default: 0); just the `p`-new subspace if given628629- ``algorithm`` - either "CohenOesterle" (the default) or "Quer". This630specifies the method to use in the case of nontrivial character:631either the Cohen--Oesterle formula as described in Stein's book, or632by Moebius inversion using the subgroups GammaH (a method due to633Jordi Quer).634635EXAMPLES::636637sage: G = DirichletGroup(9)638sage: eps = G.0^3639sage: eps.conductor()6403641sage: [Gamma1(9).dimension_new_cusp_forms(k, eps) for k in [2..10]]642[0, 0, 0, 2, 0, 2, 0, 2, 0]643sage: [Gamma1(9).dimension_cusp_forms(k, eps) for k in [2..10]]644[0, 0, 0, 2, 0, 4, 0, 6, 0]645sage: [Gamma1(9).dimension_new_cusp_forms(k, eps, 3) for k in [2..10]]646[0, 0, 0, 2, 0, 2, 0, 2, 0]647648Double check using modular symbols (independent calculation)::649650sage: [ModularSymbols(eps,k,sign=1).cuspidal_subspace().new_subspace().dimension() for k in [2..10]]651[0, 0, 0, 2, 0, 2, 0, 2, 0]652sage: [ModularSymbols(eps,k,sign=1).cuspidal_subspace().new_subspace(3).dimension() for k in [2..10]]653[0, 0, 0, 2, 0, 2, 0, 2, 0]654655Another example at level 33::656657sage: G = DirichletGroup(33)658sage: eps = G.1659sage: eps.conductor()66011661sage: [Gamma1(33).dimension_new_cusp_forms(k, G.1) for k in [2..4]]662[0, 4, 0]663sage: [Gamma1(33).dimension_new_cusp_forms(k, G.1, algorithm="Quer") for k in [2..4]]664[0, 4, 0]665sage: [Gamma1(33).dimension_new_cusp_forms(k, G.1^2) for k in [2..4]]666[2, 0, 6]667sage: [Gamma1(33).dimension_new_cusp_forms(k, G.1^2, p=3) for k in [2..4]]668[2, 0, 6]669670"""671672if eps == None:673return GammaH_class.dimension_new_cusp_forms(self, k, p)674675N = self.level()676eps = DirichletGroup(N)(eps)677678from all import Gamma0679680if eps.is_trivial():681return Gamma0(N).dimension_new_cusp_forms(k, p)682683from congroup_gammaH import mumu684685if p == 0 or N%p != 0 or eps.conductor().valuation(p) == N.valuation(p):686D = [eps.conductor()*d for d in divisors(N//eps.conductor())]687return sum([Gamma1_constructor(M).dimension_cusp_forms(k, eps.restrict(M), algorithm)*mumu(N//M) for M in D])688eps_p = eps.restrict(N//p)689old = Gamma1_constructor(N//p).dimension_cusp_forms(k, eps_p, algorithm)690return self.dimension_cusp_forms(k, eps, algorithm) - 2*old691692693694695