# Ticket #11601: trac_11601-congroups.2.patch

File trac_11601-congroups.2.patch, 46.0 KB (added by David Loeffler, 11 years ago)

New version rebased for 4.7.2.alpha3

• ## doc/en/bordeaux_2008/modular_forms_and_hecke_operators.rst

# HG changeset patch
# User David Loeffler <d.loeffler.01@cantab.net>
# Date 1310742384 -3600
# Node ID 75d46d326b626eb6c67a7275248e6fa4f985e879
# Parent  cbc455cd927e498f2430da721c8159d84af1bc52
#11601: general congruence subgroups of modular group

diff --git a/doc/en/bordeaux_2008/modular_forms_and_hecke_operators.rst b/doc/en/bordeaux_2008/modular_forms_and_hecke_operators.rst
 a Congruence Subgroup Gamma0(8) sage: Gamma1(13) Congruence Subgroup Gamma1(13) sage: GammaH(11,[2]) Congruence Subgroup Gamma_H(11) with H generated by [2] sage: GammaH(11,[4]) Congruence Subgroup Gamma_H(11) with H generated by [4] The second argument to the GammaH command is a list of generators of the subgroup :math:H of :math:(\ZZ/N\ZZ)^*.
• ## sage/modular/abvar/abvar_ambient_jacobian.py

diff --git a/sage/modular/abvar/abvar_ambient_jacobian.py b/sage/modular/abvar/abvar_ambient_jacobian.py
 a J_0(37) sage: J1(13)._latex_() 'J_1(13)' sage: latex(JH(389,[2])) J_H(389,[2]) sage: latex(JH(389,[16])) J_H(389,[16]) """ return self._ambient_latex_repr()
• ## sage/modular/abvar/constructor.py

diff --git a/sage/modular/abvar/constructor.py b/sage/modular/abvar/constructor.py
 a EXAMPLES:: sage: JH(389,[2]) Abelian variety JH(389,[2]) of dimension 32 sage: JH(389,[16]) Abelian variety JH(389,[16]) of dimension 64 """ key = 'JH(%s,%s)'%(N,H) try:
• ## sage/modular/all.py

diff --git a/sage/modular/all.py b/sage/modular/all.py
 a kronecker_character, kronecker_character_upside_down, trivial_character) from arithgroup.all import (Gamma0, Gamma1, GammaH, Gamma, SL2Z, ArithmeticSubgroup_Permutation, FareySymbol) from arithgroup.all import (Gamma0, Gamma1, GammaH, Gamma, SL2Z, ArithmeticSubgroup_Permutation, CongruenceSubgroup, FareySymbol) from cusps import Cusp, Cusps
• ## sage/modular/arithgroup/all.py

diff --git a/sage/modular/arithgroup/all.py b/sage/modular/arithgroup/all.py
 a # they are invisible to the user but easy to import all in one go by other code that needs them. from arithgroup_generic import is_ArithmeticSubgroup from congroup_generic import is_CongruenceSubgroup from congroup_generic import is_CongruenceSubgroup, CongruenceSubgroup_constructor as CongruenceSubgroup from congroup_gammaH import GammaH_constructor as GammaH, is_GammaH from congroup_gamma1 import Gamma1_constructor as Gamma1, is_Gamma1 from congroup_gamma0 import Gamma0_constructor as Gamma0, is_Gamma0
• ## sage/modular/arithgroup/arithgroup_generic.py

diff --git a/sage/modular/arithgroup/arithgroup_generic.py b/sage/modular/arithgroup/arithgroup_generic.py
 a """ return [-1, 0, 0, -1] in self def to_even_subgroup(self): r""" Return the smallest even subgroup of SL(2, \ZZ) containing self. EXAMPLE:: sage: sage.modular.arithgroup.arithgroup_generic.ArithmeticSubgroup().to_even_subgroup() Traceback (most recent call last): ... NotImplementedError """ if self.is_even(): return self else: raise NotImplementedError def order(self): r""" Return the number of elements in this arithmetic subgroup. def generalised_level(self): r""" Return the generalised level of self, i.e. the least common multiple of the widths of all cusps. Wohlfart's theorem tells us that this is equal to the (conventional) level of self when self is a congruence subgroup. the widths of all cusps. If self is *even*, Wohlfart's theorem tells us that this is equal to the (conventional) level of self when self is a congruence subgroup. This can fail if self is odd, but the actual level is at most twice the generalised level. See the paper by Kiming, Schuett and Verrill for more examples. EXAMPLE:: Traceback (most recent call last): ... NotImplementedError In the following example, the actual level is twice the generalised level. This is the group G_2 from Example 17 of K-S-V. :: sage: G = CongruenceSubgroup(8, [ [1,1,0,1], [3,-1,4,-1] ]) sage: G.level() 8 sage: G.generalised_level() 4 """ return arith.lcm([self.cusp_width(c) for c in self.cusps()])
• ## sage/modular/arithgroup/arithgroup_perm.py

