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""" Manin Relations for overconvergent modular symbols
Code to create the Manin Relations class, which solves the "Manin relations". That is, a description of `Div^0(P^1(\QQ))` as a `\ZZ[\Gamma_0(N)]`-module in terms of generators and relations is found. The method used is geometric, constructing a nice fundamental domain for `\Gamma_0(N)` and reading the relevant Manin relations off of that picture. The algorithm follows [PS2011]_.
AUTHORS:
- Robert Pollack, Jonathan Hanke (2012): initial version
""" #***************************************************************************** # Copyright (C) 2012 Robert Pollack <rpollack@math.bu.edu> # Jonathan Hanke <jonhanke@gmail.com> # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function, absolute_import from six import iteritems
from sage.matrix.matrix_space import MatrixSpace from sage.modular.modsym.all import P1List from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.structure.sage_object import SageObject from sage.misc.cachefunc import cached_method
from .sigma0 import Sigma0
M2ZSpace = MatrixSpace(ZZ,2)
def M2Z(x): r""" Create an immutable `2 \times 2` integer matrix from ``x``.
INPUT: anything that can be converted into a `2 \times 2` matrix.
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import M2Z sage: M2Z([1,2,3,4]) [1 2] [3 4] sage: M2Z(1) [1 0] [0 1] """
Id = M2Z([1, 0, 0, 1]) sig = M2Z([0, 1, -1, 0]) tau = M2Z([0, -1, 1, -1]) minone_inf_path = M2Z([1, 1, -1, 0])
# We store these so that we do not have to constantly create them. t00 = (0, 0) t10 = (1, 0) t01 = (0, 1) t11 = (1, 1)
class PollackStevensModularDomain(SageObject): r""" The domain of a modular symbol.
INPUT:
- ``N`` -- a positive integer, the level of the congruence subgroup `\Gamma_0(N)`
- ``reps`` -- a list of `2 \times 2` matrices, the coset representatives of `Div^0(P^1(\QQ))`
- ``indices`` -- a list of integers; indices of elements in ``reps`` which are generators
- ``rels`` -- a list of list of triples ``(d, A, i)``, one for each coset representative of ``reps`` which describes how to express the elements of ``reps`` in terms of generators specified by ``indices``. See :meth:`relations` for a detailed explanations of these triples.
- ``equiv_ind`` -- a dictionary which maps normalized coordinates on `P^1(\ZZ/N\ZZ)` to an integer such that a matrix whose bottom row is equivalent to `[a:b]` in `P^1(\ZZ/N\ZZ)` is in the coset of ``reps[equiv_ind[(a,b)]]``
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import PollackStevensModularDomain, M2Z sage: PollackStevensModularDomain(2 , [M2Z([1,0,0,1]), M2Z([1,1,-1,0]), M2Z([0,-1,1,1])], [0,2], [[(1, M2Z([1,0,0,1]), 0)], [(-1,M2Z([-1,-1,0,-1]),0)], [(1, M2Z([1,0,0,1]), 2)]], {(0,1): 0, (1,0): 1, (1,1): 2}) Modular Symbol domain of level 2
TESTS:
The level ``N`` must be an integer::
sage: PollackStevensModularDomain(1/2, None, None, None, None) Traceback (most recent call last): ... TypeError: no conversion of this rational to integer sage: PollackStevensModularDomain(Gamma0(11), None, None, None, None) Traceback (most recent call last): ... TypeError: unable to coerce <class 'sage.modular.arithgroup.congroup_gamma0.Gamma0_class_with_category'> to an integer
""" def __init__(self, N, reps, indices, rels, equiv_ind): r""" INPUT:
See :class:`PollackStevensModularDomain`.
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import PollackStevensModularDomain, ManinRelations sage: isinstance(ManinRelations(11), PollackStevensModularDomain) # indirect doctest True """
raise ValueError("length of reps and length of rels must be equal")
def _repr_(self): r""" A string representation of this domain.
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import PollackStevensModularDomain, M2Z sage: PollackStevensModularDomain(2 , [M2Z([1,0,0,1]), M2Z([1,1,-1,0]), M2Z([0,-1,1,1])], [0,2], [[(1, M2Z([1,0,0,1]), 0)], [(-1,M2Z([-1,-1,0,-1]),0)], [(1, M2Z([1,0,0,1]), 2)]], {(0,1): 0, (1,0): 1, (1,1): 2})._repr_() 'Modular Symbol domain of level 2' """
def __len__(self): r""" Return the number of coset representatives.
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: A = ManinRelations(11) sage: len(A) 12 """
def __getitem__(self, i): r""" Return the ``i``-th coset representative.
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: A = ManinRelations(11) sage: A[4] [-1 -2] [ 2 3] """
def __iter__(self): r""" Return an iterator over all coset representatives.
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: A = ManinRelations(11) sage: for rep in A: ....: if rep[1,0] == 1: ....: print(rep) [ 0 -1] [ 1 3] [ 0 -1] [ 1 2] [ 0 -1] [ 1 1] """
def gens(self): r""" Return the list of coset representatives chosen as generators.
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: A = ManinRelations(11) sage: A.gens() [ [1 0] [ 0 -1] [-1 -1] [0 1], [ 1 3], [ 3 2] ] """
def gen(self, n=0): r""" Return the ``n``-th generator.
INPUT:
- ``n`` -- integer (default: 0), which generator is desired
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: A = ManinRelations(137) sage: A.gen(17) [-4 -1] [ 9 2] """
def ngens(self): r""" Return the number of generators.
OUTPUT:
The number of coset representatives from which a modular symbol's value on any coset can be derived.
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: A = ManinRelations(1137) sage: A.ngens() 255 """
def level(self): r""" Return the level `N` of `\Gamma_0(N)` that we work with.
OUTPUT:
The integer `N` of the group `\Gamma_0(N)` for which the Manin Relations are being computed.
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: A = ManinRelations(11) sage: A.level() 11 """
def indices(self, n=None): r""" Return the `n`-th index of the coset representatives which were chosen as our generators.
In particular, the divisors associated to these coset representatives generate all divisors over `\ZZ[\Gamma_0(N)]`, and thus a modular symbol is uniquely determined by its values on these divisors.
INPUT:
- ``n`` -- integer (default: None)
OUTPUT:
The ``n``-th index of the generating set in ``self.reps()`` or all indices if ``n`` is ``None``.
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: A = ManinRelations(11) sage: A.indices() [0, 2, 3]
sage: A.indices(2) 3
sage: A = ManinRelations(13) sage: A.indices() [0, 2, 3, 4, 5]
sage: A = ManinRelations(101) sage: A.indices() [0, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 16, 17, 19, 20, 23, 24, 26, 28] """ else:
def reps(self, n=None): r""" Return the ``n``-th coset representative associated with our fundamental domain.
INPUT:
- ``n`` -- integer (default: None)
OUTPUT:
The ``n``-th coset representative or all coset representatives if ``n`` is ``None``.
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: A = ManinRelations(11) sage: A.reps(0) [1 0] [0 1] sage: A.reps(1) [ 1 1] [-1 0] sage: A.reps(2) [ 0 -1] [ 1 3] sage: A.reps() [ [1 0] [ 1 1] [ 0 -1] [-1 -1] [-1 -2] [-2 -1] [ 0 -1] [ 1 0] [0 1], [-1 0], [ 1 3], [ 3 2], [ 2 3], [ 3 1], [ 1 2], [-2 1], <BLANKLINE> [ 0 -1] [ 1 0] [-1 -1] [ 1 -1] [ 1 1], [-1 1], [ 2 1], [-1 2] ] """ else:
def relations(self, A=None): r""" Express the divisor attached to the coset representative of ``A`` in terms of our chosen generators.
INPUT:
- ``A`` -- ``None``, an integer, or a coset representative (default: ``None``)
OUTPUT:
A `\ZZ[\Gamma_0(N)]`-relation expressing the divisor attached to ``A`` in terms of the generating set. The relation is given as a list of triples ``(d, B, i)`` such that the divisor attached to `A`` is the sum of ``d`` times the divisor attached to ``B^{-1} * self.reps(i)``.
If ``A`` is an integer, then return this data for the ``A``-th coset representative.
If ``A`` is ``None``, then return this data in a list for all coset representatives.
.. NOTE::
These relations allow us to recover the value of a modular symbol on any coset representative in terms of its values on our generating set.
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: MR = ManinRelations(11) sage: MR.indices() [0, 2, 3] sage: MR.relations(0) [(1, [1 0] [0 1], 0)] sage: MR.relations(2) [(1, [1 0] [0 1], 2)] sage: MR.relations(3) [(1, [1 0] [0 1], 3)]
The fourth coset representative can be expressed through the second coset representative::
sage: MR.reps(4) [-1 -2] [ 2 3] sage: d, B, i = MR.relations(4)[0] sage: P = B.inverse()*MR.reps(i); P [ 2 -1] [-3 2] sage: d # the above corresponds to minus the divisor of A.reps(4) since d is -1 -1
The sixth coset representative can be expressed as the sum of the second and the third::
sage: MR.reps(6) [ 0 -1] [ 1 2] sage: MR.relations(6) [(1, [1 0] [0 1], 2), (1, [1 0] [0 1], 3)] sage: MR.reps(2), MR.reps(3) # MR.reps(6) is the sum of these divisors ( [ 0 -1] [-1 -1] [ 1 3], [ 3 2] )
TESTS:
Test that the other ways of calling this method work::
sage: MR.relations(MR.reps(6)) [(1, [1 0] [0 1], 2), (1, [1 0] [0 1], 3)] sage: MR.relations(None) [[(1, [1 0] [0 1], 0)], [(-1, [-1 -1] [ 0 -1], 0)], [(1, [1 0] [0 1], 2)], [(1, [1 0] [0 1], 3)], [(-1, [-3 -2] [11 7], 2)], [(-1, [-4 -3] [11 8], 3)], [(1, [1 0] [0 1], 2), (1, [1 0] [0 1], 3)], [(-1, [1 0] [0 1], 2), (-1, [1 0] [0 1], 3)], [(1, [1 0] [0 1], 2), (1, [1 0] [0 1], 3), (-1, [-3 -2] [11 7], 2), (-1, [-4 -3] [11 8], 3)], [(-1, [1 0] [0 1], 2), (-1, [1 0] [0 1], 3), (1, [-3 -2] [11 7], 2), (1, [-4 -3] [11 8], 3)], [(-1, [-3 -2] [11 7], 2), (-1, [-4 -3] [11 8], 3)], [(1, [-3 -2] [11 7], 2), (1, [-4 -3] [11 8], 3)]] """ else:
def equivalent_index(self, A): r""" Return the index of the coset representative equivalent to ``A``.
Here by equivalent we mean the unique coset representative whose bottom row is equivalent to the bottom row of ``A`` in `P^1(\ZZ/N\ZZ)`.
INPUT:
- ``A`` -- an element of `SL_2(\ZZ)`
OUTPUT:
The unique integer ``j`` satisfying that the bottom row of ``self.reps(j)`` is equivalent to the bottom row of ``A``.
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: MR = ManinRelations(11) sage: A = matrix(ZZ,2,2,[1,5,3,16]) sage: j = MR.equivalent_index(A); j 11 sage: MR.reps(11) [ 1 -1] [-1 2] sage: MR.equivalent_rep(A) [ 1 -1] [-1 2] sage: MR.P1().normalize(3,16) (1, 9) """
def equivalent_rep(self, A): r""" Return a coset representative that is equivalent to ``A`` modulo `\Gamma_0(N)`.
INPUT:
- ``A`` -- a matrix in `SL_2(\ZZ)`
OUTPUT:
The unique generator congruent to ``A`` modulo `\Gamma_0(N)`.
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: A = matrix([[5,3],[38,23]]) sage: ManinRelations(60).equivalent_rep(A) [-7 -3] [26 11] """
def P1(self): r""" Return the Sage representation of `P^1(\ZZ/N\ZZ)`.
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: A = ManinRelations(11) sage: A.P1() The projective line over the integers modulo 11
"""
class ManinRelations(PollackStevensModularDomain): r""" This class gives a description of `Div^0(P^1(\QQ))` as a `\ZZ[\Gamma_0(N)]`-module.
INPUT:
- ``N`` -- a positive integer, the level of `\Gamma_0(N)` to work with
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: ManinRelations(1) Manin Relations of level 1 sage: ManinRelations(11) Manin Relations of level 11
Large values of ``N`` are not supported::
sage: ManinRelations(2^20) Traceback (most recent call last): ... OverflowError: Modulus is too large (must be <= 46340)
TESTS:
``N`` has to be a positive integer::
sage: ManinRelations(0) Traceback (most recent call last): ... ValueError: N must be a positive integer sage: ManinRelations(-5) Traceback (most recent call last): ... ValueError: N must be a positive integer
""" def __init__(self, N): r""" Create an instance of this class.
INPUT:
- ``N`` -- a positive integer, the level of `\Gamma_0(N)` to work with
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: type(ManinRelations(30)) <class 'sage.modular.pollack_stevens.fund_domain.ManinRelations'> """
## Creates and stores the Sage representation of P^1(Z/NZ)
## Creates a fundamental domain for Gamma_0(N) whose boundary ## is a union of unimodular paths (except in the case of ## 3-torsion). We will call the intersection of this domain ## with the real axis the collection of cusps (even if some ## are Gamma_0(N) equivalent to one another).
## Takes the boundary of this fundamental domain and finds ## SL_2(Z) matrices whose associated unimodular path gives ## this boundary. These matrices form the beginning of our ## collection of coset reps for Gamma_0(N) / SL_2(Z).
## Takes the bottom row of each of our current coset reps, ## thinking of them as distinct elements of P^1(Z/NZ)
## Initializes relevant Manin data
## the list rels (above) will give Z[Gamma_0(N)] relations between ## the associated divisor of each coset representatives in terms ## of our chosen set of generators. ## entries of rels will be lists of elements of the form (c,A,r) ## with c a constant, A a Gamma_0(N) matrix, and r the index of a ## generator. The meaning is that the divisor associated to the ## j-th coset rep will equal the sum of: ## ## c * A^(-1) * (divisor associated to r-th coset rep) ## ## as one varies over all (c,A,r) in rels[j]. ## (Here r must be in self.generator_indices().) ## ## This will be used for modular symbols as then the value of a ## modular symbol phi on the (associated divisor) of the j-th ## element of coset_reps will be the sum of c * phi (r-th generator) | A ## as one varies over the tuples in rels[j]
## The list boundary_checked keeps track of which boundary pieces of the ## fundamental domain have been already used as we are picking ## our generators
## The following loop will choose our generators by picking one edge ## out of each pair of edges that are glued to each other and picking ## each edge glued to itself (arising from two-torsion) ## ------------------------------------------------------------------
## We now check if this boundary edge is glued to itself by ## Gamma_0(N)
## This edge is glued to itself and so coset_reps[r] ## needs to be added to our generator list.
## this relation expresses the fact that ## coset_reps[r] is one of our basic generators
## the index r is adding to our list ## of indexes of generators
## the index r is adding to our list of indexes of ## generators which satisfy a 2-torsion relation
# we use the adjoint instead of the inverse for speed ## gam is 2-torsion matrix and in Gamma_0(N). ## if D is the divisor associated to coset_reps[r] ## then gam * D = - D and so (1+gam)D=0.
## This gives a restriction to the possible values of ## modular symbols on D
## The 2-torsion matrix gam is recorded in our list of ## 2-torsion relations.
## We have now finished with this edge.
else:
## In the following case the ideal triangle below ## the unimodular path described by coset_reps[r] ## contains a point fixed by a 3-torsion element.
## the index r is adding to our list of indexes ## of generators
## this relation expresses the fact that coset_reps[r] ## is one of our basic generators
## the index r is adding to our list of indexes of ## generators which satisfy a 3-torsion relation
# Use the adjoint instead of the inverse for speed. ## gam is 3-torsion matrix and in Gamma_0(N). ## if D is the divisor associated to coset_reps[r] ## then (1+gam+gam^2)D=0. ## This gives a restriction to the possible values of ## modular symbols on D
## The 3-torsion matrix gam is recorded in our list of ## 3-torsion relations.
## The reverse of the unimodular path associated to ## coset_reps[r] is not Gamma_0(N) equivalent to it, so ## we need to include it in our list of coset ## representatives and record the relevant relations.
## A (representing the reversed edge) is included in ## our list of coset reps
## This relation means that phi on the reversed edge ## equals -phi on original edge
## We have now finished with this edge.
else: ## This is the generic case where neither 2 or ## 3-torsion intervenes. ## The below loop searches through the remaining edges ## and finds which one is equivalent to the reverse of ## coset_reps[r] ## --------------------------------------------------- ## the reverse of coset_reps[r] is ## Gamma_0(N)-equivalent to coset_reps[s] ## coset_reps[r] will now be made a generator ## and we need to express phi(coset_reps[s]) ## in terms of phi(coset_reps[r])
## the index r is adding to our list of ## indexes of generators
## this relation expresses the fact that ## coset_reps[r] is one of our basic generators
## A corresponds to reversing the orientation ## of the edge corr. to coset_reps[r] # Use adjoint instead of inverse for speed ## gam is in Gamma_0(N) (by assumption of ## ending up here in this if statement)
## this relation means that phi evaluated on ## coset_reps[s] equals -phi(coset_reps[r])|gam ## To see this, let D_r be the divisor ## associated to coset_reps[r] and D_s to ## coset_reps[s]. Then gam D_s = -D_r and so ## phi(gam D_s) = - phi(D_r) and thus ## phi(D_s) = -phi(D_r)|gam ## since gam is in Gamma_0(N)
## this is a dictionary whose keys are the ## non-torsion generators and whose values ## are the corresponding gamma_i. It is ## eventually stored as self.gammas.
## We now need to complete our list of coset representatives by ## finding all unimodular paths in the interior of the fundamental ## domain, as well as express these paths in terms of our chosen set ## of generators. ## -------------------------------------------------------------------
## r is the index of the cusp on the left of the path. We only run ## thru to the number of cusps - 2 since you cannot start an ## interior path on either of the last two cusps
## s is in the index of the cusp on the right of the path ## A and B are the matrices whose associated paths ## connect cusp1 to cusp2 and cusp2 to cusp1 (respectively) ## A and B are added to our coset reps
## This loop now encodes the relation between the ## unimodular path A and our generators. This is done ## simply by accounting for all of the edges that lie ## below the path attached to A (as they form a triangle) ## Similarly, this is also done for B.
## Running between the cusps between cusp1 and cusp2 ## Add edge relation ## Add negative of edge relation ## Add relations for A and B to relations list
## Make the translation table between the Sage and Geometric ## descriptions of P^1
rels, equiv_ind)
## A list of indices of the (geometric) coset representatives whose ## paths are identified by some 2-torsion element (which switches the ## path orientation)
## A dictionary of (2-torsion in PSL_2(Z)) matrices in ## Gamma_0(N) that give the orientation identification in the ## paths listed in twotor_index above!
## A list of indices of the (geometric) coset representatives that ## form one side of an ideal triangle with an interior fixed point of ## a 3-torsion element of Gamma_0(N)
## A dictionary of (3-torsion in PSL_2(Z)) matrices in ## Gamma_0(N) that give the interior fixed point described in ## threetor_index above!
def _repr_(self): r""" A printable representation of this domain.
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: ManinRelations(11)._repr_() 'Manin Relations of level 11' """
def indices_with_two_torsion(self): r""" Return the indices of coset representatives whose associated unimodular path contains a point fixed by a `\Gamma_0(N)` element of order 2 (where the order is computed in `PSL_2(\ZZ)`).
OUTPUT:
A list of integers.
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: MR = ManinRelations(11) sage: MR.indices_with_two_torsion() [] sage: MR = ManinRelations(13) sage: MR.indices_with_two_torsion() [3, 4] sage: MR.reps(3), MR.reps(4) ( [-1 -1] [-1 -2] [ 3 2], [ 2 3] )
The corresponding matrix of order 2::
sage: A = MR.two_torsion_matrix(MR.reps(3)); A [ 5 2] [-13 -5] sage: A^2 [-1 0] [ 0 -1]
You can see that multiplication by ``A`` just interchanges the rational cusps determined by the columns of the matrix ``MR.reps(3)``::
sage: MR.reps(3), A*MR.reps(3) ( [-1 -1] [ 1 -1] [ 3 2], [-2 3] ) """
def reps_with_two_torsion(self): r""" The coset representatives whose associated unimodular path contains a point fixed by a `\Gamma_0(N)` element of order 2 (where the order is computed in `PSL_2(\ZZ)`).
OUTPUT:
A list of matrices.
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: MR = ManinRelations(11) sage: MR.reps_with_two_torsion() [] sage: MR = ManinRelations(13) sage: MR.reps_with_two_torsion() [ [-1 -1] [-1 -2] [ 3 2], [ 2 3] ] sage: B = MR.reps_with_two_torsion()[0]
The corresponding matrix of order 2::
sage: A = MR.two_torsion_matrix(B); A [ 5 2] [-13 -5] sage: A^2 [-1 0] [ 0 -1]
You can see that multiplication by ``A`` just interchanges the rational cusps determined by the columns of the matrix ``MR.reps(3)``::
sage: B, A*B ( [-1 -1] [ 1 -1] [ 3 2], [-2 3] ) """
def two_torsion_matrix(self, A): r""" Return the matrix of order two in `\Gamma_0(N)` which corresponds to an ``A`` in ``self.reps_with_two_torsion()``.
INPUT:
- ``A`` -- a matrix in ``self.reps_with_two_torsion()``
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: MR = ManinRelations(25) sage: B = MR.reps_with_two_torsion()[0]
The corresponding matrix of order 2::
sage: A = MR.two_torsion_matrix(B); A [ 7 2] [-25 -7] sage: A^2 [-1 0] [ 0 -1] """
def indices_with_three_torsion(self): r""" A list of indices of coset representatives whose associated unimodular path contains a point fixed by a `\Gamma_0(N)` element of order 3 in the ideal triangle directly below that path (the order is computed in `PSL_2(\ZZ)`).
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: MR = ManinRelations(11) sage: MR.indices_with_three_torsion() [] sage: MR = ManinRelations(13) sage: MR.indices_with_three_torsion() [2, 5] sage: B = MR.reps(2); B [ 0 -1] [ 1 3]
The corresponding matrix of order three::
sage: A = MR.three_torsion_matrix(B); A [-4 -1] [13 3] sage: A^3 [1 0] [0 1]
The columns of ``B`` and the columns of ``A*B`` and ``A^2*B`` give the same rational cusps::
sage: B [ 0 -1] [ 1 3] sage: A*B, A^2*B ( [-1 1] [ 1 0] [ 3 -4], [-4 1] ) """
def reps_with_three_torsion(self): r""" A list of coset representatives whose associated unimodular path contains a point fixed by a `\Gamma_0(N)` element of order 3 in the ideal triangle directly below that path (the order is computed in `PSL_2(\ZZ)`).
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: MR = ManinRelations(13) sage: B = MR.reps_with_three_torsion()[0]; B [ 0 -1] [ 1 3]
The corresponding matrix of order three::
sage: A = MR.three_torsion_matrix(B); A [-4 -1] [13 3] sage: A^3 [1 0] [0 1]
The columns of ``B`` and the columns of ``A*B`` and ``A^2*B`` give the same rational cusps::
sage: B [ 0 -1] [ 1 3] sage: A*B, A^2*B ( [-1 1] [ 1 0] [ 3 -4], [-4 1] ) """
def three_torsion_matrix(self, A): r""" Return the matrix of order two in `\Gamma_0(N)` which corresponds to an ``A`` in ``self.reps_with_two_torsion()``.
INPUT:
- ``A`` -- a matrix in ``self.reps_with_two_torsion()``
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: MR = ManinRelations(37) sage: B = MR.reps_with_three_torsion()[0]
The corresponding matrix of order 3::
sage: A = MR.three_torsion_matrix(B); A [-11 -3] [ 37 10] sage: A^3 [1 0] [0 1] """
def form_list_of_cusps(self): r""" Return the intersection of a fundamental domain for `\Gamma_0(N)` with the real axis.
The construction of this fundamental domain follows the arguments of [PS2011]_ Section 2. The boundary of this fundamental domain consists entirely of unimodular paths when `\Gamma_0(N)` has no elements of order 3. (See [PS2011]_ Section 2.5 for the case when there are elements of order 3.)
OUTPUT:
A sorted list of rational numbers marking the intersection of a fundamental domain for `\Gamma_0(N)` with the real axis.
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: A = ManinRelations(11) sage: A.form_list_of_cusps() [-1, -2/3, -1/2, -1/3, 0] sage: A = ManinRelations(13) sage: A.form_list_of_cusps() [-1, -2/3, -1/2, -1/3, 0] sage: A = ManinRelations(101) sage: A.form_list_of_cusps() [-1, -6/7, -5/6, -4/5, -7/9, -3/4, -11/15, -8/11, -5/7, -7/10, -9/13, -2/3, -5/8, -13/21, -8/13, -3/5, -7/12, -11/19, -4/7, -1/2, -4/9, -3/7, -5/12, -7/17, -2/5, -3/8, -4/11, -1/3, -2/7, -3/11, -1/4, -2/9, -1/5, -1/6, 0] """ ## Get the level
## Some convenient shortcuts
## Initialize some lists
## Initialize the list of cusps at the bottom of the fund. domain. ## The ? denotes that it has not yet been checked if more cusps need ## to be added between the surrounding cusps.
## This initializes a list indexed by P^1(Z/NZ) which keeps track of ## which right coset representatives we've found for Gamma_0(N)/SL_2(Z) ## thru the construction of a fundamental domain
## Includeds the coset repns formed by the original ideal triangle ## (with corners at -1, 0, infty)
## Main Loop -- Ideal Triangle Flipping ## ====================================
## This loop runs through the current set of cusps ## and checks to see if more cusps should be added ## ----------------------------------------------- # final list C
## Single out our two cusps (path from cusp2 to cusp1)
## Makes the unimodular transform for the path from cusp2 ## to cusp1
## This is the point where it is determined whether ## or not the adjacent triangle should be added ## ------------------------------------------------ ## row of our unimodular ## transformation gam
## Check if we need to flip (since this P1 element has not ## yet been accounted for!) ## Say that the other two ideal triangle edges ## also occur!
## Check to see if this triangle contains a fixed ## point by an element of Gamma_0(N). If such an ## element is present, the fundamental domain can be ## extended no further.
## this congruence is exactly equivalent to ## gam * [0 -1; 1 -1] * gam^(-1) is in Gamma_0(N) ## where gam is the matrix corresponding to the ## unimodular path connecting cusp1 to cusp2
## indicating that a new cusp needs to ## be inserted here else: # more checking below is needed! =) else: ## checking below is needed! =)
## Now insert the missing cusps (where there is an 'i' in ## the final list) ## This will keep the fundamental domain as flat as possible! ## ---------------------------------------------------------------
## Single out our two cusps (path from cusp2 to cusp1)
## Makes the unimodular transform for the path ## from cusp2 to cusp1
## Inserts the Farey center of these two cusps!
## Remove the (now superfluous) extra string characters that appear ## in the odd list entries
def is_unimodular_path(self, r1, r2): r""" Determine whether two (non-infinite) cusps are connected by a unimodular path.
INPUT:
- ``r1, r2`` -- rational numbers
OUTPUT:
A boolean expressing whether or not a unimodular path connects ``r1`` to ``r2``.
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: A = ManinRelations(11) sage: A.is_unimodular_path(0,1/3) True sage: A.is_unimodular_path(1/3,0) True sage: A.is_unimodular_path(0,2/3) False sage: A.is_unimodular_path(2/3,0) False """
def unimod_to_matrices(self, r1, r2): r""" Return the two matrices whose associated unimodular paths connect ``r1`` and ``r2`` and ``r2`` and ``r1``, respectively.
INPUT:
- ``r1, r2`` -- rational numbers (that are assumed to be connected by a unimodular path)
OUTPUT:
A pair of `2 \times 2` matrices of determinant 1
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: A = ManinRelations(11) sage: A.unimod_to_matrices(0,1/3) ( [ 0 1] [1 0] [-1 3], [3 1] ) """ else:
def fd_boundary(self, C): r""" Find matrices whose associated unimodular paths give the boundary of a fundamental domain.
Here the fundamental domain is for `\Gamma_0(N)`. (In the case when `\Gamma_0(N)` has elements of order three the shape cut out by these unimodular matrices is a little smaller than a fundamental domain. See Section 2.5 of [PS2011]_.)
INPUT:
- ``C`` -- a list of rational numbers coming from ``self.form_list_of_cusps()``
OUTPUT:
A list of `2 \times 2` integer matrices of determinant 1 whose associated unimodular paths give the boundary of a fundamental domain for `\Gamma_0(N)` (or nearly so in the case of 3-torsion).
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: A = ManinRelations(11) sage: C = A.form_list_of_cusps(); C [-1, -2/3, -1/2, -1/3, 0] sage: A.fd_boundary(C) [ [1 0] [ 1 1] [ 0 -1] [-1 -1] [-1 -2] [-2 -1] [0 1], [-1 0], [ 1 3], [ 3 2], [ 2 3], [ 3 1] ] sage: A = ManinRelations(13) sage: C = A.form_list_of_cusps(); C [-1, -2/3, -1/2, -1/3, 0] sage: A.fd_boundary(C) [ [1 0] [ 1 1] [ 0 -1] [-1 -1] [-1 -2] [-2 -1] [0 1], [-1 0], [ 1 3], [ 3 2], [ 2 3], [ 3 1] ] sage: A = ManinRelations(101) sage: C = A.form_list_of_cusps(); C [-1, -6/7, -5/6, -4/5, -7/9, -3/4, -11/15, -8/11, -5/7, -7/10, -9/13, -2/3, -5/8, -13/21, -8/13, -3/5, -7/12, -11/19, -4/7, -1/2, -4/9, -3/7, -5/12, -7/17, -2/5, -3/8, -4/11, -1/3, -2/7, -3/11, -1/4, -2/9, -1/5, -1/6, 0] sage: A.fd_boundary(C) [ [1 0] [ 1 1] [ 0 -1] [-1 -1] [-1 -2] [-2 -1] [-1 -3] [-3 -2] [0 1], [-1 0], [ 1 6], [ 6 5], [ 5 9], [ 9 4], [ 4 11], [11 7], <BLANKLINE> [-2 -1] [-1 -4] [-4 -3] [-3 -2] [-2 -7] [-7 -5] [-5 -3] [-3 -4] [ 7 3], [ 3 11], [11 8], [ 8 5], [ 5 17], [17 12], [12 7], [ 7 9], <BLANKLINE> [-4 -1] [-1 -4] [ -4 -11] [-11 -7] [-7 -3] [-3 -8] [ -8 -13] [ 9 2], [ 2 7], [ 7 19], [ 19 12], [12 5], [ 5 13], [ 13 21], <BLANKLINE> [-13 -5] [-5 -2] [-2 -9] [-9 -7] [-7 -5] [-5 -8] [ -8 -11] [ 21 8], [ 8 3], [ 3 13], [13 10], [10 7], [ 7 11], [ 11 15], <BLANKLINE> [-11 -3] [-3 -7] [-7 -4] [-4 -5] [-5 -6] [-6 -1] [ 15 4], [ 4 9], [ 9 5], [ 5 6], [ 6 7], [ 7 1] ] """
## These matrices correspond to the paths from infty to 0 and ## -1 to infty
## Now find SL_2(Z) matrices whose associated unimodular paths ## connect the cusps listed in C.
@cached_method def prep_hecke_on_gen(self, l, gen, modulus=None): r""" This function does some precomputations needed to compute `T_l`.
In particular, if `\phi` is a modular symbol and `D_m` is the divisor associated to the generator ``gen``, to compute `(\phi|T_{l})(D_m)` one needs to compute `\phi(\gamma_a D_m)|\gamma_a` where `\gamma_a` runs through the `l+1` matrices defining `T_l`. One then takes `\gamma_a D_m` and writes it as a sum of unimodular divisors. For each such unimodular divisor, say `[M]` where `M` is a `SL_2` matrix, we then write `M=\gamma h` where `\gamma` is in `\Gamma_0(N)` and `h` is one of our chosen coset representatives. Then `\phi([M]) = \phi([h]) | \gamma^{-1}`. Thus, one has
.. MATH::
(\phi | \gamma_a)(D_m) = \sum_h \sum_j \phi([h]) | \gamma_{hj}^{-1} \cdot \gamma_a
as `h` runs over all coset representatives and `j` simply runs over however many times `M_h` appears in the above computation.
Finally, the output of this function is a dictionary ``D`` whose keys are the coset representatives in ``self.reps()`` where each value is a list of matrices, and the entries of ``D`` satisfy:
.. MATH::
D[h][j] = \gamma_{hj} * \gamma_a
INPUT:
- ``l`` -- a prime - ``gen`` -- a generator
OUTPUT:
A list of lists (see above).
EXAMPLES::
sage: E = EllipticCurve('11a') sage: phi = E.pollack_stevens_modular_symbol() sage: phi.values() [-1/5, 1, 0] sage: M = phi.parent().source() sage: w = M.prep_hecke_on_gen(2, M.gens()[0]) sage: one = Matrix(ZZ,2,2,1) sage: one.set_immutable() sage: w[one] [[1 0] [0 2], [1 1] [0 2], [2 0] [0 1]] """
# this will be the dictionary D above enumerated by coset reps
# This loop will run thru the l+1 (or l) matrices # defining T_l of the form [1, a, 0, l] and carry out the # computation described above. # ------------------------------------- # if the level is not prime to l the matrix [l, 0, 0, 1] is avoided. # In the notation above this is gam_a * D_m # This expresses t as a sum of unimodular divisors
# This loop runs over each such unimodular divisor # ------------------------------------------------ # B is the coset rep equivalent to A # gaminv = B*A^(-1), but A is in SL2. # The matrix gaminv * gamma is added to our list in the j-th slot # (as described above)
@cached_method def prep_hecke_on_gen_list(self, l, gen, modulus=None): r""" Return the precomputation to compute `T_l` in a way that speeds up the Hecke calculation.
Namely, returns a list of the form [h,A].
INPUT:
- ``l`` -- a prime - ``gen`` -- a generator
OUTPUT:
A list of lists (see above).
EXAMPLES::
sage: E = EllipticCurve('11a') sage: phi = E.pollack_stevens_modular_symbol() sage: phi.values() [-1/5, 1, 0] sage: M = phi.parent().source() sage: len(M.prep_hecke_on_gen_list(2, M.gens()[0])) 4 """
def basic_hecke_matrix(a, l): r""" Return the `2 \times 2` matrix with entries ``[1, a, 0, l]`` if ``a<l`` and ``[l, 0, 0, 1]`` if ``a>=l``.
INPUT:
- `a` -- an integer or Infinity - ``l`` -- a prime
OUTPUT:
A `2 \times 2` matrix of determinant l
EXAMPLES::
sage: from sage.modular.pollack_stevens.fund_domain import basic_hecke_matrix sage: basic_hecke_matrix(0, 7) [1 0] [0 7] sage: basic_hecke_matrix(5, 7) [1 5] [0 7] sage: basic_hecke_matrix(7, 7) [7 0] [0 1] sage: basic_hecke_matrix(19, 7) [7 0] [0 1] sage: basic_hecke_matrix(infinity, 7) [7 0] [0 1] """ else: |