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
""" Ambient Hecke modules """ from __future__ import absolute_import
#***************************************************************************** # Sage: System for Algebra and Geometry Experimentation # # Copyright (C) 2005 William Stein <wstein@gmail.com> # # Distributed under the terms of the GNU General Public License (GPL) # # This code is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # The full text of the GPL is available at: # # http://www.gnu.org/licenses/ #*****************************************************************************
from . import degenmap from . import module from . import submodule
import sage.modules.all
import sage.rings.all
import sage.misc.misc as misc
import sage.arith.all as arith
import sage.matrix.matrix_space as matrix_space from sage.matrix.constructor import matrix
from sage.modular.arithgroup.all import Gamma0 # for Sturm bound
def is_AmbientHeckeModule(x): r""" Return True if x is of type AmbientHeckeModule.
EXAMPLES::
sage: from sage.modular.hecke.ambient_module import is_AmbientHeckeModule sage: is_AmbientHeckeModule(ModularSymbols(6)) True sage: is_AmbientHeckeModule(ModularSymbols(6).cuspidal_subspace()) False sage: is_AmbientHeckeModule(ModularForms(11)) True sage: is_AmbientHeckeModule(BrandtModule(2, 3)) True """
class AmbientHeckeModule(module.HeckeModule_free_module): """ An ambient Hecke module, i.e. a Hecke module that is isomorphic as a module over its base ring `R` to the standard free module `R^k` for some `k`. This is the base class for ambient spaces of modular forms and modular symbols, and for Brandt modules. """ def __init__(self, base_ring, rank, level, weight, category=None): r""" Create an ambient Hecke module.
EXAMPLES::
sage: ModularSymbols(6) # indirect doctest Modular Symbols space of dimension 3 for Gamma_0(6) of weight 2 with sign 0 over Rational Field sage: sage.modular.hecke.ambient_module.AmbientHeckeModule(QQ, 3, 2, 4) Generic ambient Hecke module of rank 3, level 2 and weight 4 over Rational Field """ raise ValueError("rank (=%s) must be nonnegative"%rank) weight, category=category)
def rank(self): """ Return the rank of this ambient Hecke module.
OUTPUT:
Integer
EXAMPLES::
sage: M = sage.modular.hecke.ambient_module.AmbientHeckeModule(QQ, 3, 11, 2); M Generic ambient Hecke module of rank 3, level 11 and weight 2 over Rational Field sage: M.rank() 3 """
def __add__(self, other): r""" Sum of self and other. As self is an ambient space, this will only make sense if other is a subspace of self, in which case the answer is self.
EXAMPLES::
sage: M = ModularSymbols(23) sage: M + M is M True sage: M + 3 Traceback (most recent call last): ... TypeError: other (=3) must be a Hecke module. """ raise ArithmeticError("Sum only defined for subspaces of a common ambient Hecke module.")
def _repr_(self): r""" String representation of self. Should be overridden by derived classes.
EXAMPLES::
sage: sage.modular.hecke.ambient_module.AmbientHeckeModule(QQ, 3, 2, 4)._repr_() 'Generic ambient Hecke module of rank 3, level 2 and weight 4 over Rational Field' """
def _degeneracy_raising_matrix(self, codomain): """ Matrix of the degeneracy map (with t = 1) from self to codomain, whose level should be a multiple of the level of self.
EXAMPLES::
sage: sage.modular.hecke.ambient_module.AmbientHeckeModule(QQ, 3, 2, 4)._degeneracy_raising_matrix(4) Traceback (most recent call last): ... NotImplementedError """
def _degeneracy_lowering_matrix(self, codomain, t): """ Matrix of the degeneracy map of index t from self to codomain, whose level should be a divisor of the level of self.
EXAMPLES::
sage: sage.modular.hecke.ambient_module.AmbientHeckeModule(QQ, 3, 2, 4)._degeneracy_lowering_matrix(2, 2) Traceback (most recent call last): ... NotImplementedError """
def _hecke_image_of_ith_basis_element(self, n, i): """ Return the image under the Hecke operator T_n of the i-th basis element.
EXAMPLES::
sage: sage.modular.hecke.ambient_module.AmbientHeckeModule(QQ, 3, 2, 4)._hecke_image_of_ith_basis_element(4, 2) Traceback (most recent call last): ... NotImplementedError: All subclasses must implement _compute_hecke_matrix_prime """
def _set_dual_free_module(self, V): r""" Store the embedded dual module of this module. Since this module is an ambient module, this is not necessary.
EXAMPLES::
sage: ModularForms(11, 2)._set_dual_free_module(None) """
def ambient_hecke_module(self): r""" Return the ambient space that contains this ambient space. This is, of course, just this space again.
EXAMPLES::
sage: M = ModularForms(11, 4); M.ambient_hecke_module() is M True """
def complement(self): """ Return the largest Hecke-stable complement of this space.
EXAMPLES::
sage: M=ModularSymbols(11,2,1) sage: M Modular Symbols space of dimension 2 for Gamma_0(11) of weight 2 with sign 1 over Rational Field sage: M.complement() Modular Symbols subspace of dimension 0 of Modular Symbols space of dimension 2 for Gamma_0(11) of weight 2 with sign 1 over Rational Field sage: C=M.cuspidal_subspace() sage: C Modular Symbols subspace of dimension 1 of Modular Symbols space of dimension 2 for Gamma_0(11) of weight 2 with sign 1 over Rational Field sage: C.complement() Modular Symbols subspace of dimension 1 of Modular Symbols space of dimension 2 for Gamma_0(11) of weight 2 with sign 1 over Rational Field """
def decomposition_matrix(self): r""" Returns the matrix whose columns form a basis for the canonical sorted decomposition of self coming from the Hecke operators.
If the simple factors are `D_0, \ldots, D_n`, then the first few columns are an echelonized basis for `D_0`, the next an echelonized basis for `D_1`, the next for `D_2`, etc.
EXAMPLES::
sage: S = ModularSymbols(37, 2) sage: S.decomposition_matrix() [ 1 0 0 0 -1/3] [ 0 1 -1 0 1/2] [ 0 0 0 1 -1/2] [ 0 1 1 1 0] [ 0 0 0 0 1] """
def decomposition_matrix_inverse(self): """ Returns the inverse of the matrix returned by decomposition_matrix().
EXAMPLES::
sage: S = ModularSymbols(37, 2) sage: t = S.decomposition_matrix_inverse(); t [ 1 0 0 0 1/3] [ 0 1/2 -1/2 1/2 -1/2] [ 0 -1/2 -1/2 1/2 0] [ 0 0 1 0 1/2] [ 0 0 0 0 1] sage: t * S.decomposition_matrix() == 1 True """
def degeneracy_map(self, codomain, t=1): """ The `t`-th degeneracy map from self to the module ``codomain``. The level of the codomain must be a divisor or multiple of level, and t must be a divisor of the quotient.
INPUT:
- ``codomain`` - a Hecke module, which should be of the same type as self, or a positive integer (in which case Sage will use :meth:`~hecke_module_of_level` to find the "natural" module of the corresponding level). - ``t`` - int, the parameter of the degeneracy map, i.e., the map is related to `f(q)` - `f(q^t)`.
OUTPUT: A morphism from self to codomain.
EXAMPLES::
sage: M = ModularSymbols(11,sign=1) sage: d1 = M.degeneracy_map(33); d1 Hecke module morphism degeneracy map corresponding to f(q) |--> f(q) defined by the matrix [ 1 0 0 0 -2 -1] [ 0 0 -2 2 0 0] Domain: Modular Symbols space of dimension 2 for Gamma_0(11) of weight ... Codomain: Modular Symbols space of dimension 6 for Gamma_0(33) of weight ... sage: M.degeneracy_map(33,3).matrix() [ 3 2 2 0 -2 1] [ 0 2 0 -2 0 0] sage: M = ModularSymbols(33,sign=1) sage: d2 = M.degeneracy_map(11); d2.matrix() [ 1 0] [ 0 1/2] [ 0 -1] [ 0 1] [ -1 0] [ -1 0] sage: (d2*d1).matrix() [4 0] [0 4]
::
sage: M = ModularSymbols(3,12,sign=1) sage: M.degeneracy_map(1) Hecke module morphism degeneracy map corresponding to f(q) |--> f(q) defined by the matrix [1 0] [0 0] [0 1] [0 1] [0 1] Domain: Modular Symbols space of dimension 5 for Gamma_0(3) of weight ... Codomain: Modular Symbols space of dimension 2 for Gamma_0(1) of weight ...
::
sage: S = M.cuspidal_submodule() sage: S.degeneracy_map(1) Hecke module morphism defined by the matrix [1 0] [0 0] [0 0] Domain: Modular Symbols subspace of dimension 3 of Modular Symbols space ... Codomain: Modular Symbols space of dimension 2 for Gamma_0(1) of weight ...
::
sage: D = ModularSymbols(10,4).cuspidal_submodule().decomposition() sage: D [ Modular Symbols subspace of dimension 2 of Modular Symbols space of dimension 10 for Gamma_0(10) of weight 4 with sign 0 over Rational Field, Modular Symbols subspace of dimension 4 of Modular Symbols space of dimension 10 for Gamma_0(10) of weight 4 with sign 0 over Rational Field ] sage: D[1].degeneracy_map(5) Hecke module morphism defined by the matrix [ 0 0 -1 1] [ 0 1/2 3/2 -2] [ 0 -1 1 0] [ 0 -3/4 -1/4 1] Domain: Modular Symbols subspace of dimension 4 of Modular Symbols space ... Codomain: Modular Symbols space of dimension 4 for Gamma_0(5) of weight ...
We check for a subtle caching bug that came up in work on :trac:`10453`::
sage: loads(dumps(J0(33).decomposition()[0].modular_symbols())) Modular Symbols subspace of dimension 2 of Modular Symbols space of dimension 9 for Gamma_0(33) of weight 2 with sign 0 over Rational Field
We check that certain absurd inputs are correctly caught::
sage: chi = kronecker_character(7) sage: ModularSymbols(Gamma0(7), 4).degeneracy_map(ModularSymbols(chi, 4)) Traceback (most recent call last): ... ValueError: The characters of the domain and codomain must match """ else:
err = True else: err = True "level (=%s), and t (=%s) must be a divisor of the quotient.")%\ (self.level(), level, t))
raise ArithmeticError("The conductor of the character of this space " + \ "(=%s) must be divisible by the level (=%s)."%\ (eps.conductor(), level))
# bad idea to use (M, t) as the key, because using complicated objects # like modular forms spaces as dictionary keys causes weird behaviour; # on the other hand, (M.level(), t) isn't enough information.
def dual_free_module(self): r""" The free module dual to self, as a submodule of the dual module of the ambient space. As this space is ambient anyway, this just returns self.free_module().
EXAMPLES::
sage: M = ModularForms(2,8); M.dual_free_module() Vector space of dimension 3 over Rational Field sage: M.dual_free_module() is M.free_module() True """
def fcp(self, n, var='x'): """ Returns the factorization of the characteristic polynomial of the Hecke operator `T_n` of index `n` acting on this space.
INPUT:
- ``self`` - Hecke module invariant under the Hecke operator of index n.
- ``int n`` - a positive integer.
- ``var`` - variable of polynomial (default `x`)
OUTPUT:
- ``list`` - list of the pairs (g,e), where g is an irreducible factor of the characteristic polynomial of T_n, and e is its multiplicity.
EXAMPLES::
sage: m = ModularSymbols(23, 2, sign=1) sage: m.fcp(2) (x - 3) * (x^2 + x - 1) sage: m.hecke_operator(2).charpoly('x').factor() (x - 3) * (x^2 + x - 1) """ raise ArithmeticError("n (=%s) must be positive"%n)
def free_module(self): """ Return the free module underlying this ambient Hecke module (the forgetful functor from Hecke modules to modules over the base ring)
EXAMPLES::
sage: ModularForms(59, 2).free_module() Vector space of dimension 6 over Rational Field """
def hecke_bound(self): r""" Return an integer B such that the Hecke operators `T_n`, for `n\leq B`, generate the full Hecke algebra as a module over the base ring. Note that we include the `n` with `n` not coprime to the level.
At present this returns an unproven guess for non-cuspidal spaces which appears to be valid for `M_k(\Gamma_0(N))`, where k and N are the weight and level of self. (It is clearly valid for *cuspidal* spaces of any fixed character, as a consequence of the Sturm bound theorem.) It returns a hopelessly wrong answer for spaces of full level `\Gamma_1`.
TODO: Get rid of this dreadful bit of code.
EXAMPLES::
sage: ModularSymbols(17, 4).hecke_bound() 15 sage: ModularSymbols(Gamma1(17), 4).hecke_bound() # wrong! 15 """ except AttributeError: pass
def hecke_module_of_level(self, level): r""" Return the Hecke module corresponding to self at the given level, which should be either a divisor or a multiple of the level of self. This raises NotImplementedError, and should be overridden in derived classes.
EXAMPLES::
sage: sage.modular.hecke.ambient_module.AmbientHeckeModule.hecke_module_of_level(ModularForms(2, 8),6) Traceback (most recent call last): ... NotImplementedError """
def hecke_images(self, i, v): """ Return images of the `i`-th standard basis vector under the Hecke operators `T_p` for all integers in `v`.
INPUT:
- ``i`` - nonnegative integer
- ``v`` - a list of positive integer
OUTPUT:
- ``matrix`` - whose rows are the Hecke images
EXAMPLES::
sage: M = ModularSymbols(DirichletGroup(13).0, 3) sage: M.T(2)(M.0).element() (zeta12 + 4, 0, -1, 1) sage: M.hecke_images(0, [1,2]) [ 1 0 0 0] [zeta12 + 4 0 -1 1] """ # Use slow generic algorithm
def intersection(self, other): """ Returns the intersection of self and other, which must both lie in a common ambient space of modular symbols.
EXAMPLES::
sage: M = ModularSymbols(43, sign=1) sage: A = M[0] + M[1] sage: B = M[1] + M[2] sage: A.rank(), B.rank() (2, 3) sage: C = A.intersection(B); C.rank() # TODO 1 """ raise TypeError("other (=%s) must be a Hecke module."%other) raise ArithmeticError("Intersection only defined for subspaces of a common ambient Hecke module.")
def is_ambient(self): r""" Returns True if and only if self is an ambient Hecke module.
.. warning::
self can only be ambient by being of type AmbientHeckeModule.
For example, decomposing a simple ambient space yields a single factor, and that factor is *not* considered an ambient space.
EXAMPLES::
sage: m = ModularSymbols(10) sage: m.is_ambient() True
::
sage: a = m[0] # the unique simple factor sage: a == m True sage: a.is_ambient() False """
def is_full_hecke_module(self, compute=True): """ Returns True if this space is invariant under the action of all Hecke operators, even those that divide the level. This is always true for ambient Hecke modules, so return True.
EXAMPLES::
sage: ModularSymbols(11, 4).is_full_hecke_module() True """
def is_new(self, p=None): r""" Return True if this module is entirely new.
EXAMPLES::
sage: ModularSymbols(11, 4).is_new() False sage: ModularSymbols(1, 12).is_new() True """ return self.__is_new[p]
def is_old(self, p=None): r""" Return True if this module is entirely old.
EXAMPLES::
sage: ModularSymbols(22).is_old() True sage: ModularSymbols(3, 12).is_old() False """ return self.__is_old[p]
def is_submodule(self, V): """ Returns True if and only if self is a submodule of V. Since this is an ambient space, this returns True if and only if V is equal to self.
EXAMPLES::
sage: ModularSymbols(1, 4).is_submodule(ModularSymbols(11,4)) False sage: ModularSymbols(11, 4).is_submodule(ModularSymbols(11,4)) True """
def linear_combination_of_basis(self, v): r""" Given a list or vector of length equal to the dimension of self, construct the appropriate linear combination of the basis vectors of self.
EXAMPLES::
sage: ModularForms(3, 12).linear_combination_of_basis([1,0,0,0,1]) 2*q + 2049*q^2 + 177147*q^3 + 4196177*q^4 + 48830556*q^5 + O(q^6)
"""
def new_submodule(self, p=None): """ Returns the new or p-new submodule of self.
INPUT:
- ``p`` - (default: None); if not None, return only the p-new submodule.
OUTPUT: the new or p-new submodule of self, i.e. the intersection of the kernel of the degeneracy lowering maps to level `N/p` (for the given prime `p`, or for all prime divisors of `N` if `p` is not given).
If self is cuspidal this is a Hecke-invariant complement of the corresponding old submodule, but this may break down on Eisenstein subspaces (see the amusing example in William Stein's book of a form which is new and old at the same time).
EXAMPLES::
sage: m = ModularSymbols(33); m.rank() 9 sage: m.new_submodule().rank() 3 sage: m.new_submodule(3).rank() 4 sage: m.new_submodule(11).rank() 8 """
# Construct the degeneracy map d. else: else: raise ValueError("p must divide the level.") # Here we are only using degeneracy *lowering* maps, so it is fine # to be careless and pass an integer for the level. One needs to be # a bit more careful with degeneracy *raising* maps for the Gamma1 # and GammaH cases. else: #end if #end for else:
def nonembedded_free_module(self): r""" Return the free module corresponding to self as an abstract free module (rather than as a submodule of an ambient free module). As this module is ambient anyway, this just returns ``self.free_module()``.
EXAMPLES::
sage: M = ModularSymbols(11, 2) sage: M.nonembedded_free_module() is M.free_module() True """
def old_submodule(self, p=None): """ Returns the old or p-old submodule of self, i.e. the sum of the images of the degeneracy maps from level `N/p` (for the given prime `p`, or for all primes `p` dividing `N` if `p` is not given).
INPUT:
- ``p`` - (default: None); if not None, return only the p-old submodule.
OUTPUT: the old or p-old submodule of self
EXAMPLES::
sage: m = ModularSymbols(33); m.rank() 9 sage: m.old_submodule().rank() 7 sage: m.old_submodule(3).rank() 6 sage: m.new_submodule(11).rank() 8
::
sage: e = DirichletGroup(16)([-1, 1]) sage: M = ModularSymbols(e, 3, sign=1); M Modular Symbols space of dimension 4 and level 16, weight 3, character [-1, 1], sign 1, over Rational Field sage: M.old_submodule() Modular Symbols subspace of dimension 3 of Modular Symbols space of dimension 4 and level 16, weight 3, character [-1, 1], sign 1, over Rational Field
Illustrate that :trac:`10664` is fixed::
sage: ModularSymbols(DirichletGroup(42)[7], 6, sign=1).old_subspace(3) Modular Symbols subspace of dimension 0 of Modular Symbols space of dimension 40 and level 42, weight 6, character [-1, -1], sign 1, over Rational Field
""" return self
self.__is_old[p] = True return self
# Construct the degeneracy map d.
else:
else: raise ValueError("p must divide the level.")
# Here it is vital to pass self as an argument to # degeneracy_map, because M and the level N don't uniquely # determine self (e.g. the degeneracy map from level 1 to level # N could go to Gamma0(N), Gamma1(N) or anything in between)
else: #end if #end for else:
def submodule(self, M, Mdual=None, check=True): """ Return the Hecke submodule of self generated by M, which may be a submodule of the free module of self, or a list of elements of self.
EXAMPLES::
sage: M = ModularForms(37, 2) sage: A = M.submodule([M.newforms()[0].element(), M.newforms()[1].element()]); A Modular Forms subspace of dimension 2 of Modular Forms space of dimension 3 for Congruence Subgroup Gamma0(37) of weight 2 over Rational Field """ else: M = V.span(M) raise TypeError("M must be a submodule of the free module associated to this module.") return self
def _submodule_class(self): r""" The class of submodules of this module. This is a separate method so it can be overridden in derived classes.
EXAMPLES::
sage: sage.modular.hecke.ambient_module.AmbientHeckeModule._submodule_class(ModularForms(1, 24)) <class 'sage.modular.hecke.submodule.HeckeSubmodule'> sage: ModularForms(1, 24)._submodule_class() <class 'sage.modular.modform.submodule.ModularFormsSubmodule'> """
def submodule_from_nonembedded_module(self, V, Vdual=None, check=True): """ Create a submodule of this module, from a submodule of an ambient free module of the same rank as the rank of self.
INPUT:
- ``V`` - submodule of ambient free module of the same rank as the rank of self.
- ``Vdual`` - used to pass in dual submodule (may be None)
- ``check`` - whether to check that submodule is Hecke equivariant
OUTPUT: Hecke submodule of self
EXAMPLES::
sage: V = QQ^8 sage: ModularForms(24, 2).submodule_from_nonembedded_module(V.submodule([0])) Modular Forms subspace of dimension 0 of Modular Forms space of dimension 8 for Congruence Subgroup Gamma0(24) of weight 2 over Rational Field """
def submodule_generated_by_images(self, M): """ Return the submodule of this ambient modular symbols space generated by the images under all degeneracy maps of M. The space M must have the same weight, sign, and group or character as this ambient space.
EXAMPLES::
sage: ModularSymbols(6, 12).submodule_generated_by_images(ModularSymbols(1,12)) Modular Symbols subspace of dimension 12 of Modular Symbols space of dimension 22 for Gamma_0(6) of weight 12 with sign 0 over Rational Field """ elif M.level() % self.level() == 0: D = arith.divisors(M.level() // self.level()) else: D = [] raise ArithmeticError("incompatible spaces of modular symbols")
|