diff --git a/sage/modular/arithgroup/arithgroup_perm.py b/sage/modular/arithgroup/arithgroup_perm.py
 a """ return arith.lcm(self.cusp_widths()) def congruence_closure(self): r""" Returns the smallest congruence subgroup containing self. If self is congruence, this is just self, but represented as a congruence subgroup data type. If self is not congruence, then it may be larger. In practice, we use the following criterion: let m be the generalised level of self. If this subgroup is even, let n = m, else let n = 2m. Then any congruence subgroup containing self contains \Gamma(n) (a generalisation of Wohlfahrt's theorem due to Kiming, Verrill and Schuett). So we compute the image of self modulo n and return the preimage of that. EXAMPLE:: sage: Gamma1(3).as_permutation_group().congruence_closure() Congruence subgroup of SL(2,Z) of level 3, preimage of: Matrix group over Ring of integers modulo 3 with 2 generators: [[[1, 2], [0, 1]], [[1, 1], [0, 1]]] sage: sage.modular.arithgroup.arithgroup_perm.HsuExample10().congruence_closure() # long time (40 sec) Modular Group SL(2,Z) """ if self.is_even(): N = self.generalised_level() else: N = 2*self.generalised_level() from congroup_generic import CongruenceSubgroup_constructor as CS return CS(N, [x.matrix() for x in self.gens()]) class OddArithmeticSubgroup_Permutation(ArithmeticSubgroup_Permutation_class): def __init__(self, S2, S3, L, R, canonical_labels=False): r"""
• ## sage/modular/arithgroup/congroup_gamma.py

diff --git a/sage/modular/arithgroup/congroup_gamma.py b/sage/modular/arithgroup/congroup_gamma.py
 a from congroup_generic import CongruenceSubgroup from arithgroup_element import ArithmeticSubgroupElement from sage.misc.misc import prod from sage.rings.all import ZZ from sage.rings.all import ZZ, Zmod from sage.groups.matrix_gps.matrix_group import MatrixGroup from sage.matrix.constructor import matrix _gamma_cache = {} def Gamma_constructor(N): return ZZ(3) return prod([p**(2*e) - p**(2*e-2) for (p,e) in n.factor()])//2 def nu3(self): r""" Return the number of elliptic points of order 3 for this arithmetic subgroup. Since this subgroup is \Gamma(N) for N \ge 2, there are no such points, so we return 0. EXAMPLE:: sage: Gamma(89).nu3() 0 """ return 0 # We don't need to override nu2, since the default nu2 implementation knows # that nu2 = 0 for odd subgroups. def image_mod_n(self): r""" Return the image of this group modulo N, as a subgroup of SL(2, \ZZ / N\ZZ). This is just the trivial subgroup. EXAMPLE:: sage: Gamma(3).image_mod_n() Matrix group over Ring of integers modulo 3 with 1 generators: [[[1, 0], [0, 1]]] """ return MatrixGroup([matrix(Zmod(self.level()), 2, 2, 1)]) def is_Gamma(x): r"""
• ## sage/modular/arithgroup/congroup_gamma0.py

diff --git a/sage/modular/arithgroup/congroup_gamma0.py b/sage/modular/arithgroup/congroup_gamma0.py
 a """ if self.level() in [1, 2]: return [] return [int(x) for x in IntegerModRing(self.level()).unit_gens()] return [ZZ(x) for x in IntegerModRing(self.level()).unit_gens()] @cached_method def _list_of_elements_in_H(self): sage: G = Gamma0(11) sage: G.gamma_h_subgroups() [Congruence Subgroup Gamma_H(11) with H generated by [2], Congruence Subgroup Gamma_H(11) with H generated by [4], Congruence Subgroup Gamma_H(11) with H generated by [10], Congruence Subgroup Gamma_H(11) with H generated by []] [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)] sage: G = Gamma0(12) sage: G.gamma_h_subgroups() [Congruence Subgroup Gamma_H(12) with H generated by [5, 7], 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 Gamma_H(12) with H generated by []] [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)] """ from all import GammaH N = self.level()
• ## sage/modular/arithgroup/congroup_gamma1.py

diff --git a/sage/modular/arithgroup/congroup_gamma1.py b/sage/modular/arithgroup/congroup_gamma1.py
 a from sage.misc.misc import prod from congroup_generic import is_CongruenceSubgroup from congroup_gammaH import GammaH_class, is_GammaH, GammaH_constructor #from congroup_gamma0 import Gamma0_constructor -- circular! from arithgroup_element import ArithmeticSubgroupElement from sage.rings.all import ZZ, euler_phi as phi, moebius, divisors from sage.modular.dirichlet import DirichletGroup True sage: is_Gamma1(Gamma0(6)) False sage: is_Gamma1(GammaH(12, [])) sage: is_Gamma1(GammaH(12, [])) # trick question! True sage: is_Gamma1(GammaH(12, [5])) False """ #from congroup_sl2z import is_SL2Z sage: G is loads(dumps(G)) True """ if N == 1 or N == 2: from congroup_gamma0 import Gamma0_constructor return Gamma0_constructor(N) try: return _gamma1_cache[N] except KeyError:
• ## sage/modular/arithgroup/congroup_gammaH.py

diff --git a/sage/modular/arithgroup/congroup_gammaH.py b/sage/modular/arithgroup/congroup_gammaH.py
 a AUTHORS: - Jordi Quer - David Loeffler """ # Just for now until we make an SL_2 group type. from sage.rings.integer_ring import ZZ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.rings.finite_rings.integer_mod_ring import Zmod from sage.matrix.matrix_space import MatrixSpace from sage.groups.matrix_gps.matrix_group import MatrixGroup from sage.matrix.constructor import matrix Mat2Z = MatrixSpace(ZZ,2) Congruence Subgroup Gamma0(11) sage: GammaH(11,1) Congruence Subgroup Gamma1(11) sage: GammaH(11,[2]) Congruence Subgroup Gamma_H(11) with H generated by [2] sage: GammaH(11,[2,1]) Congruence Subgroup Gamma_H(11) with H generated by [2] 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): ... return Gamma1(level) H = _normalize_H(H, level) if H == []: return Gamma1(level) Hlist = _list_subgroup(level, H) if len(Hlist) == euler_phi(level): return Gamma0(level) key = (level, tuple(H)) try: return _gammaH_cache[key] except KeyError: _gammaH_cache[key] = GammaH_class(level, H) _gammaH_cache[key] = GammaH_class(level, H, Hlist) return _gammaH_cache[key] def is_GammaH(x): 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 __init__(self, level, H): def __init__(self, level, H, Hlist=None): r""" The congruence subgroup \Gamma_H(N). The subgroup H must be input as a list. True """ CongruenceSubgroup.__init__(self, level) self.__H = _normalize_H(H, level) self.__H = H if Hlist is None: Hlist = _list_subgroup(level, H) self.__Hlist = Hlist def restrict(self, M): r""" sage: G = GammaH(33,[2]) sage: G.restrict(11) Congruence Subgroup Gamma_H(11) with H generated by [2] Congruence Subgroup Gamma0(11) sage: G.restrict(1) Modular Group SL(2,Z) sage: G.restrict(15) EXAMPLES:: sage: GammaH(92,[5,11]).__reduce__() (, (92, [5, 11])) sage: GammaH(92,[45,47]).__reduce__() (, (92, [45, 47])) """ return GammaH_constructor, (self.level(), self.__H) [1, 2, 4, 8, 16, 17, 25, 29, 31, 32] sage: G.divisor_subgroups() [Modular Group SL(2,Z), Congruence Subgroup Gamma_H(3) with H generated by [2], Congruence Subgroup Gamma_H(11) with H generated by [2], Congruence Subgroup Gamma0(3), Congruence Subgroup Gamma0(11), Congruence Subgroup Gamma_H(33) with H generated by [2]] """ v = self.__H ans.append(GammaH_constructor(M, w)) return ans def to_even_subgroup(self): r""" Return the smallest even subgroup of SL(2, \ZZ) containing self. EXAMPLE:: 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] """ if self.is_even(): return self else: return GammaH_constructor(self.level(), self._generators_for_H() + [-1]) def __cmp__(self, other): """ Compare self to other. sage: Gamma0(11) == GammaH(11, [2]) True """ if not is_CongruenceSubgroup(other): return cmp(type(self), type(other)) c = cmp(self.level(), other.level()) if c: return c if is_GammaH(other): return cmp(self._list_of_elements_in_H(), other._list_of_elements_in_H()) return cmp(type(self), type(other)) t = cmp(self.level(), other.level()) if t: return t else: return cmp(self._list_of_elements_in_H(), other._list_of_elements_in_H()) else: return CongruenceSubgroup.__cmp__(self, other) def _generators_for_H(self): """ EXAMPLES:: sage: GammaH(3,[2])._latex_() '\\Gamma_H(3)' sage: GammaH(5,[4])._latex_() '\\Gamma_H(5)' """ return "\\Gamma_H(%s)"%self.level() @cached_method def _list_of_elements_in_H(self): """ Returns a sorted list of Python ints that are representatives sage: G._list_of_elements_in_H() [1, 3, 4, 5, 9] """ N = self.level() if N == 1: return [1] gens = self.__H H = set([1]) N = int(N) for g in gens: if gcd(g, N) != 1: raise ValueError, "gen (=%s) is not in (Z/%sZ)^*"%(g,N) gk = int(g) % N sbgrp = [gk] while not (gk in H): gk = (gk * g)%N sbgrp.append(gk) H = set([(x*h)%N for x in sbgrp for h in H]) H = list(H) H.sort() return H return self.__Hlist def is_even(self): """ EXAMPLES:: sage: G = GammaH(12,[-1,5]); G Congruence Subgroup Gamma_H(12) with H generated by [5, 11] sage: G._coset_reduction_data_first_coord() 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)] """ EXAMPLES:: sage: G = GammaH(12,[-1,7]); G Congruence Subgroup Gamma_H(12) with H generated by [7, 11] sage: G = GammaH(13, [-1]); G Congruence Subgroup Gamma_H(13) with H generated by [12] sage: G._coset_reduction_data() ([(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)], {1: [1], 2: [1, 7], 3: [1, 5],  4: [1, 7], 6: [1, 5, 7, 11], 12: [1, 5, 7, 11]}) ([(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]}) """ return (self._coset_reduction_data_first_coord(), self._coset_reduction_data_second_coord()) else: return self.dimension_cusp_forms(k) - \ 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). EXAMPLE:: sage: Gamma0(3).image_mod_n() Matrix group over Ring of integers modulo 3 with 2 generators: [[[2, 0], [0, 2]], [[1, 1], [0, 1]]] TEST:: 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() """ N = self.level() if N == 1: raise NotImplementedError, "Matrix groups over ring of integers modulo 1 not implemented" gens = [matrix(Zmod(N), 2, 2, [x, 0, 0, Zmod(N)(1)/x]) for x in self._generators_for_H()] gens += [matrix(Zmod(N),2,[1,1,0,1])] return MatrixGroup(gens) 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. EXAMPLE:: sage: sage.modular.arithgroup.congroup_gammaH._list_subgroup(11, [3]) [1, 3, 4, 5, 9] """ H = set([1]) N = int(N) for g in gens: if gcd(g, N) != 1: raise ValueError, "gen (=%s) is not in (Z/%sZ)^*"%(g,N) gk = int(g) % N sbgrp = [gk] while not (gk in H): gk = (gk * g)%N sbgrp.append(gk) H = set([(x*h)%N for x in sbgrp for h in H]) H = list(H) H.sort() return H def _GammaH_coset_helper(N, H): r""" 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] """ t = [IntegerModRing(N)(1)] W = [IntegerModRing(N)(h) for h in H] HH = [IntegerModRing(N)(h) for h in H] t = [Zmod(N)(1)] W = [Zmod(N)(h) for h in H] HH = [Zmod(N)(h) for h in H] k = euler_phi(N) for i in xrange(1, N):
• ## sage/modular/arithgroup/congroup_generic.py

diff --git a/sage/modular/arithgroup/congroup_generic.py b/sage/modular/arithgroup/congroup_generic.py
 a # ################################################################################ from sage.rings.all import QQ, ZZ from sage.rings.all import QQ, ZZ, Zmod from sage.rings.arith import gcd #import sage.modular.cusps # circular! from sage.sets.set import Set from sage.groups.matrix_gps.matrix_group import MatrixGroup, MatrixGroup_generic from sage.matrix.matrix_space import MatrixSpace from sage.misc.misc_c import prod from arithgroup_element import ArithmeticSubgroupElement from arithgroup_generic import ArithmeticSubgroup from arithgroup_generic import ArithmeticSubgroup, is_ArithmeticSubgroup def CongruenceSubgroup_constructor(*args): r""" Attempt to create a congruence subgroup from the given data. The allowed inputs are as follows: - A :class:~sage.groups.matrix_gps.matrix_group.MatrixGroup object. This must be a group of matrices over \ZZ / N\ZZ for some N, with determinant 1, in which case the function will return the group of matrices in SL(2, \ZZ) whose reduction mod N is in the given group. - A list of matrices over \ZZ / N\ZZ for some N. The function will then compute the subgroup of SL(2, \ZZ) generated by these matrices, and proceed as above. - An integer N and a list of matrices (over any ring coercible to \ZZ / N\ZZ, e.g. over \ZZ). The matrices will then be coerced to \ZZ / N\ZZ. The function checks that the input G is valid. It then tests to see if G is the preimage mod N of some group of matrices modulo a proper divisor M of N, in which case it replaces G with this group before continuing. EXAMPLES:: sage: from sage.modular.arithgroup.congroup_generic import CongruenceSubgroup_constructor as CS sage: CS(2, [[1,1,0,1]]) Congruence subgroup of SL(2,Z) of level 2, preimage of: Matrix group over Ring of integers modulo 2 with 1 generators: [[[1, 1], [0, 1]]] sage: CS([matrix(Zmod(2), 2, [1,1,0,1])]) Congruence subgroup of SL(2,Z) of level 2, preimage of: Matrix group over Ring of integers modulo 2 with 1 generators: [[[1, 1], [0, 1]]] sage: CS(MatrixGroup([matrix(Zmod(2), 2, [1,1,0,1])])) Congruence subgroup of SL(2,Z) of level 2, preimage of: Matrix group over Ring of integers modulo 2 with 1 generators: [[[1, 1], [0, 1]]] sage: CS(SL(2, 2)) Modular Group SL(2,Z) Some invalid inputs:: sage: CS(SU(2, 7)) Traceback (most recent call last): ... TypeError: Ring of definition must be Z / NZ for some N """ if isinstance(args[0], MatrixGroup_generic): G = args[0] elif type(args[0]) == type([]): G = MatrixGroup(args[0]) elif args[0] in ZZ: M = MatrixSpace(Zmod(args[0]), 2) G = MatrixGroup([M(x) for x in args[1]]) R = G.matrix_space().base_ring() if not hasattr(R, "cover_ring") or R.cover_ring() != ZZ: raise TypeError, "Ring of definition must be Z / NZ for some N" if not all([x.matrix().det() == 1 for x in G.gens()]): raise ValueError, "Group must be contained in SL(2, Z / N)" GG = _minimize_level(G) if GG in ZZ: from all import Gamma return Gamma(GG) else: return CongruenceSubgroupFromGroup(GG) def is_CongruenceSubgroup(x): """ Return True if x is of type CongruenceSubgroup. Note that this may be False even if x really is a congruence subgroup -- it tests whether x is "obviously" congruence, i.e.~whether it has a congruence subgroup datatype. To test whether or not an arithmetic subgroup of SL(2, \ZZ) is congruence, use the is_congruence() method instead. EXAMPLES:: sage: from sage.modular.arithgroup.congroup_generic import is_CongruenceSubgroup True sage: is_CongruenceSubgroup(GammaH(11, [3])) True sage: G = ArithmeticSubgroup_Permutation(L = "(1, 2)", R = "(1, 2)"); is_CongruenceSubgroup(G) False sage: G.is_congruence() True sage: is_CongruenceSubgroup(SymmetricGroup(3)) False """ return isinstance(x, CongruenceSubgroup) return isinstance(x, CongruenceSubgroupBase) class CongruenceSubgroup(ArithmeticSubgroup): class CongruenceSubgroupBase(ArithmeticSubgroup): def __init__(self, level): """ Create a congruence subgroup with given level. raise ArithmeticError, "Congruence groups only defined for positive levels." self.__level = level ArithmeticSubgroup.__init__(self) def _repr_(self): """ Return the string representation of self. NOTE: This function should be overridden by all subclasses. EXAMPLES:: sage: sage.modular.arithgroup.congroup_generic.CongruenceSubgroup(5)._repr_() 'Generic congruence subgroup' """ return "Generic congruence subgroup" def is_congruence(self): r""" Return True, since this is a congruence subgroup. return True def level(self): """ Return the level of this congruence subgroup. EXAMPLES:: sage: SL2Z.level() 1 sage: Gamma0(20).level() 20 sage: Gamma1(11).level() 11 sage: GammaH(14, [5]).level() 14 """ return self.__level def __cmp__(self, other): r""" EXAMPLE:: sage: CongruenceSubgroup(3,[ [1,1,0,1] ]) == Gamma1(3) True sage: CongruenceSubgroup(3,[ [1,1,0,1] ]) == Gamma(3) False sage: CongruenceSubgroup(3,[ [1,1,0,1] ]) == QQ False """ if is_CongruenceSubgroup(other): t = cmp(self.level(), other.level()) if t: return t if self.level() == 1: return 0 # shouldn't come up except with pickling/unpickling t = cmp(self.index(), other.index()) if t: return t return cmp(self.image_mod_n(),other.image_mod_n()) elif is_ArithmeticSubgroup(other): return cmp(self.as_permutation_group(), other) else: return cmp(type(self), type(other)) class CongruenceSubgroupFromGroup(CongruenceSubgroupBase): r""" A congruence subgroup, defined by the data of an integer N and a subgroup G of the finite group SL(2, \ZZ / N\ZZ); the congruence subgroup consists of all the matrices in SL(2, \ZZ) whose reduction modulo N lies in G. This class should not be instantiated directly, but created using the factory function :func:~sage.modular.arithgroup.congroup_generic.CongruenceSubgroup_constructor, which accepts much more flexible input, and checks the input to make sure it is valid. TESTS:: sage: G = CongruenceSubgroup(5, [[0,-1,1,0]]); G Congruence subgroup of SL(2,Z) of level 5, preimage of: Matrix group over Ring of integers modulo 5 with 1 generators: [[[0, 4], [1, 0]]] sage: G == loads(dumps(G)) True """ def __init__(self, G): r""" Standard init function. TESTS:: sage: from sage.modular.arithgroup.congroup_generic import CongruenceSubgroupFromGroup sage: G = MatrixGroup([matrix(Zmod(2), 2, [1,1,1,0])]) sage: CongruenceSubgroupFromGroup(G).index() # indirect doctest 2 """ N = G.base_ring().characteristic() self.__G = G CongruenceSubgroupBase.__init__(self, N) def __reduce__(self): r""" Data defining self (for pickling). EXAMPLE:: sage: G = CongruenceSubgroup(5, [[0,-1,1,0]]) sage: G.__reduce__() (, (Matrix group over Ring of integers modulo 5 with 1 generators: [[[0, 4], [1, 0]]],)) """ return CongruenceSubgroup_constructor, (self.image_mod_n(),) def __call__(self, x, check=True): r""" Attempt to convert x into an element of self. This converts x into an element of SL(2, \ZZ). If check is True (the default) it checks if the resulting element is in self, and otherwise raises an error. EXAMPLE:: sage: G = MatrixGroup([matrix(Zmod(2), 2, [1,1,1,0])]) sage: H = sage.modular.arithgroup.congroup_generic.CongruenceSubgroupFromGroup(G) sage: H(1) [1 0] [0 1] sage: H([0,-1,1,0]) Traceback (most recent call last): ... TypeError: Element [ 0 -1] [ 1 0] not in group sage: H([1,2,0,1]) [1 2] [0 1] sage: H(SL2Z([0,-1,1,0]), check=False) [ 0 -1] [ 1  0] sage: H([1,2,0,1]).parent() Modular Group SL(2,Z) """ from sage.modular.arithgroup.all import SL2Z x = SL2Z(x,check=check) if not check: return x else: y = x.matrix().change_ring(Zmod(self.level())) if y in self.image_mod_n(): return x else: raise TypeError, "Element %s not in group" % x def to_even_subgroup(self): r""" Return the smallest even subgroup of SL(2, \ZZ) containing self. EXAMPLE:: sage: G = Gamma(3) sage: G.to_even_subgroup() Congruence subgroup of SL(2,Z) of level 3, preimage of: Matrix group over Ring of integers modulo 3 with 1 generators: [[[2, 0], [0, 2]]] """ if self.is_even(): return self else: G = self.image_mod_n() H = MatrixGroup(G.gens() + [G.matrix_space()(-1)]) return CongruenceSubgroup_constructor(H) def _repr_(self): r""" String representation of self. EXAMPLE:: sage: sage.modular.arithgroup.congroup_generic.CongruenceSubgroupFromGroup(MatrixGroup([matrix(Zmod(2), 2, [1,1,1,0])]))._repr_() 'Congruence subgroup of SL(2,Z) of level 2, preimage of:\n Matrix group over Ring of integers modulo 2 with 1 generators: \n [[[1, 1], [1, 0]]]' """ return "Congruence subgroup of SL(2,Z) of level %s, preimage of:\n %s" % (self.level(), self.image_mod_n()) def index(self): r""" Return the index of self in the full modular group. This is equal to the index in SL(2, \ZZ / N\ZZ) of the image of this group modulo \Gamma(N). EXAMPLE:: sage: sage.modular.arithgroup.congroup_generic.CongruenceSubgroupFromGroup(MatrixGroup([matrix(Zmod(2), 2, [1,1,1,0])])).index() 2 """ return prod([p**(3*e-2)*(p*p-1) for (p,e) in self.level().factor()]) // self.image_mod_n().order() def image_mod_n(self): r""" Return the subgroup of SL(2, \ZZ / N\ZZ) of which this is the preimage, where N is the level of self. EXAMPLE:: sage: G = MatrixGroup([matrix(Zmod(2), 2, [1,1,1,0])]) sage: H = sage.modular.arithgroup.congroup_generic.CongruenceSubgroupFromGroup(G); H.image_mod_n() Matrix group over Ring of integers modulo 2 with 1 generators: [[[1, 1], [1, 0]]] sage: H.image_mod_n() == G True """ return self.__G class CongruenceSubgroup(CongruenceSubgroupFromGroup): r""" One of the "standard" congruence subgroups \Gamma_0(N), \Gamma_1(N), \Gamma(N), or \Gamma_H(N) (for some H). This class is not intended to be instantiated directly. Derived subclasses must override __call__, _repr_, and image_mod_n. """ def image_mod_n(self): r""" Raise an error: all derived subclasses should override this function. EXAMPLE: sage: sage.modular.arithgroup.congroup_generic.CongruenceSubgroup(5).image_mod_n() Traceback (most recent call last): ... NotImplementedError """ raise NotImplementedError def __init__(self,*args, **kwds): r""" Bypass the init function of the CongruenceSubgroupFromGroup class. EXAMPLE:: sage: sage.modular.arithgroup.congroup_generic.CongruenceSubgroup(5) # indirect doctest Generic congruence subgroup of level 5 """ return CongruenceSubgroupBase.__init__(self, *args, **kwds) def _repr_(self): """ Return the string representation of self. NOTE: This function should be overridden by all subclasses. EXAMPLES:: sage: sage.modular.arithgroup.congroup_generic.CongruenceSubgroup(5)._repr_() 'Generic congruence subgroup of level 5' """ return "Generic congruence subgroup of level %s" % self.level() def modular_symbols(self, sign=0, weight=2, base_ring=QQ): """ Return the space of modular symbols of the specified weight and sign from sage.modular.abvar.abvar_ambient_jacobian import ModAbVar_ambient_jacobian return ModAbVar_ambient_jacobian(self) def level(self): """ Return the level of this congruence subgroup. EXAMPLES:: sage: SL2Z.level() 1 sage: Gamma0(20).level() 20 sage: Gamma1(11).level() 11 sage: GammaH(14, [5]).level() 14 """ return self.__level def _new_group_from_level(self, level): r""" Return a new group of the same type (Gamma0, Gamma1, or GammaH) as self of the given level. In the case that self is of type GammaH, we take the largest H inside $(Z/\text{level}Z)^\times$ which maps to H, namely its inverse image under the natural reduction map. GammaH) as self of the given level. In the case that self is of type GammaH, we take the largest H inside (\ZZ/ \text{level}\ZZ)^\times which maps to H, namely its inverse image under the natural reduction map. EXAMPLES:: ... ValueError: one level must divide the other sage: G = GammaH(50,[3,37]) sage: G Congruence Subgroup Gamma_H(50) with H generated by [3, 37] sage: G = GammaH(50,[7]); G Congruence Subgroup Gamma_H(50) with H generated by [7] sage: G._new_group_from_level(25) Congruence Subgroup Gamma_H(25) with H generated by [3, 12] Congruence Subgroup Gamma_H(25) with H generated by [7] sage: G._new_group_from_level(10) Congruence Subgroup Gamma0(10) sage: G._new_group_from_level(100) Congruence Subgroup Gamma_H(100) with H generated by [3, 37, 53, 87] sage: G._new_group_from_level(150) Congruence Subgroup Gamma_H(150) with H generated by [37, 53, 103, 137] Congruence Subgroup Gamma_H(100) with H generated by [7, 57] """ from congroup_gamma0 import is_Gamma0 from congroup_gamma1 import is_Gamma1 else: raise NotImplementedError def _minimize_level(G): r""" Utility function. Given a matrix group G contained in SL(2, \ZZ / N\ZZ) for some N, test whether or not G is the preimage of a subgroup of smaller level, and if so, return that subgroup. The trivial group is handled specially: instead of returning a group, it returns an integer N, representing the trivial subgroup of SL(2, \ZZ / N\ZZ). EXAMPLE:: sage: M = MatrixSpace(Zmod(9), 2, 2) sage: G = MatrixGroup([M(x) for x in [[1,1,0,1],[1,3,0,1],[1,0,3,1],[4,0,0,7]]]); G Matrix group over Ring of integers modulo 9 with 4 generators: [[[1, 1], [0, 1]], [[1, 3], [0, 1]], [[1, 0], [3, 1]], [[4, 0], [0, 7]]] sage: sage.modular.arithgroup.congroup_generic._minimize_level(G) Matrix group over Ring of integers modulo 3 with 1 generators: [[[1, 1], [0, 1]]] sage: G = MatrixGroup([M(x) for x in [[1,3,0,1],[1,0,3,1],[4,0,0,7]]]); G Matrix group over Ring of integers modulo 9 with 3 generators: [[[1, 3], [0, 1]], [[1, 0], [3, 1]], [[4, 0], [0, 7]]] sage: sage.modular.arithgroup.congroup_generic._minimize_level(G) 3 """ from congroup_gamma import Gamma_constructor as Gamma Glist = list(G) N = G.base_ring().characteristic() i = Gamma(N).index() for p in N.prime_divisors(): j = Gamma(N // p).index() k = len([g for g in Glist if g.matrix().change_ring(Zmod(N // p)) == 1]) if k == i // j: if N // p == 1: return ZZ(1) H = MatrixGroup([g.matrix().change_ring(Zmod(N//p)) for g in G.gens()]) return _minimize_level(H) # now sanitize the generators (remove duplicates and copies of the identity) new_gens = [x.matrix() for x in G.gens() if x.matrix() != 1] all([x.set_immutable() for x in new_gens]) new_gens = list(Set(new_gens)) if new_gens == []: return ZZ(G.base_ring().characteristic()) return MatrixGroup(new_gens)
