Path: blob/master/sage/modular/arithgroup/congroup_gamma0.py
4079 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_class, is_GammaH17from congroup_gamma1 import is_Gamma118from sage.modular.modsym.p1list import lift_to_sl2z19from congroup_generic import is_CongruenceSubgroup, CongruenceSubgroup20from arithgroup_element import ArithmeticSubgroupElement2122from sage.modular.cusps import Cusp23from sage.misc.cachefunc import cached_method24from sage.rings.all import (IntegerModRing, kronecker_symbol, ZZ)25from sage.misc.misc import prod26import sage.modular.modsym.p1list27import sage.rings.arith as arith2829# Just for now until we make an SL_2 group type.30from sage.matrix.matrix_space import MatrixSpace31Mat2Z = MatrixSpace(ZZ,2)323334def is_Gamma0(x):35"""36Return True if x is a congruence subgroup of type Gamma0.3738EXAMPLES::3940sage: from sage.modular.arithgroup.all import is_Gamma041sage: is_Gamma0(SL2Z)42True43sage: is_Gamma0(Gamma0(13))44True45sage: is_Gamma0(Gamma1(6))46False47"""48return isinstance(x, Gamma0_class)4950_gamma0_cache = {}51def Gamma0_constructor(N):52"""53Return the congruence subgroup Gamma0(N).5455EXAMPLES::5657sage: G = Gamma0(51) ; G # indirect doctest58Congruence Subgroup Gamma0(51)59sage: G == Gamma0(51)60True61sage: G is Gamma0(51)62True63"""64from all import SL2Z65if N == 1: return SL2Z66try:67return _gamma0_cache[N]68except KeyError:69_gamma0_cache[N] = Gamma0_class(N)70return _gamma0_cache[N]7172class Gamma0_class(GammaH_class):73r"""74The congruence subgroup `\Gamma_0(N)`.7576TESTS::7778sage: Gamma0(11).dimension_cusp_forms(2)79180sage: a = Gamma0(1).dimension_cusp_forms(2); a81082sage: type(a)83<type 'sage.rings.integer.Integer'>84sage: Gamma0(5).dimension_cusp_forms(0)85086sage: Gamma0(20).dimension_cusp_forms(1)87088sage: Gamma0(20).dimension_cusp_forms(4)8969091sage: Gamma0(23).dimension_cusp_forms(2)92293sage: Gamma0(1).dimension_cusp_forms(24)94295sage: Gamma0(3).dimension_cusp_forms(3)96097sage: Gamma0(11).dimension_cusp_forms(-1)98099100sage: Gamma0(22).dimension_new_cusp_forms()1010102sage: Gamma0(100).dimension_new_cusp_forms(2, 5)1035104105Independently compute the dimension 5 above::106107sage: m = ModularSymbols(100, 2,sign=1).cuspidal_subspace()108sage: m.new_subspace(5)109Modular Symbols subspace of dimension 5 of Modular Symbols space of dimension 18 for Gamma_0(100) of weight 2 with sign 1 over Rational Field110111"""112113114def __init__(self, level):115r"""116The congruence subgroup `\Gamma_0(N)`.117118EXAMPLES::119120sage: G = Gamma0(11); G121Congruence Subgroup Gamma0(11)122sage: loads(G.dumps()) == G123True124125TESTS::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 __cmp__(self, other):147"""148Compare self to other.149150The ordering on congruence subgroups of the form GammaH(N) for151some H is first by level and then by the subgroup H. In152particular, this means that we have Gamma1(N) < GammaH(N) <153Gamma0(N) for every nontrivial subgroup H.154155EXAMPLES::156157sage: G = Gamma0(86)158sage: G.__cmp__(G)1590160sage: G.__cmp__(GammaH(86, [11])) is not 0161True162sage: Gamma1(17) < Gamma0(17)163True164sage: Gamma0(1) == SL2Z165True166sage: Gamma0(11) == GammaH(11, [2])167True168sage: Gamma0(2) == Gamma1(2)169True170"""171if not is_CongruenceSubgroup(other):172return cmp(type(self), type(other))173174c = cmp(self.level(), other.level())175if c: return c176177# Since Gamma0(N) is GammaH(N) for H all of (Z/N)^\times,178# we know how to compare it to any other GammaH without having179# to look at self._list_of_elements_in_H().180from all import is_GammaH, is_Gamma0181if is_GammaH(other):182if is_Gamma0(other):183return 0184else:185H = other._list_of_elements_in_H()186return cmp(len(H), arith.euler_phi(self.level()))187return cmp(type(self), type(other))188189def _repr_(self):190"""191Return the string representation of self.192193EXAMPLES::194195sage: Gamma0(98)._repr_()196'Congruence Subgroup Gamma0(98)'197"""198return "Congruence Subgroup Gamma0(%s)"%self.level()199200def __reduce__(self):201"""202Used for pickling self.203204EXAMPLES::205206sage: Gamma0(22).__reduce__()207(<function Gamma0_constructor at ...>, (22,))208"""209return Gamma0_constructor, (self.level(),)210211def _latex_(self):212r"""213Return the \LaTeX representation of self.214215EXAMPLES::216217sage: Gamma0(20)._latex_()218'\\Gamma_0(20)'219sage: latex(Gamma0(20))220\Gamma_0(20)221"""222return "\\Gamma_0(%s)"%self.level()223224@cached_method225def _generators_for_H(self):226"""227Return generators for the subgroup H of the units mod228self.level() that defines self.229230EXAMPLES::231232sage: Gamma0(15)._generators_for_H()233[11, 7]234"""235if self.level() in [1, 2]:236return []237return [ZZ(x) for x in IntegerModRing(self.level()).unit_gens()]238239@cached_method240def _list_of_elements_in_H(self):241"""242Returns a sorted list of Python ints that are representatives243between 0 and N-1 of the elements of H.244245EXAMPLES::246247sage: G = Gamma0(11)248sage: G._list_of_elements_in_H()249[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]250251sage: G = Gamma0(6)252sage: G._list_of_elements_in_H()253[1, 5]254255sage: G = Gamma0(1)256sage: G._list_of_elements_in_H()257[1]258"""259N = self.level()260if N != 1:261gcd = arith.gcd262H = [ x for x in range(1, N) if gcd(x, N) == 1 ]263else:264H = [1]265266return H267268def divisor_subgroups(self):269r"""270Return the subgroups of SL2Z of the form Gamma0(M) that contain this subgroup,271i.e. those for M a divisor of N.272273EXAMPLE::274275sage: Gamma0(24).divisor_subgroups()276[Modular Group SL(2,Z),277Congruence Subgroup Gamma0(2),278Congruence Subgroup Gamma0(3),279Congruence Subgroup Gamma0(4),280Congruence Subgroup Gamma0(6),281Congruence Subgroup Gamma0(8),282Congruence Subgroup Gamma0(12),283Congruence Subgroup Gamma0(24)]284"""285return [Gamma0_constructor(M) for M in self.level().divisors()]286287def is_even(self):288r"""289Return True precisely if this subgroup contains the matrix -1.290291Since `\Gamma0(N)` always contains the matrix -1, this always292returns True.293294EXAMPLES::295296sage: Gamma0(12).is_even()297True298sage: SL2Z.is_even()299True300"""301return True302303def is_subgroup(self, right):304"""305Return True if self is a subgroup of right.306307EXAMPLES::308309sage: G = Gamma0(20)310sage: G.is_subgroup(SL2Z)311True312sage: G.is_subgroup(Gamma0(4))313True314sage: G.is_subgroup(Gamma0(20))315True316sage: G.is_subgroup(Gamma0(7))317False318sage: G.is_subgroup(Gamma1(20))319False320sage: G.is_subgroup(GammaH(40, []))321False322sage: Gamma0(80).is_subgroup(GammaH(40, [31, 21, 17]))323True324sage: Gamma0(2).is_subgroup(Gamma1(2))325True326"""327if right.level() == 1:328return True329if is_Gamma0(right):330return self.level() % right.level() == 0331if is_Gamma1(right):332if right.level() >= 3:333return False334elif right.level() == 2:335return self.level() == 2336# case level 1 dealt with above337else:338return GammaH_class.is_subgroup(self, right)339340def coset_reps(self):341r"""342Return representatives for the right cosets of this congruence343subgroup in `{\rm SL}_2(\ZZ)` as a generator object.344345Use ``list(self.coset_reps())`` to obtain coset reps as a346list.347348EXAMPLES::349350sage: list(Gamma0(5).coset_reps())351[352[1 0] [ 0 -1] [1 0] [ 0 -1] [ 0 -1] [ 0 -1]353[0 1], [ 1 0], [1 1], [ 1 2], [ 1 3], [ 1 4]354]355sage: list(Gamma0(4).coset_reps())356[357[1 0] [ 0 -1] [1 0] [ 0 -1] [ 0 -1] [1 0]358[0 1], [ 1 0], [1 1], [ 1 2], [ 1 3], [2 1]359]360sage: list(Gamma0(1).coset_reps())361[362[1 0]363[0 1]364]365"""366from all import SL2Z367N = self.level()368if N == 1: # P1List isn't very happy working modulo 1369yield SL2Z([1,0,0,1])370else:371for z in sage.modular.modsym.p1list.P1List(N):372yield SL2Z(lift_to_sl2z(z[0], z[1], N))373374@cached_method375def generators(self, algorithm="farey"):376r"""377Return generators for this congruence subgroup.378379INPUT:380381- ``algorithm`` (string): either ``farey`` (default) or382``todd-coxeter``.383384If ``algorithm`` is set to ``"farey"``, then the generators will be385calculated using Farey symbols, which will always return a *minimal*386generating set. See :mod:`~sage.modular.arithgroup.farey_symbol` for387more information.388389If ``algorithm`` is set to ``"todd-coxeter"``, a simpler algorithm390based on Todd-Coxeter enumeration will be used. This tends to return391far larger sets of generators.392393EXAMPLE::394395sage: Gamma0(3).generators()396[397[1 1] [-1 1]398[0 1], [-3 2]399]400sage: Gamma0(3).generators(algorithm="todd-coxeter")401[402[1 1] [-1 0] [ 1 -1] [1 0] [1 1] [-1 0] [ 1 0]403[0 1], [ 0 -1], [ 0 1], [3 1], [0 1], [ 3 -1], [-3 1]404]405sage: SL2Z.gens()406(407[ 0 -1] [1 1]408[ 1 0], [0 1]409)410"""411if self.level() == 1:412# we return a fixed set of generators for SL2Z, for historical413# reasons, which aren't the ones the Farey symbol code gives414return [ self([0,-1,1,0]), self([1,1,0,1]) ]415416elif algorithm=="farey":417return self.farey_symbol().generators()418419elif algorithm=="todd-coxeter":420from sage.modular.modsym.p1list import P1List421from congroup_pyx import generators_helper422level = self.level()423if level == 1: # P1List isn't very happy working mod 1424return [ self([0,-1,1,0]), self([1,1,0,1]) ]425gen_list = generators_helper(P1List(level), level, Mat2Z)426return [self(g, check=False) for g in gen_list]427428else:429raise ValueError, "Unknown algorithm '%s' (should be either 'farey' or 'todd-coxeter')" % algorithm430431def gamma_h_subgroups(self):432r"""433Return the subgroups of the form `\Gamma_H(N)` contained434in self, where `N` is the level of self.435436EXAMPLES::437438sage: G = Gamma0(11)439sage: G.gamma_h_subgroups()440[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)]441sage: G = Gamma0(12)442sage: G.gamma_h_subgroups()443[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)]444"""445from all import GammaH446N = self.level()447R = IntegerModRing(N)448return [GammaH(N, H) for H in R.multiplicative_subgroups()]449450def __call__(self, x, check=True):451r"""452Create an element of this congruence subgroup from x.453454If the optional flag check is True (default), check whether455x actually gives an element of self.456457EXAMPLES::458459sage: G = Gamma0(12)460sage: G([1, 0, 24, 1])461[ 1 0]462[24 1]463sage: G(matrix(ZZ, 2, [1, 1, -12, -11]))464[ 1 1]465[-12 -11]466sage: G([1, 0, 23, 1])467Traceback (most recent call last):468...469TypeError: matrix must have lower left entry (=23) divisible by 12470"""471from all import SL2Z472x = SL2Z(x, check)473if not check:474return x475476c = x.c()477N = self.level()478if c%N == 0:479return x480else:481raise TypeError, "matrix must have lower left entry (=%s) divisible by %s" %(c, N)482483def _find_cusps(self):484r"""485Return an ordered list of inequivalent cusps for self, i.e. a486set of representatives for the orbits of self on487`\mathbb{P}^1(\QQ)`. These are returned in a reduced488form; see self.reduce_cusp for the definition of reduced.489490ALGORITHM:491Uses explicit formulae specific to `\Gamma_0(N)`: a reduced cusp on492`\Gamma_0(N)` is always of the form `a/d` where `d | N`, and `a_1/d493\sim a_2/d` if and only if `a_1 \cong a_2 \bmod {\rm gcd}(d,494N/d)`.495496EXAMPLES::497498sage: Gamma0(90)._find_cusps()499[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]500sage: Gamma0(1).cusps()501[Infinity]502sage: Gamma0(180).cusps() == Gamma0(180).cusps(algorithm='modsym')503True504"""505N = self.level()506s = []507508for d in arith.divisors(N):509w = arith.gcd(d, N//d)510if w == 1:511if d == 1:512s.append(Cusp(1,0))513elif d == N:514s.append(Cusp(0,1))515else:516s.append(Cusp(1,d))517else:518for a in xrange(1, w):519if arith.gcd(a, w) == 1:520while arith.gcd(a, d//w) != 1:521a += w522s.append(Cusp(a,d))523return sorted(s)524525def ncusps(self):526r"""527Return the number of cusps of this subgroup `\Gamma_0(N)`.528529EXAMPLES::530531sage: [Gamma0(n).ncusps() for n in [1..19]]532[1, 2, 2, 3, 2, 4, 2, 4, 4, 4, 2, 6, 2, 4, 4, 6, 2, 8, 2]533sage: [Gamma0(n).ncusps() for n in prime_range(2,100)]534[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]535"""536n = self.level()537return sum([arith.euler_phi(arith.gcd(d,n//d)) for d in n.divisors()])538539540def nu2(self):541r"""542Return the number of elliptic points of order 2 for this congruence543subgroup `\Gamma_0(N)`. The number of these is given by a standard formula:5440 if `N` is divisible by 4 or any prime congruent to -1 mod 4, and545otherwise `2^d` where d is the number of odd primes dividing `N`.546547EXAMPLE::548549sage: Gamma0(2).nu2()5501551sage: Gamma0(4).nu2()5520553sage: Gamma0(21).nu2()5540555sage: Gamma0(1105).nu2()5568557sage: [Gamma0(n).nu2() for n in [1..19]]558[1, 1, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 2, 0, 0]559"""560n = self.level()561if n%4 == 0:562return ZZ(0)563return prod([ 1 + kronecker_symbol(-4, p) for p, _ in n.factor()])564565def nu3(self):566r"""567Return the number of elliptic points of order 3 for this congruence568subgroup `\Gamma_0(N)`. The number of these is given by a standard formula:5690 if `N` is divisible by 9 or any prime congruent to -1 mod 3, and570otherwise `2^d` where d is the number of primes other than 3 dividing `N`.571572EXAMPLE::573574sage: Gamma0(2).nu3()5750576sage: Gamma0(3).nu3()5771578sage: Gamma0(9).nu3()5790580sage: Gamma0(7).nu3()5812582sage: Gamma0(21).nu3()5832584sage: Gamma0(1729).nu3()5858586"""587n = self.level()588if (n % 9 == 0):589return ZZ(0)590return prod([ 1 + kronecker_symbol(-3, p) for p, _ in n.factor()])591592def index(self):593r"""594Return the index of self in the full modular group. This is given by595596.. math::597598N \prod_{\substack{p \mid N \\ \text{$p$ prime}}}\left(1 + \frac{1}{p}\right).599600EXAMPLE::601sage: [Gamma0(n).index() for n in [1..19]]602[1, 3, 4, 6, 6, 12, 8, 12, 12, 18, 12, 24, 14, 24, 24, 24, 18, 36, 20]603sage: Gamma0(32041).index()60432220605"""606return prod([p**e + p**(e-1) for (p,e) in self.level().factor()])607608609610611612