Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# -*- coding: utf-8 -*- r""" Congruence Subgroup `\Gamma_H(N)`
AUTHORS:
- Jordi Quer - David Loeffler """ from __future__ import absolute_import
################################################################################ # # Copyright (C) 2009, The Sage Group -- http://www.sagemath.org/ # # Distributed under the terms of the GNU General Public License (GPL) # # The full text of the GPL is available at: # # http://www.gnu.org/licenses/ # ################################################################################ from six.moves import range
from sage.arith.all import euler_phi, lcm, gcd, divisors, get_inverse_mod, get_gcd, factor from sage.modular.modsym.p1list import lift_to_sl2z from .congroup_generic import CongruenceSubgroup from sage.modular.cusps import Cusp from sage.misc.cachefunc import cached_method from sage.rings.integer_ring import ZZ from sage.rings.finite_rings.integer_mod_ring import Zmod from sage.groups.matrix_gps.finitely_generated import MatrixGroup from sage.matrix.constructor import matrix from sage.structure.richcmp import richcmp_method, richcmp
_gammaH_cache = {} def GammaH_constructor(level, H): r""" Return the congruence subgroup `\Gamma_H(N)`, which is the subgroup of `SL_2(\ZZ)` consisting of matrices of the form `\begin{pmatrix} a & b \\ c & d \end{pmatrix}` with `N | c` and `a, b \in H`, for `H` a specified subgroup of `(\ZZ/N\ZZ)^\times`.
INPUT:
- level -- an integer - H -- either 0, 1, or a list * If H is a list, return `\Gamma_H(N)`, where `H` is the subgroup of `(\ZZ/N\ZZ)^*` **generated** by the elements of the list. * If H = 0, returns `\Gamma_0(N)`. * If H = 1, returns `\Gamma_1(N)`.
EXAMPLES::
sage: GammaH(11,0) # indirect doctest Congruence Subgroup Gamma0(11) sage: GammaH(11,1) Congruence Subgroup Gamma1(11) sage: GammaH(11,[10]) Congruence Subgroup Gamma_H(11) with H generated by [10] sage: GammaH(11,[10,1]) Congruence Subgroup Gamma_H(11) with H generated by [10] sage: GammaH(14,[10]) Traceback (most recent call last): ... ArithmeticError: The generators [10] must be units modulo 14 """
def is_GammaH(x): """ Return True if x is a congruence subgroup of type GammaH.
EXAMPLES::
sage: from sage.modular.arithgroup.all import is_GammaH sage: is_GammaH(GammaH(13, [2])) True sage: is_GammaH(Gamma0(6)) True sage: is_GammaH(Gamma1(6)) True sage: is_GammaH(sage.modular.arithgroup.congroup_generic.CongruenceSubgroup(5)) False """
def _normalize_H(H, level): """ Normalize representatives for a given subgroup H of the units modulo level.
.. NOTE::
This function does *not* make any attempt to find a minimal set of generators for H. It simply normalizes the inputs for use in hashing.
EXAMPLES::
sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([23], 10) [3] sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([1,5], 7) [3] sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([4,18], 14) Traceback (most recent call last): ... ArithmeticError: The generators [4, 4] must be units modulo 14 sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([3,17], 14) [3] sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([-1,7,9], 10) [3, 9]
TESTS::
sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([4, 16], 21) [4] """ else:
@richcmp_method class GammaH_class(CongruenceSubgroup): r""" The congruence subgroup `\Gamma_H(N)` for some subgroup `H \trianglelefteq (\ZZ / N\ZZ)^\times`, which is the subgroup of `{\rm SL}_2(\ZZ)` consisting of matrices of the form `\begin{pmatrix} a & b \\ c & d \end{pmatrix}` with `N \mid c` and `a, b \in H`.
TESTS:
We test calculation of various invariants of the group::
sage: GammaH(33,[2]).projective_index() 96 sage: GammaH(33,[2]).genus() 5 sage: GammaH(7,[2]).genus() 0 sage: GammaH(23, [1..22]).genus() 2 sage: Gamma0(23).genus() 2 sage: GammaH(23, [1]).genus() 12 sage: Gamma1(23).genus() 12
We calculate the dimensions of some modular forms spaces::
sage: GammaH(33,[2]).dimension_cusp_forms(2) 5 sage: GammaH(33,[2]).dimension_cusp_forms(3) 0 sage: GammaH(33,[2,5]).dimension_cusp_forms(2) 3 sage: GammaH(32079, [21676]).dimension_cusp_forms(20) 180266112
We can sometimes show that there are no weight 1 cusp forms::
sage: GammaH(20, [9]).dimension_cusp_forms(1) 0 """
def __init__(self, level, H, Hlist=None): r""" The congruence subgroup `\Gamma_H(N)`.
The subgroup `H` must be given as a list.
EXAMPLES::
sage: GammaH(117, [4]) Congruence Subgroup Gamma_H(117) with H generated by [4] sage: G = GammaH(16, [7]) sage: TestSuite(G).run() sage: G is loads(dumps(G)) True """
def restrict(self, M): r""" Return the subgroup of `\Gamma_0(M)`, for `M` a divisor of `N`, obtained by taking the image of this group under reduction modulo `N`.
EXAMPLES::
sage: G = GammaH(33,[2]) sage: G.restrict(11) Congruence Subgroup Gamma0(11) sage: G.restrict(1) Modular Group SL(2,Z) sage: G.restrict(15) Traceback (most recent call last): ... ValueError: M (=15) must be a divisor of the level (33) of self """
def extend(self, M): r""" Return the subgroup of `\Gamma_0(M)`, for `M` a multiple of `N`, obtained by taking the preimage of this group under the reduction map; in other words, the intersection of this group with `\Gamma_0(M)`.
EXAMPLES::
sage: G = GammaH(33, [2]) sage: G.extend(99) Congruence Subgroup Gamma_H(99) with H generated by [2, 17, 68] sage: G.extend(11) Traceback (most recent call last): ... ValueError: M (=11) must be a multiple of the level (33) of self """
def __reduce__(self): """ Used for pickling self.
EXAMPLES::
sage: GammaH(92,[45,47]).__reduce__() (<function GammaH_constructor at ...>, (92, [45, 47])) """
def divisor_subgroups(self): r""" Given this congruence subgroup `\Gamma_H(N)`, return all subgroups `\Gamma_G(M)` for `M` a divisor of `N` and such that `G` is equal to the image of `H` modulo `M`.
EXAMPLES::
sage: G = GammaH(33,[2]); G Congruence Subgroup Gamma_H(33) with H generated by [2] sage: G._list_of_elements_in_H() [1, 2, 4, 8, 16, 17, 25, 29, 31, 32] sage: G.divisor_subgroups() [Modular Group SL(2,Z), Congruence Subgroup Gamma0(3), Congruence Subgroup Gamma0(11), Congruence Subgroup Gamma_H(33) with H generated by [2]] """
def to_even_subgroup(self): r""" Return the smallest even subgroup of `SL(2, \ZZ)` containing self.
EXAMPLES::
sage: GammaH(11, [4]).to_even_subgroup() Congruence Subgroup Gamma0(11) sage: Gamma1(11).to_even_subgroup() Congruence Subgroup Gamma_H(11) with H generated by [10]
""" else:
def __richcmp__(self, other, op): """ Compare self to other.
The ordering on congruence subgroups of the form GammaH(N) for some H is first by level, then by the order of H, then lexicographically by H. In particular, this means that we have Gamma1(N) < GammaH(N) < Gamma0(N) for every nontrivial proper subgroup H.
EXAMPLES::
sage: G = GammaH(86, [9]) sage: G == G True sage: G != GammaH(86, [11]) True sage: Gamma1(11) < Gamma0(11) True sage: Gamma1(11) == GammaH(11, []) True sage: Gamma0(11) == GammaH(11, [2]) True sage: G = Gamma0(86) sage: G == G True sage: G != GammaH(86, [11]) True sage: Gamma1(17) < Gamma0(17) True sage: Gamma0(1) == SL2Z True sage: Gamma0(2) == Gamma1(2) True
sage: [x._list_of_elements_in_H() for x in sorted(Gamma0(24).gamma_h_subgroups())] [[1], [1, 5], [1, 7], [1, 11], [1, 13], [1, 17], [1, 19], [1, 23], [1, 5, 7, 11], [1, 5, 13, 17], [1, 5, 19, 23], [1, 7, 13, 19], [1, 7, 17, 23], [1, 11, 13, 23], [1, 11, 17, 19], [1, 5, 7, 11, 13, 17, 19, 23]] """ self._list_of_elements_in_H()), (other.level(), -other.index(), other._list_of_elements_in_H()), op) else:
def _generators_for_H(self): """ Return generators for the subgroup H of the units mod self.level() that defines self.
EXAMPLES::
sage: GammaH(17,[4])._generators_for_H() [4] sage: GammaH(12,[-1])._generators_for_H() [11] """
def _repr_(self): """ Return the string representation of self.
EXAMPLES::
sage: GammaH(123, [55])._repr_() 'Congruence Subgroup Gamma_H(123) with H generated by [55]' """
def _latex_(self): r""" Return the \LaTeX representation of self.
EXAMPLES::
sage: GammaH(5,[4])._latex_() '\\Gamma_H(5, [4])' """
def _list_of_elements_in_H(self): """ Returns a sorted list of Python ints that are representatives between 1 and N-1 of the elements of H.
WARNING: Do not change this returned list.
EXAMPLES::
sage: G = GammaH(11,[3]); G Congruence Subgroup Gamma_H(11) with H generated by [3] sage: G._list_of_elements_in_H() [1, 3, 4, 5, 9] """
def is_even(self): """ Return True precisely if this subgroup contains the matrix -1.
EXAMPLES::
sage: GammaH(10, [3]).is_even() True sage: GammaH(14, [1]).is_even() False """ return True
@cached_method def generators(self, algorithm="farey"): r""" Return generators for this congruence subgroup. The result is cached.
INPUT:
- ``algorithm`` (string): either ``farey`` (default) or ``todd-coxeter``.
If ``algorithm`` is set to ``"farey"``, then the generators will be calculated using Farey symbols, which will always return a *minimal* generating set. See :mod:`~sage.modular.arithgroup.farey_symbol` for more information.
If ``algorithm`` is set to ``"todd-coxeter"``, a simpler algorithm based on Todd-Coxeter enumeration will be used. This tends to return far larger sets of generators.
EXAMPLES::
sage: GammaH(7, [2]).generators() [ [1 1] [ 2 -1] [ 4 -3] [0 1], [ 7 -3], [ 7 -5] ] sage: GammaH(7, [2]).generators(algorithm="todd-coxeter") [ [1 1] [-90 29] [ 15 4] [-10 -3] [ 1 -1] [1 0] [1 1] [-3 -1] [0 1], [301 -97], [-49 -13], [ 7 2], [ 0 1], [7 1], [0 1], [ 7 2], <BLANKLINE> [-13 4] [-5 -1] [-5 -2] [-10 3] [ 1 0] [ 9 -1] [-20 7] [ 42 -13], [21 4], [28 11], [ 63 -19], [-7 1], [28 -3], [-63 22], <BLANKLINE> [1 0] [-3 -1] [ 15 -4] [ 2 -1] [ 22 -7] [-5 1] [ 8 -3] [7 1], [ 7 2], [ 49 -13], [ 7 -3], [ 63 -20], [14 -3], [-21 8], <BLANKLINE> [11 5] [-13 -4] [35 16], [-42 -13] ] """ else: raise ValueError("Unknown algorithm '%s' (should be either 'farey' or 'todd-coxeter')" % algorithm)
def _coset_reduction_data_first_coord(G): """ Compute data used for determining the canonical coset representative of an element of SL_2(Z) modulo G. This function specifically returns data needed for the first part of the reduction step (the first coordinate).
INPUT:
G -- a congruence subgroup Gamma_0(N), Gamma_1(N), or Gamma_H(N).
OUTPUT:
A list v such that
v[u] = (min(u*h: h in H), gcd(u,N) , an h such that h*u = min(u*h: h in H)).
EXAMPLES::
sage: G = Gamma0(12) sage: sage.modular.arithgroup.congroup_gammaH.GammaH_class._coset_reduction_data_first_coord(G) [(0, 12, 0), (1, 1, 1), (2, 2, 1), (3, 3, 1), (4, 4, 1), (1, 1, 5), (6, 6, 1), (1, 1, 7), (4, 4, 5), (3, 3, 7), (2, 2, 5), (1, 1, 11)] """
# Get some useful fast functions for inverse and gcd
# We will be filling this list in below.
# We can fill in 0 and all elements of H immediately
# Make a table of the reduction of H (mod N/d), one for each # divisor d. # We special-case N == d because in this case, # 1 % N_over_d is 0 # For each element of H, we look at its image mod # N_over_d. If we haven't yet seen it, add it on to # the end of z.
# Compute the rest of the tuples. The values left to process # are those where reduct_data has a 0. Note that several of # these values are processed on each loop below, so re-index # each time.
def _coset_reduction_data_second_coord(G): """ Compute data used for determining the canonical coset representative of an element of SL_2(Z) modulo G. This function specifically returns data needed for the second part of the reduction step (the second coordinate).
INPUT:
self
OUTPUT:
a dictionary v with keys the divisors of N such that v[d] is the subgroup {h in H : h = 1 (mod N/d)}.
EXAMPLES::
sage: G = GammaH(240,[7,239]) sage: G._coset_reduction_data_second_coord() {1: [1], 2: [1], 3: [1], 4: [1], 5: [1, 49], 6: [1], 8: [1], 10: [1, 49], 12: [1], 15: [1, 49], 16: [1], 20: [1, 49], 24: [1, 191], 30: [1, 49, 137, 233], 40: [1, 7, 49, 103], 48: [1, 191], 60: [1, 49, 137, 233], 80: [1, 7, 49, 103], 120: [1, 7, 49, 103, 137, 191, 233, 239], 240: [1, 7, 49, 103, 137, 191, 233, 239]} sage: G = GammaH(1200,[-1,7]); G Congruence Subgroup Gamma_H(1200) with H generated by [7, 1199] sage: K = G._coset_reduction_data_second_coord().keys() ; K.sort() sage: K == divisors(1200) True """
@cached_method def _coset_reduction_data(self): """ Compute data used for determining the canonical coset representative of an element of SL_2(Z) modulo G.
EXAMPLES::
sage: G = GammaH(13, [-1]); G Congruence Subgroup Gamma_H(13) with H generated by [12] sage: G._coset_reduction_data() ([(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]}) """ self._coset_reduction_data_second_coord())
def _reduce_coset(self, uu, vv): r""" Compute a canonical form for a given Manin symbol.
INPUT:
Two integers (uu,vv) that define an element of `(Z/NZ)^2`.
- uu -- an integer - vv -- an integer
OUTPUT:
pair of integers that are equivalent to (uu,vv).
.. NOTE::
We do *not* require that gcd(uu,vv,N) = 1. If the gcd is not 1, we return (0,0).
EXAMPLES:
An example at level 9::
sage: G = GammaH(9,[4]); G Congruence Subgroup Gamma_H(9) with H generated by [4] sage: a = [] sage: for i in range(G.level()): ....: for j in range(G.level()): ....: a.append(G._reduce_coset(i,j)) sage: v = list(set(a)) sage: v.sort() sage: v [(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)]
An example at level 100::
sage: G = GammaH(100,[3,7]); G Congruence Subgroup Gamma_H(100) with H generated by [3, 7] sage: a = [] sage: for i in range(G.level()): ....: for j in range(G.level()): ....: a.append(G._reduce_coset(i,j)) sage: v = list(set(a)) sage: v.sort() sage: len(v) 361
This demonstrates the problem underlying :trac:`1220`::
sage: G = GammaH(99, [67]) sage: G._reduce_coset(11,-3) (11, 96) sage: G._reduce_coset(77, -3) (11, 96) """
def reduce_cusp(self, c): r""" Compute a minimal representative for the given cusp c. Returns a cusp c' which is equivalent to the given cusp, and is in lowest terms with minimal positive denominator, and minimal positive numerator for that denominator.
Two cusps `u_1/v_1` and `u_2/v_2` are equivalent modulo `\Gamma_H(N)` if and only if
.. MATH::
v_1 = h v_2 \bmod N\quad \text{and}\quad u_1 = h^{-1} u_2 \bmod {\rm gcd}(v_1,N)
or
.. MATH::
v_1 = -h v_2 \bmod N\quad \text{and}\quad u_1 = -h^{-1} u_2 \bmod {\rm gcd}(v_1,N)
for some `h \in H`.
EXAMPLES::
sage: GammaH(6,[5]).reduce_cusp(5/3) 1/3 sage: GammaH(12,[5]).reduce_cusp(Cusp(8,9)) 1/3 sage: GammaH(12,[5]).reduce_cusp(5/12) Infinity sage: GammaH(12,[]).reduce_cusp(Cusp(5,12)) 5/12 sage: GammaH(21,[5]).reduce_cusp(Cusp(-9/14)) 1/7 sage: Gamma1(5).reduce_cusp(oo) Infinity sage: Gamma1(5).reduce_cusp(0) 0 """
def _reduce_cusp(self, c): r""" Compute a minimal representative for the given cusp c.
Returns a pair (c', t), where c' is the minimal representative for the given cusp, and t is either 1 or -1, as explained below. Largely for internal use.
The minimal representative for a cusp is the element in `P^1(Q)` in lowest terms with minimal positive denominator, and minimal positive numerator for that denominator.
Two cusps `u1/v1` and `u2/v2` are equivalent modulo `\Gamma_H(N)` if and only if
- `v1 = h*v2 (mod N)` and `u1 = h^(-1)*u2 (mod gcd(v1,N))`
or
- `v1 = -h*v2 (mod N)` and `u1 = -h^(-1)*u2 (mod gcd(v1,N))`
for some `h \in H`. Then t is 1 or -1 as c and c' fall into the first or second case, respectively.
EXAMPLES::
sage: GammaH(6,[5])._reduce_cusp(Cusp(5,3)) (1/3, -1) sage: GammaH(12,[5])._reduce_cusp(Cusp(8,9)) (1/3, -1) sage: GammaH(12,[5])._reduce_cusp(Cusp(5,12)) (Infinity, 1) sage: GammaH(12,[])._reduce_cusp(Cusp(5,12)) (5/12, 1) sage: GammaH(21,[5])._reduce_cusp(Cusp(-9/14)) (1/7, 1) """
# First, if N | v, take care of this case. If u is in \pm H, # then we return Infinity. If not, let u_0 be the minimum # of \{ h*u | h \in \pm H \}. Then return u_0/N. else:
# If (N,v) == 1, let v_0 be the minimal element # in \{ v * h | h \in \pm H \}. Then we either return # Infinity or 1/v_0, as v is or is not in \pm H, # respectively. else:
# Now we're in the case (N,v) > 1. So we have to do several # steps: first, compute v_0 as above. While computing this # minimum, keep track of *all* pairs of (h,s) which give this # value of v_0.
# Finally, we find our minimal numerator. Let u_1 be the # minimum of s*h^-1*u mod d as (h,s) ranges over the elements # of hs_ls. We must find the smallest integer u_0 which is # smaller than v_0, congruent to u_1 mod d, and coprime to # v_0. Then u_0/v_0 is our minimal representative.
def _find_cusps(self): r""" Return an ordered list of inequivalent cusps for self, i.e. a set of representatives for the orbits of self on `\mathbf{P}^1(\QQ)`. These are returned in a reduced form; see self.reduce_cusp for the definition of reduced.
ALGORITHM: Lemma 3.2 in Cremona's 1997 book shows that for the action of Gamma1(N) on "signed projective space" `\Q^2 / (\Q_{\geq 0}^+)`, we have `u_1/v_1 \sim u_2 / v_2` if and only if `v_1 = v_2 \bmod N` and `u_1 = u_2 \bmod gcd(v_1, N)`. It follows that every orbit has a representative `u/v` with `v \le N` and `0 \le u \le gcd(v, N)`. We iterate through all pairs `(u,v)` satisfying this.
Having found a set containing at least one of every equivalence class modulo Gamma1(N), we can be sure of picking up every class modulo GammaH(N) since this contains Gamma1(N); and the reduce_cusp call does the checking to make sure we don't get any duplicates.
EXAMPLES::
sage: Gamma1(5)._find_cusps() [0, 2/5, 1/2, Infinity] sage: Gamma1(35)._find_cusps() [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] sage: Gamma1(24)._find_cusps() == Gamma1(24).cusps(algorithm='modsym') True sage: GammaH(24, [13,17])._find_cusps() == GammaH(24,[13,17]).cusps(algorithm='modsym') True """
def _contains_sl2(self, a,b,c,d): r""" Test whether [a,b,c,d] is an element of this subgroup.
EXAMPLES::
sage: G = GammaH(10, [3]) sage: [1, 0, -10, 1] in G True sage: matrix(ZZ, 2, [7, 1, 20, 3]) in G True sage: SL2Z.0 in G False sage: GammaH(10, [9])([7, 1, 20, 3]) # indirect doctest Traceback (most recent call last): ... TypeError: matrix [ 7 1] [20 3] is not an element of Congruence Subgroup Gamma_H(10) with H generated by [9] """
def gamma0_coset_reps(self): r""" Return a set of coset representatives for self \\ Gamma0(N), where N is the level of self.
EXAMPLES::
sage: GammaH(108, [1,-1]).gamma0_coset_reps() [ [1 0] [-43 -45] [ 31 33] [-49 -54] [ 25 28] [-19 -22] [0 1], [108 113], [108 115], [108 119], [108 121], [108 125], <BLANKLINE> [-17 -20] [ 47 57] [ 13 16] [ 41 52] [ 7 9] [-37 -49] [108 127], [108 131], [108 133], [108 137], [108 139], [108 143], <BLANKLINE> [-35 -47] [ 29 40] [ -5 -7] [ 23 33] [-11 -16] [ 53 79] [108 145], [108 149], [108 151], [108 155], [108 157], [108 161] ] """
def coset_reps(self): r""" Return a set of coset representatives for self \\ SL2Z.
EXAMPLES::
sage: list(Gamma1(3).coset_reps()) [ [1 0] [-1 -2] [ 0 -1] [-2 1] [1 0] [-3 -2] [ 0 -1] [-2 -3] [0 1], [ 3 5], [ 1 0], [ 5 -3], [1 1], [ 8 5], [ 1 2], [ 5 7] ] sage: len(list(Gamma1(31).coset_reps())) == 31**2 - 1 True """
def is_subgroup(self, other): r""" Return True if self is a subgroup of right, and False otherwise.
EXAMPLES::
sage: GammaH(24,[7]).is_subgroup(SL2Z) True sage: GammaH(24,[7]).is_subgroup(Gamma0(8)) True sage: GammaH(24, []).is_subgroup(GammaH(24, [7])) True sage: GammaH(24, []).is_subgroup(Gamma1(24)) True sage: GammaH(24, [17]).is_subgroup(GammaH(24, [7])) False sage: GammaH(1371, [169]).is_subgroup(GammaH(457, [169])) True """
raise NotImplementedError
# level of self should divide level of other return False
# easy cases
return False
else: # difficult case
def index(self): r""" Return the index of self in SL2Z.
EXAMPLES::
sage: [G.index() for G in Gamma0(40).gamma_h_subgroups()] [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] """
def nu2(self): r""" Return the number of orbits of elliptic points of order 2 for this group.
EXAMPLES::
sage: [H.nu2() for n in [1..10] for H in Gamma0(n).gamma_h_subgroups()] [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] sage: GammaH(33,[2]).nu2() 0 sage: GammaH(5,[2]).nu2() 2
AUTHORS:
- Jordi Quer
"""
def nu3(self): r""" Return the number of orbits of elliptic points of order 3 for this group.
EXAMPLES::
sage: [H.nu3() for n in [1..10] for H in Gamma0(n).gamma_h_subgroups()] [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] sage: GammaH(33,[2]).nu3() 0 sage: GammaH(7,[2]).nu3() 2
AUTHORS:
- Jordi Quer
"""
def ncusps(self): r""" Return the number of orbits of cusps (regular or otherwise) for this subgroup.
EXAMPLES::
sage: GammaH(33,[2]).ncusps() 8 sage: GammaH(32079, [21676]).ncusps() 28800
AUTHORS:
- Jordi Quer
""" else:
def nregcusps(self): r""" Return the number of orbits of regular cusps for this subgroup. A cusp is regular if we may find a parabolic element generating the stabiliser of that cusp whose eigenvalues are both +1 rather than -1. If G contains -1, all cusps are regular.
EXAMPLES::
sage: GammaH(20, [17]).nregcusps() 4 sage: GammaH(20, [17]).nirregcusps() 2 sage: GammaH(3212, [2045, 2773]).nregcusps() 1440 sage: GammaH(3212, [2045, 2773]).nirregcusps() 720
AUTHOR:
- Jordi Quer """
else:
def nirregcusps(self): r""" Return the number of irregular cusps for this subgroup.
EXAMPLES::
sage: GammaH(3212, [2045, 2773]).nirregcusps() 720 """
def dimension_new_cusp_forms(self, k=2, p=0): r""" Return the dimension of the space of new (or `p`-new) weight `k` cusp forms for this congruence subgroup.
INPUT:
- ``k`` - an integer (default: 2), the weight. Not fully implemented for k = 1. - ``p`` - integer (default: 0); if nonzero, compute the `p`-new subspace.
OUTPUT: Integer
EXAMPLES::
sage: GammaH(33,[2]).dimension_new_cusp_forms() 3 sage: Gamma1(4*25).dimension_new_cusp_forms(2, p=5) 225 sage: Gamma1(33).dimension_new_cusp_forms(2) 19 sage: Gamma1(33).dimension_new_cusp_forms(2,p=11) 21
""" for H in self.divisor_subgroups()]) else: 2*self.restrict(N//p).dimension_new_cusp_forms(k)
def image_mod_n(self): r""" Return the image of this group in `SL(2, \ZZ / N\ZZ)`.
EXAMPLES::
sage: Gamma0(3).image_mod_n() Matrix group over Ring of integers modulo 3 with 2 generators ( [2 0] [1 1] [0 2], [0 1] )
TESTS::
sage: for n in [2..20]: ....: for g in Gamma0(n).gamma_h_subgroups(): ....: G = g.image_mod_n() ....: assert G.order() == Gamma(n).index() / g.index() """ raise NotImplementedError("Matrix groups over ring of integers modulo 1 not implemented")
def _list_subgroup(N, gens): r""" Given an integer ``N`` and a list of integers ``gens``, return a list of the elements of the subgroup of `(\ZZ / N\ZZ)^\times` generated by the elements of ``gens``.
EXAMPLES::
sage: sage.modular.arithgroup.congroup_gammaH._list_subgroup(11, [3]) [1, 3, 4, 5, 9] """ raise ValueError("gen (=%s) is not in (Z/%sZ)^*"%(g,N))
def _GammaH_coset_helper(N, H): r""" Return a list of coset representatives for H in (Z / NZ)^*.
EXAMPLES::
sage: from sage.modular.arithgroup.congroup_gammaH import _GammaH_coset_helper sage: _GammaH_coset_helper(108, [1, 107]) [1, 5, 7, 11, 13, 17, 19, 23, 25, 29, 31, 35, 37, 41, 43, 47, 49, 53] """
def mumu(N): """ Return 0 if any cube divides `N`. Otherwise return `(-2)^v` where `v` is the number of primes that exactly divide `N`.
This is similar to the Möbius function.
INPUT:
- ``N`` - an integer at least 1
OUTPUT: Integer
EXAMPLES::
sage: from sage.modular.arithgroup.congroup_gammaH import mumu sage: mumu(27) 0 sage: mumu(6*25) 4 sage: mumu(7*9*25) -2 sage: mumu(9*25) 1 """ raise ValueError("N must be at least 1") |