• ## sage/modular/arithgroup/congroup_sl2z.py

diff --git a/sage/modular/arithgroup/congroup_sl2z.py b/sage/modular/arithgroup/congroup_sl2z.py
 a r""" Create an element of self from x. If check=True (the default), check that x really defines a 2x2 integer matrix of det 1. EXAMPLE:: sage: SL2Z([1,0,0,1]) [1 0] [0 1] sage: SL2Z([1, QQ, False], check=False) # don't do this! [1, Rational Field, False] """ return ArithmeticSubgroupElement(self, x, check=check)
• ## sage/modular/modform/ambient.py

diff --git a/sage/modular/modform/ambient.py b/sage/modular/modform/ambient.py
 a :: sage: m = ModularForms(GammaH(11,[2]), 2); m Modular Forms space of dimension 2 for Congruence Subgroup Gamma_H(11) with H generated by [2] of weight 2 over Rational Field sage: m = ModularForms(GammaH(11,[4]), 2); m Modular Forms space of dimension 2 for Congruence Subgroup Gamma_H(11) with H generated by [4] of weight 2 over Rational Field sage: type(m) sage: m == loads(dumps(m)) EXAMPLES:: sage: m = ModularForms(GammaH(11,[2]), 2); m Modular Forms space of dimension 2 for Congruence Subgroup Gamma_H(11) with H generated by [2] of weight 2 over Rational Field sage: m = ModularForms(GammaH(11,[4]), 2); m Modular Forms space of dimension 2 for Congruence Subgroup Gamma_H(11) with H generated by [4] of weight 2 over Rational Field sage: m._dim_cuspidal() 1 """ EXAMPLES:: sage: m = ModularForms(GammaH(13,[2]), 2); m Modular Forms space of dimension 1 for Congruence Subgroup Gamma_H(13) with H generated by [2] of weight 2 over Rational Field sage: m = ModularForms(GammaH(13,[4]), 2); m Modular Forms space of dimension 3 for Congruence Subgroup Gamma_H(13) with H generated by [4] of weight 2 over Rational Field sage: m._dim_eisenstein() 1 3 """ try: return self.__the_dim_eisenstein
