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. EXAMPLE:: 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] """ try: return self.__decomposition_matrix_cache except AttributeError: rows = [] for A in self.decomposition(): for x in A.basis(): rows.append(x.list()) A = matrix_space.MatrixSpace(self.base_ring(), self.rank())(rows) self.__decomposition_matrix_cache = A return self.__decomposition_matrix_cache
def restrict_domain(self, sub): """ Restrict self to the subvariety sub of self.domain(). EXAMPLES:: sage: J = J0(37) ; A, B = J.decomposition() sage: A.lattice().matrix() [ 1 -1 1 0] [ 0 0 2 -1] sage: B.lattice().matrix() [1 1 1 0] [0 0 0 1] sage: T = J.hecke_operator(2) ; T.matrix() [-1 1 1 -1] [ 1 -1 1 0] [ 0 0 -2 1] [ 0 0 0 0] sage: T.restrict_domain(A) Abelian variety morphism: From: Simple abelian subvariety 37a(1,37) of dimension 1 of J0(37) To: Abelian variety J0(37) of dimension 2 sage: T.restrict_domain(A).matrix() [-2 2 -2 0] [ 0 0 -4 2] sage: T.restrict_domain(B) Abelian variety morphism: From: Simple abelian subvariety 37b(1,37) of dimension 1 of J0(37) To: Abelian variety J0(37) of dimension 2 sage: T.restrict_domain(B).matrix() [0 0 0 0] [0 0 0 0] """ if not sub.is_subvariety(self.domain()): raise ValueError("sub must be a subvariety of self.domain()") if sub == self.domain(): return self L = self.domain().lattice() B = sub.lattice().basis() ims = sum([(L(b) * self.matrix()).list() for b in B], []) MS = matrix_space.MatrixSpace(self.base_ring(), len(B), self.codomain().rank()) H = sub.Hom(self.codomain(), self.category_for()) return H(MS(ims))
def gens_to_basis_matrix(syms, relation_matrix, mod, field, sparse): """ Compute echelon form of 3-term relation matrix, and read off each generator in terms of basis. INPUT: - ``syms`` - a ManinSymbols object - ``relation_matrix`` - as output by ``__compute_T_relation_matrix(self, mod)`` - ``mod`` - quotient of modular symbols modulo the 2-term S (and possibly I) relations - ``field`` - base field - ``sparse`` - (bool): whether or not matrix should be sparse OUTPUT: - ``matrix`` - a matrix whose ith row expresses the Manin symbol generators in terms of a basis of Manin symbols (modulo the S, (possibly I,) and T rels) Note that the entries of the matrix need not be integers. - ``list`` - integers i, such that the Manin symbols `x_i` are a basis. EXAMPLE:: sage: from sage.modular.modsym.relation_matrix import * sage: L = sage.modular.modsym.manin_symbols.ManinSymbolList_gamma1(4, 3) sage: modS = sparse_2term_quotient(modS_relations(L), 24, GF(3)) sage: gens_to_basis_matrix(L, T_relation_matrix_wtk_g0(L, modS, GF(3), 24), modS, GF(3), True) (24 x 2 sparse matrix over Finite Field of size 3, [13, 23]) """ if not sage.matrix.all.is_Matrix(relation_matrix): raise TypeError, "relation_matrix must be a matrix" if not isinstance(mod, list): raise TypeError, "mod must be a list" misc.verbose(str(relation_matrix.parent())) try: h = relation_matrix.height() except AttributeError: h = 9999999 tm = misc.verbose("putting relation matrix in echelon form (height = %s)"%h) if h < 10: A = relation_matrix.echelon_form(algorithm='multimodular', height_guess=1) else: A = relation_matrix.echelon_form() A.set_immutable() tm = misc.verbose('finished echelon', tm) tm = misc.verbose("Now creating gens --> basis mapping") basis_set = set(A.nonpivots()) pivots = A.pivots() basis_mod2 = set([j for j,c in mod if c != 0]) basis_set = basis_set.intersection(basis_mod2) basis = list(basis_set) basis.sort() ONE = field(1) misc.verbose("done doing setup",tm) tm = misc.verbose("now forming quotient matrix") M = matrix_space.MatrixSpace(field, len(syms), len(basis), sparse=sparse) B = M(0) cols_index = dict([(basis[i], i) for i in range(len(basis))]) for i in basis_mod2: t, l = search(basis, i) if t: B[i,l] = ONE else: _, r = search(pivots, i) # so pivots[r] = i # Set row i to -(row r of A), but where we only take # the non-pivot columns of A: B._set_row_to_negative_of_row_of_A_using_subset_of_columns(i, A, r, basis, cols_index) misc.verbose("done making quotient matrix",tm) # The following is very fast (over Q at least). tm = misc.verbose('now filling in the rest of the matrix') k = 0 for i in range(len(mod)): j, s = mod[i] if j != i and s != 0: # ignored in the above matrix k += 1 B.set_row_to_multiple_of_row(i, j, s) misc.verbose("set %s rows"%k) tm = misc.verbose("time to fill in rest of matrix", tm) return B, basis
def T_relation_matrix_wtk_g0(syms, mod, field, sparse): r""" Compute a matrix whose echelon form gives the quotient by 3-term T relations. Despite the name, this is used for all modular symbols spaces (including those with character and those for `\Gamma_1` and `\Gamma_H` groups), not just `\Gamma_0`. INPUT: - ``syms`` - ManinSymbols - ``mod`` - list that gives quotient modulo some two-term relations, i.e., the S relations, and if sign is nonzero, the I relations. - ``field`` - base_ring - ``sparse`` - (True or False) whether to use sparse rather than dense linear algebra OUTPUT: A sparse matrix whose rows correspond to the reduction of the T relations modulo the S and I relations. EXAMPLE:: sage: from sage.modular.modsym.relation_matrix import * sage: L = sage.modular.modsym.manin_symbols.ManinSymbolList_gamma_h(GammaH(36, [17,19]), 2) sage: modS = sparse_2term_quotient(modS_relations(L), 216, QQ) sage: T_relation_matrix_wtk_g0(L, modS, QQ, False) 72 x 216 dense matrix over Rational Field sage: T_relation_matrix_wtk_g0(L, modS, GF(17), True) 72 x 216 sparse matrix over Finite Field of size 17 """ tm = misc.verbose() row = 0 entries = {} already_seen = set() w = syms.weight() for i in xrange(len(syms)): if i in already_seen: continue iT_plus_iTT = syms.apply_T(i) + syms.apply_TT(i) j0, s0 = mod[i] v = {j0:s0} for j, s in iT_plus_iTT: if w==2: already_seen.add(j) j0, s0 = mod[j] s0 = s*s0 if v.has_key(j0): v[j0] += s0 else: v[j0] = s0 for j0 in v.keys(): entries[(row, j0)] = v[j0] row += 1 MAT = matrix_space.MatrixSpace(field, row, len(syms), sparse=True) R = MAT(entries) if not sparse: R = R.dense_matrix() misc.verbose("finished (number of rows=%s)"%row, tm) return R
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 """ if is_AmbientHeckeModule(codomain): M = codomain level = int(M.level()) else: level = int(codomain) M = None t = int(t) err = False if self.level() % level == 0: quo = self.level() // level if quo % t != 0: err = True elif level % self.level() == 0: quo = level // self.level() if quo % t != 0: err = True else: err = True if err: raise ValueError(("The level of self (=%s) must be a divisor or multiple of " + \ "level (=%s), and t (=%s) must be a divisor of the quotient.")%\ (self.level(), level, t)) eps = self.character() if not (eps is None) and level % eps.conductor() != 0: raise ArithmeticError("The conductor of the character of this space " + \ "(=%s) must be divisible by the level (=%s)."%\ (eps.conductor(), level)) if M is None: M = self.hecke_module_of_level(level) if eps is not None and M.character() is not None: if eps.primitive_character() != M.character().primitive_character( ): raise ValueError( "The characters of the domain and codomain must match") key = (M.group(), t) # 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. try: self._degeneracy_maps except AttributeError: self._degeneracy_maps = {} if key in self._degeneracy_maps: return self._degeneracy_maps[key] if M.rank() == 0: A = matrix_space.MatrixSpace(self.base_ring(), self.rank(), 0)(0) elif self.level() % level == 0: # lower the level A = self._degeneracy_lowering_matrix(M, t) elif level % self.level() == 0: # raise the level A = self._degeneracy_raising_matrix(M, t) d = degenmap.DegeneracyMap(A, self, M, t) self._degeneracy_maps[key] = d return d