Path: blob/master/src/sage/modular/arithgroup/congroup_gamma0.py
8820 views
r"""1Congruence Subgroup `\Gamma_0(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 congroup_gammaH import GammaH_class17from congroup_gamma1 import is_Gamma118from sage.modular.modsym.p1list import lift_to_sl2z19from congroup_generic import CongruenceSubgroup2021from sage.modular.cusps import Cusp22from sage.misc.cachefunc import cached_method23from sage.rings.all import (IntegerModRing, kronecker_symbol, ZZ)24from sage.misc.misc import prod25import sage.modular.modsym.p1list26import sage.rings.arith as arith2728# Just for now until we make an SL_2 group type.29from sage.matrix.matrix_space import MatrixSpace30Mat2Z = MatrixSpace(ZZ,2)313233def is_Gamma0(x):34"""35Return True if x is a congruence subgroup of type Gamma0.3637EXAMPLES::3839sage: from sage.modular.arithgroup.all import is_Gamma040sage: is_Gamma0(SL2Z)41True42sage: is_Gamma0(Gamma0(13))43True44sage: is_Gamma0(Gamma1(6))45False46"""47return isinstance(x, Gamma0_class)4849_gamma0_cache = {}50def Gamma0_constructor(N):51"""52Return the congruence subgroup Gamma0(N).5354EXAMPLES::5556sage: G = Gamma0(51) ; G # indirect doctest57Congruence Subgroup Gamma0(51)58sage: G == Gamma0(51)59True60sage: G is Gamma0(51)61True62"""63from all import SL2Z64if N == 1: return SL2Z65try:66return _gamma0_cache[N]67except KeyError:68_gamma0_cache[N] = Gamma0_class(N)69return _gamma0_cache[N]7071class Gamma0_class(GammaH_class):72r"""73The congruence subgroup `\Gamma_0(N)`.7475TESTS::7677sage: Gamma0(11).dimension_cusp_forms(2)78179sage: a = Gamma0(1).dimension_cusp_forms(2); a80081sage: type(a)82<type 'sage.rings.integer.Integer'>83sage: Gamma0(5).dimension_cusp_forms(0)84085sage: Gamma0(20).dimension_cusp_forms(1)86087sage: Gamma0(20).dimension_cusp_forms(4)8868990sage: Gamma0(23).dimension_cusp_forms(2)91292sage: Gamma0(1).dimension_cusp_forms(24)93294sage: Gamma0(3).dimension_cusp_forms(3)95096sage: Gamma0(11).dimension_cusp_forms(-1)9709899sage: Gamma0(22).dimension_new_cusp_forms()1000101sage: Gamma0(100).dimension_new_cusp_forms(2, 5)1025103104Independently compute the dimension 5 above::105106sage: m = ModularSymbols(100, 2,sign=1).cuspidal_subspace()107sage: m.new_subspace(5)108Modular Symbols subspace of dimension 5 of Modular Symbols space of dimension 18 for Gamma_0(100) of weight 2 with sign 1 over Rational Field109110"""111112113def __init__(self, level):114r"""115The congruence subgroup `\Gamma_0(N)`.116117EXAMPLES::118119sage: G = Gamma0(11); G120Congruence Subgroup Gamma0(11)121sage: TestSuite(G).run()122sage: G is loads(dumps(G))123True124125TESTS::126127sage: g = Gamma0(5)([1,1,0,1])128sage: g in Gamma0(7)129True130sage: g = Gamma0(5)([1,0,5,1])131sage: g in Gamma0(7)132False133sage: g = Gamma0(2)([1,0,0,1])134sage: g in SL2Z135True136"""137CongruenceSubgroup.__init__(self, level)138139# We *don't* call the GammaH init script, as this requires calculating140# generators for the units modulo N which is time-consuming; this will141# be done if needed by the _generators_for_H and _list_of_elements_in_H142# methods.143#144#GammaH_class.__init__(self, level, [int(x) for x in IntegerModRing(level).unit_gens()])145146def _repr_(self):147"""148Return the string representation of self.149150EXAMPLES::151152sage: Gamma0(98)._repr_()153'Congruence Subgroup Gamma0(98)'154"""155return "Congruence Subgroup Gamma0(%s)"%self.level()156157def __reduce__(self):158"""159Used for pickling self.160161EXAMPLES::162163sage: Gamma0(22).__reduce__()164(<function Gamma0_constructor at ...>, (22,))165"""166return Gamma0_constructor, (self.level(),)167168def _latex_(self):169r"""170Return the \LaTeX representation of self.171172EXAMPLES::173174sage: Gamma0(20)._latex_()175'\\Gamma_0(20)'176sage: latex(Gamma0(20))177\Gamma_0(20)178"""179return "\\Gamma_0(%s)"%self.level()180181@cached_method182def _generators_for_H(self):183"""184Return generators for the subgroup H of the units mod185self.level() that defines self.186187EXAMPLES::188189sage: Gamma0(15)._generators_for_H()190[11, 7]191"""192if self.level() in [1, 2]:193return []194return [ZZ(x) for x in IntegerModRing(self.level()).unit_gens()]195196@cached_method197def _list_of_elements_in_H(self):198"""199Returns a sorted list of Python ints that are representatives200between 0 and N-1 of the elements of H.201202EXAMPLES::203204sage: G = Gamma0(11)205sage: G._list_of_elements_in_H()206[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]207208sage: G = Gamma0(6)209sage: G._list_of_elements_in_H()210[1, 5]211212sage: G = Gamma0(1)213sage: G._list_of_elements_in_H()214[1]215"""216N = self.level()217if N != 1:218gcd = arith.gcd219H = [ x for x in range(1, N) if gcd(x, N) == 1 ]220else:221H = [1]222223return H224225def divisor_subgroups(self):226r"""227Return the subgroups of SL2Z of the form Gamma0(M) that contain this subgroup,228i.e. those for M a divisor of N.229230EXAMPLE::231232sage: Gamma0(24).divisor_subgroups()233[Modular Group SL(2,Z),234Congruence Subgroup Gamma0(2),235Congruence Subgroup Gamma0(3),236Congruence Subgroup Gamma0(4),237Congruence Subgroup Gamma0(6),238Congruence Subgroup Gamma0(8),239Congruence Subgroup Gamma0(12),240Congruence Subgroup Gamma0(24)]241"""242return [Gamma0_constructor(M) for M in self.level().divisors()]243244def is_even(self):245r"""246Return True precisely if this subgroup contains the matrix -1.247248Since `\Gamma0(N)` always contains the matrix -1, this always249returns True.250251EXAMPLES::252253sage: Gamma0(12).is_even()254True255sage: SL2Z.is_even()256True257"""258return True259260def is_subgroup(self, right):261"""262Return True if self is a subgroup of right.263264EXAMPLES::265266sage: G = Gamma0(20)267sage: G.is_subgroup(SL2Z)268True269sage: G.is_subgroup(Gamma0(4))270True271sage: G.is_subgroup(Gamma0(20))272True273sage: G.is_subgroup(Gamma0(7))274False275sage: G.is_subgroup(Gamma1(20))276False277sage: G.is_subgroup(GammaH(40, []))278False279sage: Gamma0(80).is_subgroup(GammaH(40, [31, 21, 17]))280True281sage: Gamma0(2).is_subgroup(Gamma1(2))282True283"""284if right.level() == 1:285return True286if is_Gamma0(right):287return self.level() % right.level() == 0288if is_Gamma1(right):289if right.level() >= 3:290return False291elif right.level() == 2:292return self.level() == 2293# case level 1 dealt with above294else:295return GammaH_class.is_subgroup(self, right)296297def coset_reps(self):298r"""299Return representatives for the right cosets of this congruence300subgroup in `{\rm SL}_2(\ZZ)` as a generator object.301302Use ``list(self.coset_reps())`` to obtain coset reps as a303list.304305EXAMPLES::306307sage: list(Gamma0(5).coset_reps())308[309[1 0] [ 0 -1] [1 0] [ 0 -1] [ 0 -1] [ 0 -1]310[0 1], [ 1 0], [1 1], [ 1 2], [ 1 3], [ 1 4]311]312sage: list(Gamma0(4).coset_reps())313[314[1 0] [ 0 -1] [1 0] [ 0 -1] [ 0 -1] [1 0]315[0 1], [ 1 0], [1 1], [ 1 2], [ 1 3], [2 1]316]317sage: list(Gamma0(1).coset_reps())318[319[1 0]320[0 1]321]322"""323from all import SL2Z324N = self.level()325if N == 1: # P1List isn't very happy working modulo 1326yield SL2Z([1,0,0,1])327else:328for z in sage.modular.modsym.p1list.P1List(N):329yield SL2Z(lift_to_sl2z(z[0], z[1], N))330331@cached_method332def generators(self, algorithm="farey"):333r"""334Return generators for this congruence subgroup.335336INPUT:337338- ``algorithm`` (string): either ``farey`` (default) or339``todd-coxeter``.340341If ``algorithm`` is set to ``"farey"``, then the generators will be342calculated using Farey symbols, which will always return a *minimal*343generating set. See :mod:`~sage.modular.arithgroup.farey_symbol` for344more information.345346If ``algorithm`` is set to ``"todd-coxeter"``, a simpler algorithm347based on Todd-Coxeter enumeration will be used. This tends to return348far larger sets of generators.349350EXAMPLE::351352sage: Gamma0(3).generators()353[354[1 1] [-1 1]355[0 1], [-3 2]356]357sage: Gamma0(3).generators(algorithm="todd-coxeter")358[359[1 1] [-1 0] [ 1 -1] [1 0] [1 1] [-1 0] [ 1 0]360[0 1], [ 0 -1], [ 0 1], [3 1], [0 1], [ 3 -1], [-3 1]361]362sage: SL2Z.gens()363(364[ 0 -1] [1 1]365[ 1 0], [0 1]366)367"""368if self.level() == 1:369# we return a fixed set of generators for SL2Z, for historical370# reasons, which aren't the ones the Farey symbol code gives371return [ self([0,-1,1,0]), self([1,1,0,1]) ]372373elif algorithm=="farey":374return self.farey_symbol().generators()375376elif algorithm=="todd-coxeter":377from sage.modular.modsym.p1list import P1List378from congroup_pyx import generators_helper379level = self.level()380if level == 1: # P1List isn't very happy working mod 1381return [ self([0,-1,1,0]), self([1,1,0,1]) ]382gen_list = generators_helper(P1List(level), level, Mat2Z)383return [self(g, check=False) for g in gen_list]384385else:386raise ValueError, "Unknown algorithm '%s' (should be either 'farey' or 'todd-coxeter')" % algorithm387388def gamma_h_subgroups(self):389r"""390Return the subgroups of the form `\Gamma_H(N)` contained391in self, where `N` is the level of self.392393EXAMPLES::394395sage: G = Gamma0(11)396sage: G.gamma_h_subgroups()397[Congruence Subgroup Gamma0(11), Congruence Subgroup Gamma_H(11) with H generated by [4], Congruence Subgroup Gamma_H(11) with H generated by [10], Congruence Subgroup Gamma1(11)]398sage: G = Gamma0(12)399sage: G.gamma_h_subgroups()400[Congruence Subgroup Gamma0(12), Congruence Subgroup Gamma_H(12) with H generated by [7], Congruence Subgroup Gamma_H(12) with H generated by [11], Congruence Subgroup Gamma_H(12) with H generated by [5], Congruence Subgroup Gamma1(12)]401"""402from all import GammaH403N = self.level()404R = IntegerModRing(N)405return [GammaH(N, H) for H in R.multiplicative_subgroups()]406407def _contains_sl2(self, a,b,c,d):408r"""409Test whether x is an element of this group.410411EXAMPLES::412413sage: G = Gamma0(12)414sage: [1, 0, 24, 1] in G415True416sage: matrix(ZZ, 2, [1, 1, -12, -11]) in G417True418sage: SL2Z([0,-1,1,0]) in G419False420sage: 1 in G421True422sage: -1 in G423True424sage: 2 in G425False426427The _element_constructor_ method calls this method::428429sage: G([1, 0, 23, 1]) # indirect doctest430Traceback (most recent call last):431...432TypeError: matrix [ 1 0]433[23 1] is not an element of Congruence Subgroup Gamma0(12)434"""435return (c % self.level() == 0)436437def _find_cusps(self):438r"""439Return an ordered list of inequivalent cusps for self, i.e. a440set of representatives for the orbits of self on441`\mathbb{P}^1(\QQ)`. These are returned in a reduced442form; see self.reduce_cusp for the definition of reduced.443444ALGORITHM:445Uses explicit formulae specific to `\Gamma_0(N)`: a reduced cusp on446`\Gamma_0(N)` is always of the form `a/d` where `d | N`, and `a_1/d447\sim a_2/d` if and only if `a_1 \cong a_2 \bmod {\rm gcd}(d,448N/d)`.449450EXAMPLES::451452sage: Gamma0(90)._find_cusps()453[0, 1/45, 1/30, 1/18, 1/15, 1/10, 1/9, 2/15, 1/6, 1/5, 1/3, 11/30, 1/2, 2/3, 5/6, Infinity]454sage: Gamma0(1).cusps()455[Infinity]456sage: Gamma0(180).cusps() == Gamma0(180).cusps(algorithm='modsym')457True458"""459N = self.level()460s = []461462for d in arith.divisors(N):463w = arith.gcd(d, N//d)464if w == 1:465if d == 1:466s.append(Cusp(1,0))467elif d == N:468s.append(Cusp(0,1))469else:470s.append(Cusp(1,d))471else:472for a in xrange(1, w):473if arith.gcd(a, w) == 1:474while arith.gcd(a, d//w) != 1:475a += w476s.append(Cusp(a,d))477return sorted(s)478479def ncusps(self):480r"""481Return the number of cusps of this subgroup `\Gamma_0(N)`.482483EXAMPLES::484485sage: [Gamma0(n).ncusps() for n in [1..19]]486[1, 2, 2, 3, 2, 4, 2, 4, 4, 4, 2, 6, 2, 4, 4, 6, 2, 8, 2]487sage: [Gamma0(n).ncusps() for n in prime_range(2,100)]488[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]489"""490n = self.level()491return sum([arith.euler_phi(arith.gcd(d,n//d)) for d in n.divisors()])492493494def nu2(self):495r"""496Return the number of elliptic points of order 2 for this congruence497subgroup `\Gamma_0(N)`. The number of these is given by a standard formula:4980 if `N` is divisible by 4 or any prime congruent to -1 mod 4, and499otherwise `2^d` where d is the number of odd primes dividing `N`.500501EXAMPLE::502503sage: Gamma0(2).nu2()5041505sage: Gamma0(4).nu2()5060507sage: Gamma0(21).nu2()5080509sage: Gamma0(1105).nu2()5108511sage: [Gamma0(n).nu2() for n in [1..19]]512[1, 1, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 2, 0, 0]513"""514n = self.level()515if n%4 == 0:516return ZZ(0)517return prod([ 1 + kronecker_symbol(-4, p) for p, _ in n.factor()])518519def nu3(self):520r"""521Return the number of elliptic points of order 3 for this congruence522subgroup `\Gamma_0(N)`. The number of these is given by a standard formula:5230 if `N` is divisible by 9 or any prime congruent to -1 mod 3, and524otherwise `2^d` where d is the number of primes other than 3 dividing `N`.525526EXAMPLE::527528sage: Gamma0(2).nu3()5290530sage: Gamma0(3).nu3()5311532sage: Gamma0(9).nu3()5330534sage: Gamma0(7).nu3()5352536sage: Gamma0(21).nu3()5372538sage: Gamma0(1729).nu3()5398540"""541n = self.level()542if (n % 9 == 0):543return ZZ(0)544return prod([ 1 + kronecker_symbol(-3, p) for p, _ in n.factor()])545546def index(self):547r"""548Return the index of self in the full modular group. This is given by549550.. math::551552N \prod_{\substack{p \mid N \\ \text{$p$ prime}}}\left(1 + \frac{1}{p}\right).553554EXAMPLE::555sage: [Gamma0(n).index() for n in [1..19]]556[1, 3, 4, 6, 6, 12, 8, 12, 12, 18, 12, 24, 14, 24, 24, 24, 18, 36, 20]557sage: Gamma0(32041).index()55832220559"""560return prod([p**e + p**(e-1) for (p,e) in self.level().factor()])561562563564565566