• ## sage/modular/modform/ambient_g1.py

diff --git a/sage/modular/modform/ambient_g1.py b/sage/modular/modform/ambient_g1.py
 a q^2 - 2*q^3 - q^4 + 2*q^5 + O(q^6) ] sage: M = ModularForms(GammaH(50, [27])); M Modular Forms space of dimension 13 for Congruence Subgroup Gamma_H(50) with H generated by [27] of weight 2 over Rational Field sage: M.q_expansion_basis(25) sage: M = ModularForms(GammaH(11, [4])); M Modular Forms space of dimension 2 for Congruence Subgroup Gamma_H(11) with H generated by [4] of weight 2 over Rational Field sage: M.q_expansion_basis(8) [ q + q^4 - q^6 - 2*q^9 - 3*q^11 - 2*q^14 + q^16 + 5*q^19 + 2*q^21 - q^24 + O(q^25), q^2 - q^3 - 2*q^7 + q^8 - q^12 + 4*q^13 + 3*q^17 - 2*q^18 - 3*q^22 - 6*q^23 + O(q^25), 1 + O(q^25), q + 12*q^11 + 8*q^14 + q^16 - 6*q^19 + 19*q^21 + 5*q^24 + O(q^25), q^2 + 5*q^12 - q^18 + 12*q^22 + O(q^25), q^3 - 3/2*q^12 + 9/2*q^13 - 1/2*q^17 + q^18 + 8*q^23 + O(q^25), q^4 + 4*q^14 + q^16 + 8*q^24 + O(q^25), q^5 + 4*q^15 - 2*q^20 + O(q^25), q^6 - q^14 + 3*q^16 + q^24 + O(q^25), q^7 + 1/2*q^12 - 1/2*q^13 + 5/2*q^17 + q^18 - q^23 + O(q^25), q^8 + q^12 + 2*q^18 + O(q^25), q^9 - q^16 + 2*q^19 + q^21 - q^24 + O(q^25), q^10 + 3*q^20 + O(q^25) q - 2*q^2 - q^3 + 2*q^4 + q^5 + 2*q^6 - 2*q^7 + O(q^8), 1 + 12/5*q + 36/5*q^2 + 48/5*q^3 + 84/5*q^4 + 72/5*q^5 + 144/5*q^6 + 96/5*q^7 + O(q^8) ] sage: M.q_integral_basis(25) [ 1 + O(q^25), q - 4*q^14 + q^16 + 6*q^19 + 7*q^21 - 7*q^24 + O(q^25), q^2 + 5*q^13 + 5*q^17 - q^18 + 2*q^22 + O(q^25), q^3 + 3*q^13 - 2*q^17 + q^18 + 3*q^22 + 8*q^23 + O(q^25), q^4 + 4*q^14 + q^16 + 8*q^24 + O(q^25), q^5 + 4*q^15 - 2*q^20 + O(q^25), q^6 - q^14 + 3*q^16 + q^24 + O(q^25), q^7 + 3*q^17 + q^18 - q^22 - q^23 + O(q^25), q^8 + q^13 + q^17 + 2*q^18 - 2*q^22 + O(q^25), q^9 - q^16 + 2*q^19 + q^21 - q^24 + O(q^25), q^10 + 3*q^20 + O(q^25), q^11 + q^14 - q^19 + q^21 + q^24 + O(q^25), q^12 - q^13 - q^17 + 2*q^22 + O(q^25) ] TESTS::
• ## sage/modular/modsym/ambient.py

diff --git a/sage/modular/modsym/ambient.py b/sage/modular/modsym/ambient.py
 a sage: M = ModularSymbols(GammaH(15,[7]),6) sage: M.modular_symbols_of_level(5) Modular Symbols space of dimension 4 for Congruence Subgroup Gamma_H(5) with H generated by [2] of weight 6 with sign 0 and over Rational Field Modular Symbols space of dimension 4 for Gamma_0(5) of weight 6 with sign 0 over Rational Field sage: M.modular_symbols_of_level(30) Traceback (most recent call last): ...
• ## sage/modular/modsym/boundary.py

diff --git a/sage/modular/modsym/boundary.py b/sage/modular/modsym/boundary.py
 a EXAMPLES:: sage: ModularSymbols(GammaH(14,[3]), 2).boundary_space().group() Congruence Subgroup Gamma_H(14) with H generated by [3] sage: ModularSymbols(GammaH(14,[9]), 2).boundary_space().group() Congruence Subgroup Gamma_H(14) with H generated by [9] """ return self.__group
• ## sage/modular/modsym/ghlist.py

diff --git a/sage/modular/modsym/ghlist.py b/sage/modular/modsym/ghlist.py
 a EXAMPLE:: sage: L = sage.modular.modsym.ghlist.GHlist(GammaH(3,[])); L.__repr__() 'List of coset representatives for Congruence Subgroup Gamma_H(3) with H generated by []' sage: L = sage.modular.modsym.ghlist.GHlist(GammaH(11,[4])); L.__repr__() 'List of coset representatives for Congruence Subgroup Gamma_H(11) with H generated by [4]' """ return "List of coset representatives for %s"%self.__group
• ## sage/modular/modsym/modsym.py

diff --git a/sage/modular/modsym/modsym.py b/sage/modular/modsym/modsym.py`
 a # TODO -- implement minimize_base_ring over finite fields eps = group G = eps.parent() group = (eps, G) if eps.is_trivial(): group = arithgroup.Gamma0(eps.modulus()) else: group = (eps, G) if base_ring is None: base_ring = eps.base_ring() if base_ring is None: base_ring = rational_field.RationalField()