Ejemplo n.º 1
0
    def _dim_new_eisenstein(self):
        """
        Compute the dimension of the Eisenstein submodule.

        EXAMPLES::

            sage: m = ModularForms(Gamma0(11), 4)
            sage: m._dim_new_eisenstein()
            0
            sage: m = ModularForms(Gamma0(11), 2)
            sage: m._dim_new_eisenstein()
            1
        """
        try:
            return self.__the_dim_new_eisenstein
        except AttributeError:
            if arithgroup.is_Gamma0(self.group()) and self.weight() == 2:
                if rings.is_prime(self.level()):
                    d = 1
                else:
                    d = 0
            else:
                E = self.eisenstein_series()
                d = len([g for g in E if g.new_level() == self.level()])
            self.__the_dim_new_eisenstein = d
        return self.__the_dim_new_eisenstein
Ejemplo n.º 2
0
    def _dim_new_eisenstein(self):
        """
        Return the dimension of the new Eisenstein subspace, computed
        by enumerating all Eisenstein series of the appropriate level.

        EXAMPLES::

            sage: m = ModularForms(Gamma0(11), 4)
            sage: m._dim_new_eisenstein()
            0
            sage: m = ModularForms(Gamma0(11), 2)
            sage: m._dim_new_eisenstein()
            1
            sage: m = ModularForms(DirichletGroup(36).0,5); m
            Modular Forms space of dimension 28, character [-1, 1] and weight 5 over Rational Field
            sage: m._dim_new_eisenstein()
            2
            sage: m._dim_eisenstein()
            8
        """
        if arithgroup.is_Gamma0(self.group()) and self.weight() == 2:
            if is_prime(self.level()):
                d = 1
            else:
                d = 0
        else:
            E = self.eisenstein_series()
            d = len([g for g in E if g.new_level() == self.level()])
        return d
Ejemplo n.º 3
0
    def __init__(self, group, weight, base_ring, character=None):
        """
        Create an ambient space of modular forms.
        
        EXAMPLES::
        
            sage: m = ModularForms(Gamma1(20),20); m
            Modular Forms space of dimension 238 for Congruence Subgroup Gamma1(20) of weight 20 over Rational Field
            sage: m.is_ambient()
            True
        """
        if not arithgroup.is_CongruenceSubgroup(group):
            raise TypeError, 'group (=%s) must be a congruence subgroup' % group
        weight = rings.Integer(weight)

        if character is None and arithgroup.is_Gamma0(group):
            character = dirichlet.TrivialCharacter(group.level(), base_ring)

        space.ModularFormsSpace.__init__(self, group, weight, character,
                                         base_ring)
        try:
            d = self.dimension()
        except NotImplementedError:
            d = None
        hecke.AmbientHeckeModule.__init__(self, base_ring, d, group.level(),
                                          weight)
Ejemplo n.º 4
0
    def newform_decomposition(self, names=None):
        """
        Return the newforms of the simple subvarieties in the decomposition of
        self as a product of simple subvarieties, up to isogeny.

        OUTPUT:

        - an array of newforms

        EXAMPLES::

            sage: J0(81).newform_decomposition('a')
            [q - 2*q^4 + O(q^6), q - 2*q^4 + O(q^6), q + a0*q^2 + q^4 - a0*q^5 + O(q^6)]

            sage: J1(19).newform_decomposition('a')
            [q - 2*q^3 - 2*q^4 + 3*q^5 + O(q^6),
             q + a1*q^2 + (-1/9*a1^5 - 1/3*a1^4 - 1/3*a1^3 + 1/3*a1^2 - a1 - 1)*q^3 + (4/9*a1^5 + 2*a1^4 + 14/3*a1^3 + 17/3*a1^2 + 6*a1 + 2)*q^4 + (-2/3*a1^5 - 11/3*a1^4 - 10*a1^3 - 14*a1^2 - 15*a1 - 9)*q^5 + O(q^6)]
        """
        if self.dimension() == 0:
            return []
        G = self.group()
        if not (is_Gamma0(G) or is_Gamma1(G)):
            return [S.newform(names=names) for S in self.decomposition()]
        Gtype = G.parent()
        N = G.level()
        preans = [
            Newforms(Gtype(d), names=names) * len((N // d).divisors())
            for d in N.divisors()
        ]
        return [newform for l in preans for newform in l]
Ejemplo n.º 5
0
    def divisor_of_order(self):
        """
        Return a divisor of the order of this torsion subgroup of a modular
        abelian variety.

        OUTPUT:

        A divisor of this torsion subgroup.

        EXAMPLES::

            sage: t = J0(37)[1].rational_torsion_subgroup()
            sage: t.divisor_of_order()
            3

            sage: J = J1(19)
            sage: J.rational_torsion_subgroup().divisor_of_order()
            4383

        """
        try:
            return self._divisor_of_order
        except:
            pass

        A = self.abelian_variety()
        N = A.level()

        if A.dimension() == 0:
            self._divisor_of_order = ZZ(1)
            return self._divisor_of_order

        # return the order of the cuspidal subgroup in the J0(p) case
        if A.is_J0() and N.is_prime():
            self._divisor_of_order = QQ((A.level() - 1) / 12).numerator()
            return self._divisor_of_order

        # The elliptic curve case
        if A.dimension() == 1:
            self._divisor_of_order = A.elliptic_curve().torsion_order()
            return self._divisor_of_order

        # The J1(p) case
        if A.is_J1() and N.is_prime():
            epsilons = [
                epsilon for epsilon in DirichletGroup(N)
                if not epsilon.is_trivial() and epsilon.is_even()
            ]
            bernoullis = [epsilon.bernoulli(2) for epsilon in epsilons]
            self._divisor_of_order = ZZ(N / (2**(N - 3)) * prod(bernoullis))
            return self._divisor_of_order

        # The Gamma0 case
        if all(is_Gamma0(G) for G in A.groups()):
            self._divisor_of_order = A.rational_cusp_subgroup().order()
            return self._divisor_of_order

        # Unhandled case
        self._divisor_of_order = ZZ(1)
        return self._divisor_of_order
Ejemplo n.º 6
0
    def label(self) -> str:
        """
        Return canonical label that defines this newform modular
        abelian variety.

        OUTPUT:

        string

        EXAMPLES::

            sage: A = AbelianVariety('43b')
            sage: A.label()
            '43b'
        """
        G = self.__f.group()
        if is_Gamma0(G):
            group = ''
        elif is_Gamma1(G):
            group = 'G1'
        elif is_GammaH(G):
            group = 'GH[' + ','.join(str(z)
                                     for z in G._generators_for_H()) + ']'
        return '%s%s%s' % (self.level(),
                           cremona_letter_code(self.factor_number()), group)
Ejemplo n.º 7
0
    def _dim_new_eisenstein(self):
        """
        Return the dimension of the new Eisenstein subspace, computed
        by enumerating all Eisenstein series of the appropriate level.

        EXAMPLES::

            sage: m = ModularForms(Gamma0(11), 4)
            sage: m._dim_new_eisenstein()
            0
            sage: m = ModularForms(Gamma0(11), 2)
            sage: m._dim_new_eisenstein()
            1
            sage: m = ModularForms(DirichletGroup(36).0,5); m
            Modular Forms space of dimension 28, character [-1, 1] and weight 5 over Rational Field
            sage: m._dim_new_eisenstein()
            2
            sage: m._dim_eisenstein()
            8
        """
        if arithgroup.is_Gamma0(self.group()) and self.weight() == 2:
            if is_prime(self.level()):
                d = 1
            else:
                d = 0
        else:
            E = self.eisenstein_series()
            d = len([g for g in E if g.new_level() == self.level()])
        return d
    def newform_decomposition(self, names=None):
        """
        Return the newforms of the simple subvarieties in the decomposition of
        self as a product of simple subvarieties, up to isogeny.

        OUTPUT:

        - an array of newforms

        EXAMPLES::

            sage: J0(81).newform_decomposition('a')
            [q - 2*q^4 + O(q^6), q - 2*q^4 + O(q^6), q + a0*q^2 + q^4 - a0*q^5 + O(q^6)]

            sage: J1(19).newform_decomposition('a')
            [q - 2*q^3 - 2*q^4 + 3*q^5 + O(q^6),
             q + a1*q^2 + (-1/9*a1^5 - 1/3*a1^4 - 1/3*a1^3 + 1/3*a1^2 - a1 - 1)*q^3 + (4/9*a1^5 + 2*a1^4 + 14/3*a1^3 + 17/3*a1^2 + 6*a1 + 2)*q^4 + (-2/3*a1^5 - 11/3*a1^4 - 10*a1^3 - 14*a1^2 - 15*a1 - 9)*q^5 + O(q^6)]
        """
        if self.dimension() == 0:
            return []
        G = self.group()
        if not (is_Gamma0(G) or is_Gamma1(G)):
            return [S.newform(names=names) for S in self.decomposition()]
        Gtype = G.parent()
        N = G.level()
        preans = [Newforms(Gtype(d), names=names) *
                  len(Integer(N/d).divisors()) for d in N.divisors()]
        ans = [newform for l in preans for newform in l]
        return ans
Ejemplo n.º 9
0
 def _dim_new_eisenstein(self):
     """
     Compute the dimension of the Eisenstein submodule.
     
     EXAMPLES::
     
         sage: m = ModularForms(Gamma0(11), 4)
         sage: m._dim_new_eisenstein()
         0
         sage: m = ModularForms(Gamma0(11), 2)
         sage: m._dim_new_eisenstein()
         1
     """
     try:
         return self.__the_dim_new_eisenstein
     except AttributeError:
         if arithgroup.is_Gamma0(self.group()) and self.weight() == 2:
             if rings.is_prime(self.level()):
                 d = 1
             else:
                 d = 0
         else:
             E = self.eisenstein_series()
             d = len([g for g in E if g.new_level() == self.level()])
         self.__the_dim_new_eisenstein = d
     return self.__the_dim_new_eisenstein
Ejemplo n.º 10
0
    def __init__(self, group, weight, base_ring, character=None, eis_only=False):
        """
        Create an ambient space of modular forms.

        EXAMPLES::

            sage: m = ModularForms(Gamma1(20),20); m
            Modular Forms space of dimension 238 for Congruence Subgroup Gamma1(20) of weight 20 over Rational Field
            sage: m.is_ambient()
            True
        """
        if not arithgroup.is_CongruenceSubgroup(group):
            raise TypeError('group (=%s) must be a congruence subgroup'%group)
        weight = rings.Integer(weight)

        if character is None and arithgroup.is_Gamma0(group):
            character = dirichlet.TrivialCharacter(group.level(), base_ring)

        self._eis_only=eis_only
        space.ModularFormsSpace.__init__(self, group, weight, character, base_ring)
        if eis_only:
            d = self._dim_eisenstein()
        else:
            d = self.dimension()
        hecke.AmbientHeckeModule.__init__(self, base_ring, d, group.level(), weight)
Ejemplo n.º 11
0
 def __init__(self,
              group = arithgroup.Gamma0(1),
              weight = 2,
              sign = 0,
              base_ring = rings.QQ,
              character = None):
     """
     Space of boundary symbols for a congruence subgroup of SL_2(Z).
     
     This class is an abstract base class, so only derived classes
     should be instantiated.
     
     INPUT:
     
     
     -  ``weight`` - int, the weight
     
     -  ``group`` - arithgroup.congroup_generic.CongruenceSubgroup, a congruence
        subgroup.
     
     -  ``sign`` - int, either -1, 0, or 1
     
     -  ``base_ring`` - rings.Ring (defaults to the
        rational numbers)
     
     
     EXAMPLES::
     
         sage: B = ModularSymbols(Gamma0(11),2).boundary_space()
         sage: isinstance(B, sage.modular.modsym.boundary.BoundarySpace)
         True
         sage: B == loads(dumps(B))
         True
     """
     weight = int(weight)
     if weight <= 1:
         raise ArithmeticError, "weight must be at least 2"
     if not arithgroup.is_CongruenceSubgroup(group):
         raise TypeError, "group must be a congruence subgroup"
     sign = int(sign)
     if not isinstance(base_ring, rings.Ring) and rings.is_CommutativeRing(base_ring):
         raise TypeError, "base_ring must be a commutative ring"
     if character == None and arithgroup.is_Gamma0(group):
         character = dirichlet.TrivialCharacter(group.level(), base_ring)
     (self.__group, self.__weight, self.__character,
       self.__sign, self.__base_ring) = (group, weight,
                                          character, sign, base_ring)
     self._known_gens = []
     self._known_gens_repr = []
     self._is_zero = []
     hecke.HeckeModule_generic.__init__(self, base_ring, group.level())
Ejemplo n.º 12
0
    def __init__(self,
                 group = arithgroup.Gamma0(1),
                 weight = 2,
                 sign = 0,
                 base_ring = rings.QQ,
                 character = None):
        """
        Space of boundary symbols for a congruence subgroup of SL_2(Z).

        This class is an abstract base class, so only derived classes
        should be instantiated.

        INPUT:


        -  ``weight`` - int, the weight

        -  ``group`` - arithgroup.congroup_generic.CongruenceSubgroup, a congruence
           subgroup.

        -  ``sign`` - int, either -1, 0, or 1

        -  ``base_ring`` - rings.Ring (defaults to the
           rational numbers)


        EXAMPLES::

            sage: B = ModularSymbols(Gamma0(11),2).boundary_space()
            sage: isinstance(B, sage.modular.modsym.boundary.BoundarySpace)
            True
            sage: B == loads(dumps(B))
            True
        """
        weight = int(weight)
        if weight <= 1:
            raise ArithmeticError("weight must be at least 2")
        if not arithgroup.is_CongruenceSubgroup(group):
            raise TypeError("group must be a congruence subgroup")
        sign = int(sign)
        if not isinstance(base_ring, rings.Ring) and rings.is_CommutativeRing(base_ring):
            raise TypeError("base_ring must be a commutative ring")
        if character is None and arithgroup.is_Gamma0(group):
            character = dirichlet.TrivialCharacter(group.level(), base_ring)
        (self.__group, self.__weight, self.__character,
          self.__sign, self.__base_ring) = (group, weight,
                                             character, sign, base_ring)
        self._known_gens = []
        self._known_gens_repr = []
        self._is_zero = []
        hecke.HeckeModule_generic.__init__(self, base_ring, group.level())
Ejemplo n.º 13
0
    def _p_stabilize_parent_space(self, p, new_base_ring):
        r"""
        Return the space of Pollack-Stevens modular symbols of level
        `p N`, with changed base ring.  This is used internally when
        constructing the `p`-stabilization of a modular symbol.

        INPUT:

        - ``p`` -- prime number
        - ``new_base_ring`` -- the base ring of the result

        OUTPUT:

        The space of modular symbols of level `p N`, where `N` is the level
        of this space.

        EXAMPLES::

            sage: D = OverconvergentDistributions(2, 7)
            sage: M = PollackStevensModularSymbols(Gamma(13), coefficients=D)
            sage: M._p_stabilize_parent_space(7, M.base_ring())
            Space of overconvergent modular symbols for Congruence Subgroup
            Gamma(91) with sign 0 and values in Space of 7-adic distributions
            with k=2 action and precision cap 20

            sage: D = OverconvergentDistributions(4, 17)
            sage: M = PollackStevensModularSymbols(Gamma1(3), coefficients=D)
            sage: M._p_stabilize_parent_space(17, Qp(17))
            Space of overconvergent modular symbols for Congruence
            Subgroup Gamma1(51) with sign 0 and values in Space of
            17-adic distributions with k=4 action and precision cap 20
        """
        N = self.level()
        if N % p == 0:
            raise ValueError("the level is not prime to p")
        from sage.modular.arithgroup.all import (Gamma, is_Gamma, Gamma0,
                                                 is_Gamma0, Gamma1, is_Gamma1)
        G = self.group()
        if is_Gamma0(G):
            G = Gamma0(N * p)
        elif is_Gamma1(G):
            G = Gamma1(N * p)
        elif is_Gamma(G):
            G = Gamma(N * p)
        else:
            raise NotImplementedError
        return PollackStevensModularSymbols(
            G,
            coefficients=self.coefficient_module().change_ring(new_base_ring),
            sign=self.sign())
Ejemplo n.º 14
0
    def _p_stabilize_parent_space(self, p, new_base_ring):
        r"""
        Return the space of Pollack-Stevens modular symbols of level
        `p N`, with changed base ring.  This is used internally when
        constructing the `p`-stabilization of a modular symbol.

        INPUT:

        - ``p`` -- prime number
        - ``new_base_ring`` -- the base ring of the result

        OUTPUT:

        The space of modular symbols of level `p N`, where `N` is the level
        of this space.

        EXAMPLES::

            sage: D = OverconvergentDistributions(2, 7)
            sage: M = PollackStevensModularSymbols(Gamma(13), coefficients=D)
            sage: M._p_stabilize_parent_space(7, M.base_ring())
            Space of overconvergent modular symbols for Congruence Subgroup
            Gamma(91) with sign 0 and values in Space of 7-adic distributions
            with k=2 action and precision cap 20

            sage: D = OverconvergentDistributions(4, 17)
            sage: M = PollackStevensModularSymbols(Gamma1(3), coefficients=D)
            sage: M._p_stabilize_parent_space(17, Qp(17))
            Space of overconvergent modular symbols for Congruence
            Subgroup Gamma1(51) with sign 0 and values in Space of
            17-adic distributions with k=4 action and precision cap 20
        """
        N = self.level()
        if N % p == 0:
            raise ValueError("the level is not prime to p")
        from sage.modular.arithgroup.all import (Gamma, is_Gamma, Gamma0,
                                                 is_Gamma0, Gamma1, is_Gamma1)
        G = self.group()
        if is_Gamma0(G):
            G = Gamma0(N * p)
        elif is_Gamma1(G):
            G = Gamma1(N * p)
        elif is_Gamma(G):
            G = Gamma(N * p)
        else:
            raise NotImplementedError
        return PollackStevensModularSymbols(G, coefficients=self.coefficient_module().change_ring(new_base_ring), sign=self.sign())
Ejemplo n.º 15
0
    def divisor_of_order(self):
        """
        Return a divisor of the order of this torsion subgroup of a modular
        abelian variety.

        OUTPUT:

        A divisor of this torsion subgroup.

        EXAMPLES::

            sage: from sage_modabvar import J0
            sage: t = J0(37)[1].rational_torsion_subgroup()
            sage: t.divisor_of_order()
            3

            sage: from sage_modabvar import J1
            sage: J = J1(19)
            sage: J.rational_torsion_subgroup().divisor_of_order()
            4383

        """
        A = self.abelian_variety()
        N = A.level()
        if A.dimension() == 0:
            return ZZ(1)

        # The J1(p) case
        if A.is_J1() and N.is_prime():
            epsilons = [epsilon for epsilon in DirichletGroup(N)
                        if not epsilon.is_trivial() and epsilon.is_even()]
            bernoullis = [epsilon.bernoulli(2) for epsilon in epsilons]
            return ZZ(N/(2**(N-3))*prod(bernoullis))

        if all(is_Gamma0(G) for G in A.groups()):
            R = A.rational_cusp_subgroup()
            return R.order()

        return ZZ(1)
Ejemplo n.º 16
0
    def label(self):
        """
        Return canonical label that defines this newform modular
        abelian variety.

        OUTPUT:
            string

        EXAMPLES::

            sage: A = AbelianVariety('43b')
            sage: A.label()
            '43b'
        """
        G = self.__f.group()
        if is_Gamma0(G):
            group = ''
        elif is_Gamma1(G):
            group = 'G1'
        elif is_GammaH(G):
            group = 'GH[' + ','.join([str(z) for z in G._generators_for_H()]) + ']'
        return '%s%s%s'%(self.level(), cremona_letter_code(self.factor_number()), group)
Ejemplo n.º 17
0
    def divisor_of_order(self):
        """
        Return a divisor of the order of this torsion subgroup of a modular
        abelian variety.

        OUTPUT:

        A divisor of this torsion subgroup.

        EXAMPLES::

            sage: t = J0(37)[1].rational_torsion_subgroup()
            sage: t.divisor_of_order()
            3

            sage: J = J1(19)
            sage: J.rational_torsion_subgroup().divisor_of_order()
            4383

            sage: J = J0(45)
            sage: J.rational_cusp_subgroup().order()
            32
            sage: J.rational_cuspidal_subgroup().order()
            64
            sage: J.rational_torsion_subgroup().divisor_of_order()
            64
        """
        try:
            return self._divisor_of_order
        except AttributeError:
            pass

        A = self.abelian_variety()
        N = A.level()

        if A.dimension() == 0:
            self._divisor_of_order = ZZ(1)
            return self._divisor_of_order

        # return the order of the cuspidal subgroup in the J0(p) case
        if A.is_J0() and N.is_prime():
            self._divisor_of_order = QQ((A.level()-1)/12).numerator()
            return self._divisor_of_order

        # The elliptic curve case
        if A.dimension() == 1:
            self._divisor_of_order = A.elliptic_curve().torsion_order()
            return self._divisor_of_order

        # The J1(p) case
        if A.is_J1() and N.is_prime():
            epsilons = [epsilon for epsilon in DirichletGroup(N)
                        if not epsilon.is_trivial() and epsilon.is_even()]
            bernoullis = [epsilon.bernoulli(2) for epsilon in epsilons]
            self._divisor_of_order = ZZ(N/(2**(N-3))*prod(bernoullis))
            return self._divisor_of_order

        # The Gamma0 case
        if all(is_Gamma0(G) for G in A.groups()):
            self._divisor_of_order = A.rational_cuspidal_subgroup().order()
            return self._divisor_of_order

        # Unhandled case
        self._divisor_of_order = ZZ(1)
        return self._divisor_of_order
Ejemplo n.º 18
0
    def multiple_of_order(self, maxp=None, proof=True):
        """
        Return a multiple of the order.

        INPUT:

        - ``proof`` -- a boolean (default: True)

        The computation of the rational torsion order of J1(p) is conjectural
        and will only be used if proof=False. See Section 6.2.3 of [CES2003]_.

        EXAMPLES::

            sage: J = J1(11); J
            Abelian variety J1(11) of dimension 1
            sage: J.rational_torsion_subgroup().multiple_of_order()
            5

            sage: J = J0(17)
            sage: J.rational_torsion_subgroup().order()
            4

        This is an example where proof=False leads to a better bound and better
        performance. ::

            sage: J = J1(23)
            sage: J.rational_torsion_subgroup().multiple_of_order() # long time (2s)
            9406793
            sage: J.rational_torsion_subgroup().multiple_of_order(proof=False)
            408991
        """

        try:
            if proof:
                return self._multiple_of_order
            else:
                return self._multiple_of_order_proof_false
        except AttributeError:
            pass

        A = self.abelian_variety()
        N = A.level()

        if A.dimension() == 0:
            self._multiple_of_order = ZZ(1)
            self._multiple_of_order_proof_false = self._multiple_of_order
            return self._multiple_of_order

        # return the order of the cuspidal subgroup in the J0(p) case
        if A.is_J0() and N.is_prime():
            self._multiple_of_order = QQ((A.level()-1)/12).numerator()
            self._multiple_of_order_proof_false = self._multiple_of_order
            return self._multiple_of_order

        # The elliptic curve case
        if A.dimension() == 1:
            self._multiple_of_order = A.elliptic_curve().torsion_order()
            self._multiple_of_order_proof_false = self._multiple_of_order
            return self._multiple_of_order

        # The conjectural J1(p) case
        if not proof and A.is_J1() and N.is_prime():
            epsilons = [epsilon for epsilon in DirichletGroup(N)
                        if not epsilon.is_trivial() and epsilon.is_even()]
            bernoullis = [epsilon.bernoulli(2) for epsilon in epsilons]
            self._multiple_of_order_proof_false = ZZ(N/(2**(N-3))*prod(bernoullis))
            return self._multiple_of_order_proof_false

        # The Gamma0 and Gamma1 case
        if all((is_Gamma0(G) or is_Gamma1(G) for G in A.groups())):
            self._multiple_of_order = self.multiple_of_order_using_frobp()
            return self._multiple_of_order

        # Unhandled case
        raise NotImplementedError("No implemented algorithm")
Ejemplo n.º 19
0
def ModularForms(group  = 1,
                 weight = 2,
                 base_ring = None,
                 use_cache = True,
                 prec = defaults.DEFAULT_PRECISION):
    r"""
    Create an ambient space of modular forms.

    INPUT:


    -  ``group`` - A congruence subgroup or a Dirichlet
       character eps.

    -  ``weight`` - int, the weight, which must be an
       integer = 1.

    -  ``base_ring`` - the base ring (ignored if group is
       a Dirichlet character)


    Create using the command ModularForms(group, weight, base_ring)
    where group could be either a congruence subgroup or a Dirichlet
    character.

    EXAMPLES: First we create some spaces with trivial character::

        sage: ModularForms(Gamma0(11),2).dimension()
        2
        sage: ModularForms(Gamma0(1),12).dimension()
        2

    If we give an integer N for the congruence subgroup, it defaults to
    `\Gamma_0(N)`::

        sage: ModularForms(1,12).dimension()
        2
        sage: ModularForms(11,4)
        Modular Forms space of dimension 4 for Congruence Subgroup Gamma0(11) of weight 4 over Rational Field

    We create some spaces for `\Gamma_1(N)`.

    ::

        sage: ModularForms(Gamma1(13),2)
        Modular Forms space of dimension 13 for Congruence Subgroup Gamma1(13) of weight 2 over Rational Field
        sage: ModularForms(Gamma1(13),2).dimension()
        13
        sage: [ModularForms(Gamma1(7),k).dimension() for k in [2,3,4,5]]
        [5, 7, 9, 11]
        sage: ModularForms(Gamma1(5),11).dimension()
        12

    We create a space with character::

        sage: e = (DirichletGroup(13).0)^2
        sage: e.order()
        6
        sage: M = ModularForms(e, 2); M
        Modular Forms space of dimension 3, character [zeta6] and weight 2 over Cyclotomic Field of order 6 and degree 2
        sage: f = M.T(2).charpoly('x'); f
        x^3 + (-2*zeta6 - 2)*x^2 - 2*zeta6*x + 14*zeta6 - 7
        sage: f.factor()
        (x - zeta6 - 2) * (x - 2*zeta6 - 1) * (x + zeta6 + 1)

    We can also create spaces corresponding to the groups `\Gamma_H(N)` intermediate
    between `\Gamma_0(N)` and `\Gamma_1(N)`::

        sage: G = GammaH(30, [11])
        sage: M = ModularForms(G, 2); M
        Modular Forms space of dimension 20 for Congruence Subgroup Gamma_H(30) with H generated by [11] of weight 2 over Rational Field
        sage: M.T(7).charpoly().factor()  # long time (7s on sage.math, 2011)
        (x + 4) * x^2 * (x - 6)^4 * (x + 6)^4 * (x - 8)^7 * (x^2 + 4)

    More examples of spaces with character::

        sage: e = DirichletGroup(5, RationalField()).gen(); e
        Dirichlet character modulo 5 of conductor 5 mapping 2 |--> -1

        sage: m = ModularForms(e, 2); m
        Modular Forms space of dimension 2, character [-1] and weight 2 over Rational Field
        sage: m == loads(dumps(m))
        True
        sage: m.T(2).charpoly('x')
        x^2 - 1
        sage: m = ModularForms(e, 6); m.dimension()
        4
        sage: m.T(2).charpoly('x')
        x^4 - 917*x^2 - 42284

    This came up in a subtle bug (trac #5923)::

        sage: ModularForms(gp(1), gap(12))
        Modular Forms space of dimension 2 for Modular Group SL(2,Z) of weight 12 over Rational Field

    This came up in another bug (related to trac #8630)::

        sage: chi = DirichletGroup(109, CyclotomicField(3)).0
        sage: ModularForms(chi, 2, base_ring = CyclotomicField(15))
        Modular Forms space of dimension 10, character [zeta3 + 1] and weight 2 over Cyclotomic Field of order 15 and degree 8

    We create some weight 1 spaces. The first example works fine, since we can prove purely by Riemann surface theory that there are no weight 1 cusp forms::

        sage: M = ModularForms(Gamma1(11), 1); M
        Modular Forms space of dimension 5 for Congruence Subgroup Gamma1(11) of weight 1 over Rational Field
        sage: M.basis()
        [
        1 + 22*q^5 + O(q^6),
        q + 4*q^5 + O(q^6),
        q^2 - 4*q^5 + O(q^6),
        q^3 - 5*q^5 + O(q^6),
        q^4 - 3*q^5 + O(q^6)
        ]
        sage: M.cuspidal_subspace().basis()
        [
        ]
        sage: M == M.eisenstein_subspace()
        True

    This example doesn't work so well, because we can't calculate the cusp
    forms; but we can still work with the Eisenstein series.

        sage: M = ModularForms(Gamma1(57), 1); M
        Modular Forms space of dimension (unknown) for Congruence Subgroup Gamma1(57) of weight 1 over Rational Field
        sage: M.basis()
        Traceback (most recent call last):
        ...
        NotImplementedError: Computation of dimensions of weight 1 cusp forms spaces not implemented in general
        sage: M.cuspidal_subspace().basis()
        Traceback (most recent call last):
        ...
        NotImplementedError: Computation of dimensions of weight 1 cusp forms spaces not implemented in general

        sage: E = M.eisenstein_subspace(); E
        Eisenstein subspace of dimension 36 of Modular Forms space of dimension (unknown) for Congruence Subgroup Gamma1(57) of weight 1 over Rational Field
        sage: (E.0 + E.2).q_expansion(40)
        1 + q^2 + 1473/2*q^36 - 1101/2*q^37 + q^38 - 373/2*q^39 + O(q^40)

    """
    if isinstance(group, dirichlet.DirichletCharacter):
        if base_ring is None:
            base_ring = group.minimize_base_ring().base_ring()
    if base_ring is None:
        base_ring = rings.QQ

    if isinstance(group, dirichlet.DirichletCharacter) \
           or arithgroup.is_CongruenceSubgroup(group):
        level = group.level()
    else:
        level = group

    key = canonical_parameters(group, level, weight, base_ring)

    if use_cache and _cache.has_key(key):
         M = _cache[key]()
         if not (M is None):
             M.set_precision(prec)
             return M

    (level, group, weight, base_ring) = key

    M = None
    if arithgroup.is_Gamma0(group):
        M = ambient_g0.ModularFormsAmbient_g0_Q(group.level(), weight)
        if base_ring != rings.QQ:
            M = ambient_R.ModularFormsAmbient_R(M, base_ring)

    elif arithgroup.is_Gamma1(group):
        M = ambient_g1.ModularFormsAmbient_g1_Q(group.level(), weight)
        if base_ring != rings.QQ:
            M = ambient_R.ModularFormsAmbient_R(M, base_ring)

    elif arithgroup.is_GammaH(group):
        M = ambient_g1.ModularFormsAmbient_gH_Q(group, weight)
        if base_ring != rings.QQ:
            M = ambient_R.ModularFormsAmbient_R(M, base_ring)

    elif isinstance(group, dirichlet.DirichletCharacter):
        eps = group
        if eps.base_ring().characteristic() != 0:
            # TODO -- implement this
            # Need to add a lift_to_char_0 function for characters,
            # and need to still remember eps.
            raise NotImplementedError, "currently the character must be over a ring of characteristic 0."
        eps = eps.minimize_base_ring()
        if eps.is_trivial():
            return ModularForms(eps.modulus(), weight, base_ring,
                                use_cache = use_cache,
                                prec = prec)
        M = ambient_eps.ModularFormsAmbient_eps(eps, weight)
        if base_ring != eps.base_ring():
            M = M.base_extend(base_ring) # ambient_R.ModularFormsAmbient_R(M, base_ring)

    if M is None:
        raise NotImplementedError, \
           "computation of requested space of modular forms not defined or implemented"

    M.set_precision(prec)
    _cache[key] = weakref.ref(M)
    return M
Ejemplo n.º 20
0
def ModularForms(group=1,
                 weight=2,
                 base_ring=None,
                 eis_only=False,
                 use_cache=True,
                 prec=defaults.DEFAULT_PRECISION):
    r"""
    Create an ambient space of modular forms.

    INPUT:

    - ``group`` - A congruence subgroup or a Dirichlet character eps.

    - ``weight`` - int, the weight, which must be an integer >= 1.

    - ``base_ring`` - the base ring (ignored if group is a Dirichlet character)

    - ``eis_only`` - if True, compute only the Eisenstein part of the space.
      Only permitted (and only useful) in weight 1, where computing dimensions
      of cusp form spaces is expensive.

    Create using the command ModularForms(group, weight, base_ring)
    where group could be either a congruence subgroup or a Dirichlet
    character.

    EXAMPLES: First we create some spaces with trivial character::

        sage: ModularForms(Gamma0(11),2).dimension()
        2
        sage: ModularForms(Gamma0(1),12).dimension()
        2

    If we give an integer N for the congruence subgroup, it defaults to
    `\Gamma_0(N)`::

        sage: ModularForms(1,12).dimension()
        2
        sage: ModularForms(11,4)
        Modular Forms space of dimension 4 for Congruence Subgroup Gamma0(11) of weight 4 over Rational Field

    We create some spaces for `\Gamma_1(N)`.

    ::

        sage: ModularForms(Gamma1(13),2)
        Modular Forms space of dimension 13 for Congruence Subgroup Gamma1(13) of weight 2 over Rational Field
        sage: ModularForms(Gamma1(13),2).dimension()
        13
        sage: [ModularForms(Gamma1(7),k).dimension() for k in [2,3,4,5]]
        [5, 7, 9, 11]
        sage: ModularForms(Gamma1(5),11).dimension()
        12

    We create a space with character::

        sage: e = (DirichletGroup(13).0)^2
        sage: e.order()
        6
        sage: M = ModularForms(e, 2); M
        Modular Forms space of dimension 3, character [zeta6] and weight 2 over Cyclotomic Field of order 6 and degree 2
        sage: f = M.T(2).charpoly('x'); f
        x^3 + (-2*zeta6 - 2)*x^2 - 2*zeta6*x + 14*zeta6 - 7
        sage: f.factor()
        (x - zeta6 - 2) * (x - 2*zeta6 - 1) * (x + zeta6 + 1)

    We can also create spaces corresponding to the groups `\Gamma_H(N)` intermediate
    between `\Gamma_0(N)` and `\Gamma_1(N)`::

        sage: G = GammaH(30, [11])
        sage: M = ModularForms(G, 2); M
        Modular Forms space of dimension 20 for Congruence Subgroup Gamma_H(30) with H generated by [11] of weight 2 over Rational Field
        sage: M.T(7).charpoly().factor()  # long time (7s on sage.math, 2011)
        (x + 4) * x^2 * (x - 6)^4 * (x + 6)^4 * (x - 8)^7 * (x^2 + 4)

    More examples of spaces with character::

        sage: e = DirichletGroup(5, RationalField()).gen(); e
        Dirichlet character modulo 5 of conductor 5 mapping 2 |--> -1

        sage: m = ModularForms(e, 2); m
        Modular Forms space of dimension 2, character [-1] and weight 2 over Rational Field
        sage: m == loads(dumps(m))
        True
        sage: m.T(2).charpoly('x')
        x^2 - 1
        sage: m = ModularForms(e, 6); m.dimension()
        4
        sage: m.T(2).charpoly('x')
        x^4 - 917*x^2 - 42284

    This came up in a subtle bug (:trac:`5923`)::

        sage: ModularForms(gp(1), gap(12))
        Modular Forms space of dimension 2 for Modular Group SL(2,Z) of weight 12 over Rational Field

    This came up in another bug (related to :trac:`8630`)::

        sage: chi = DirichletGroup(109, CyclotomicField(3)).0
        sage: ModularForms(chi, 2, base_ring = CyclotomicField(15))
        Modular Forms space of dimension 10, character [zeta3 + 1] and weight 2 over Cyclotomic Field of order 15 and degree 8

    We create some weight 1 spaces. Here modular symbol algorithms do not work.
    In some small examples we can prove using Riemann--Roch that there are no
    cusp forms anyway, so the entire space is Eisenstein::

        sage: M = ModularForms(Gamma1(11), 1); M
        Modular Forms space of dimension 5 for Congruence Subgroup Gamma1(11) of weight 1 over Rational Field
        sage: M.basis()
        [
        1 + 22*q^5 + O(q^6),
        q + 4*q^5 + O(q^6),
        q^2 - 4*q^5 + O(q^6),
        q^3 - 5*q^5 + O(q^6),
        q^4 - 3*q^5 + O(q^6)
        ]
        sage: M.cuspidal_subspace().basis()
        [
        ]
        sage: M == M.eisenstein_subspace()
        True

    When this does not work (which happens as soon as the level is more than
    about 30), we use the Hecke stability algorithm of George Schaeffer::

        sage: M = ModularForms(Gamma1(57), 1); M # long time
        Modular Forms space of dimension 38 for Congruence Subgroup Gamma1(57) of weight 1 over Rational Field
        sage: M.cuspidal_submodule().basis() # long time
        [
        q - q^4 + O(q^6),
        q^3 - q^4 + O(q^6)
        ]

    The Eisenstein subspace in weight 1 can be computed quickly, without
    triggering the expensive computation of the cuspidal part::

        sage: E = EisensteinForms(Gamma1(59), 1); E # indirect doctest
        Eisenstein subspace of dimension 29 of Modular Forms space for Congruence Subgroup Gamma1(59) of weight 1 over Rational Field
        sage: (E.0 + E.2).q_expansion(40)
        1 + q^2 + 196*q^29 - 197*q^30 - q^31 + q^33 + q^34 + q^37 + q^38 - q^39 + O(q^40)

    """
    if isinstance(group, dirichlet.DirichletCharacter):
        if base_ring is None:
            base_ring = group.minimize_base_ring().base_ring()
    if base_ring is None:
        base_ring = rings.QQ

    if isinstance(group, dirichlet.DirichletCharacter) \
           or arithgroup.is_CongruenceSubgroup(group):
        level = group.level()
    else:
        level = group

    eis_only = bool(eis_only)
    key = canonical_parameters(group, level, weight, base_ring) + (eis_only, )

    if eis_only and weight != 1:
        raise ValueError("eis_only parameter only valid in weight 1")

    if use_cache and key in _cache:
        M = _cache[key]()
        if not (M is None):
            M.set_precision(prec)
            return M

    (level, group, weight, base_ring, eis_only) = key

    M = None
    if arithgroup.is_Gamma0(group):
        M = ModularFormsAmbient_g0_Q(group.level(), weight)
        if base_ring != rings.QQ:
            M = ambient_R.ModularFormsAmbient_R(M, base_ring)

    elif arithgroup.is_Gamma1(group):
        M = ModularFormsAmbient_g1_Q(group.level(), weight, eis_only)
        if base_ring != rings.QQ:
            M = ambient_R.ModularFormsAmbient_R(M, base_ring)

    elif arithgroup.is_GammaH(group):
        M = ModularFormsAmbient_gH_Q(group, weight, eis_only)
        if base_ring != rings.QQ:
            M = ambient_R.ModularFormsAmbient_R(M, base_ring)

    elif isinstance(group, dirichlet.DirichletCharacter):
        eps = group
        if eps.base_ring().characteristic() != 0:
            # TODO -- implement this
            # Need to add a lift_to_char_0 function for characters,
            # and need to still remember eps.
            raise NotImplementedError(
                "currently the character must be over a ring of characteristic 0."
            )
        eps = eps.minimize_base_ring()
        if eps.is_trivial():
            return ModularForms(eps.modulus(),
                                weight,
                                base_ring,
                                use_cache=use_cache,
                                prec=prec)
        M = ModularFormsAmbient_eps(eps, weight, eis_only=eis_only)
        if base_ring != eps.base_ring():
            M = M.base_extend(
                base_ring)  # ambient_R.ModularFormsAmbient_R(M, base_ring)

    if M is None:
        raise NotImplementedError(
            "computation of requested space of modular forms not defined or implemented"
        )

    M.set_precision(prec)
    _cache[key] = weakref.ref(M)
    return M
Ejemplo n.º 21
0
def ModularSymbols(group=1,
                   weight=2,
                   sign=0,
                   base_ring=None,
                   use_cache=True,
                   custom_init=None):
    r"""
    Create an ambient space of modular symbols.

    INPUT:

    - ``group`` - A congruence subgroup or a Dirichlet character eps.
    - ``weight`` - int, the weight, which must be >= 2.
    - ``sign`` - int, The sign of the involution on modular symbols
      induced by complex conjugation. The default is 0, which means
      "no sign", i.e., take the whole space.
    - ``base_ring`` - the base ring. Defaults to `\QQ` if no character
      is given, or to the minimal extension of `\QQ` containing the
      values of the character.
    - ``custom_init`` - a function that is called with self as input
      before any computations are done using self; this could be used
      to set a custom modular symbols presentation.  If self is
      already in the cache and use_cache=True, then this function is
      not called.

    EXAMPLES: First we create some spaces with trivial character::

        sage: ModularSymbols(Gamma0(11),2).dimension()
        3
        sage: ModularSymbols(Gamma0(1),12).dimension()
        3

    If we give an integer N for the congruence subgroup, it defaults to
    `\Gamma_0(N)`::

        sage: ModularSymbols(1,12,-1).dimension()
        1
        sage: ModularSymbols(11,4, sign=1)
        Modular Symbols space of dimension 4 for Gamma_0(11) of weight 4 with sign 1 over Rational Field

    We create some spaces for `\Gamma_1(N)`.

    ::

        sage: ModularSymbols(Gamma1(13),2)
        Modular Symbols space of dimension 15 for Gamma_1(13) of weight 2 with sign 0 and over Rational Field
        sage: ModularSymbols(Gamma1(13),2, sign=1).dimension()
        13
        sage: ModularSymbols(Gamma1(13),2, sign=-1).dimension()
        2
        sage: [ModularSymbols(Gamma1(7),k).dimension() for k in [2,3,4,5]]
        [5, 8, 12, 16]
        sage: ModularSymbols(Gamma1(5),11).dimension()
        20

    We create a space for `\Gamma_H(N)`::

        sage: G = GammaH(15,[4,13])
        sage: M = ModularSymbols(G,2)
        sage: M.decomposition()
        [
        Modular Symbols subspace of dimension 2 of Modular Symbols space of dimension 5 for Congruence Subgroup Gamma_H(15) with H generated by [4, 7] of weight 2 with sign 0 and over Rational Field,
        Modular Symbols subspace of dimension 3 of Modular Symbols space of dimension 5 for Congruence Subgroup Gamma_H(15) with H generated by [4, 7] of weight 2 with sign 0 and over Rational Field
        ]

    We create a space with character::

        sage: e = (DirichletGroup(13).0)^2
        sage: e.order()
        6
        sage: M = ModularSymbols(e, 2); M
        Modular Symbols space of dimension 4 and level 13, weight 2, character [zeta6], sign 0, over Cyclotomic Field of order 6 and degree 2
        sage: f = M.T(2).charpoly('x'); f
        x^4 + (-zeta6 - 1)*x^3 - 8*zeta6*x^2 + (10*zeta6 - 5)*x + 21*zeta6 - 21
        sage: f.factor()
        (x - zeta6 - 2) * (x - 2*zeta6 - 1) * (x + zeta6 + 1)^2

    We create a space with character over a larger base ring than the values of the character::

        sage: ModularSymbols(e, 2, base_ring = CyclotomicField(24))
        Modular Symbols space of dimension 4 and level 13, weight 2, character [zeta24^4], sign 0, over Cyclotomic Field of order 24 and degree 8

    More examples of spaces with character::

        sage: e = DirichletGroup(5, RationalField()).gen(); e
        Dirichlet character modulo 5 of conductor 5 mapping 2 |--> -1

        sage: m = ModularSymbols(e, 2); m
        Modular Symbols space of dimension 2 and level 5, weight 2, character [-1], sign 0, over Rational Field

    ::

        sage: m.T(2).charpoly('x')
        x^2 - 1
        sage: m = ModularSymbols(e, 6); m.dimension()
        6
        sage: m.T(2).charpoly('x')
        x^6 - 873*x^4 - 82632*x^2 - 1860496

    We create a space of modular symbols with nontrivial character in
    characteristic 2.

    ::

        sage: G = DirichletGroup(13,GF(4,'a')); G
        Group of Dirichlet characters modulo 13 with values in Finite Field in a of size 2^2
        sage: e = G.list()[2]; e
        Dirichlet character modulo 13 of conductor 13 mapping 2 |--> a + 1
        sage: M = ModularSymbols(e,4); M
        Modular Symbols space of dimension 8 and level 13, weight 4, character [a + 1], sign 0, over Finite Field in a of size 2^2
        sage: M.basis()
        ([X*Y,(1,0)], [X*Y,(1,5)], [X*Y,(1,10)], [X*Y,(1,11)], [X^2,(0,1)], [X^2,(1,10)], [X^2,(1,11)], [X^2,(1,12)])
        sage: M.T(2).matrix()
        [    0     0     0     0     0     0     1     1]
        [    0     0     0     0     0     0     0     0]
        [    0     0     0     0     0 a + 1     1     a]
        [    0     0     0     0     0     1 a + 1     a]
        [    0     0     0     0 a + 1     0     1     1]
        [    0     0     0     0     0     a     1     a]
        [    0     0     0     0     0     0 a + 1     a]
        [    0     0     0     0     0     0     1     0]

    We illustrate the custom_init function, which can be used to make
    arbitrary changes to the modular symbols object before its
    presentation is computed::

        sage: ModularSymbols_clear_cache()
        sage: def custom_init(M):
        ....:     M.customize='hi'
        sage: M = ModularSymbols(1,12, custom_init=custom_init)
        sage: M.customize
        'hi'

    We illustrate the relation between custom_init and use_cache::

        sage: def custom_init(M):
        ....:     M.customize='hi2'
        sage: M = ModularSymbols(1,12, custom_init=custom_init)
        sage: M.customize
        'hi'
        sage: M = ModularSymbols(1,12, custom_init=custom_init, use_cache=False)
        sage: M.customize
        'hi2'

    TESTS:

    We test use_cache::

        sage: ModularSymbols_clear_cache()
        sage: M = ModularSymbols(11,use_cache=False)
        sage: sage.modular.modsym.modsym._cache
        {}
        sage: M = ModularSymbols(11,use_cache=True)
        sage: sage.modular.modsym.modsym._cache
        {(Congruence Subgroup Gamma0(11), 2, 0, Rational Field): <weakref at ...; to 'ModularSymbolsAmbient_wt2_g0_with_category' at ...>}
        sage: M is ModularSymbols(11,use_cache=True)
        True
        sage: M is ModularSymbols(11,use_cache=False)
        False
    """
    from . import ambient
    key = canonical_parameters(group, weight, sign, base_ring)

    if use_cache and key in _cache:
        M = _cache[key]()
        if M is not None:
            return M

    (group, weight, sign, base_ring) = key

    M = None
    if arithgroup.is_Gamma0(group):
        if weight == 2:
            M = ambient.ModularSymbolsAmbient_wt2_g0(group.level(),
                                                     sign,
                                                     base_ring,
                                                     custom_init=custom_init)
        else:
            M = ambient.ModularSymbolsAmbient_wtk_g0(group.level(),
                                                     weight,
                                                     sign,
                                                     base_ring,
                                                     custom_init=custom_init)

    elif arithgroup.is_Gamma1(group):

        M = ambient.ModularSymbolsAmbient_wtk_g1(group.level(),
                                                 weight,
                                                 sign,
                                                 base_ring,
                                                 custom_init=custom_init)

    elif arithgroup.is_GammaH(group):

        M = ambient.ModularSymbolsAmbient_wtk_gamma_h(group,
                                                      weight,
                                                      sign,
                                                      base_ring,
                                                      custom_init=custom_init)

    elif isinstance(group, tuple):
        eps = group[0]
        M = ambient.ModularSymbolsAmbient_wtk_eps(eps,
                                                  weight,
                                                  sign,
                                                  base_ring,
                                                  custom_init=custom_init)

    if M is None:
        raise NotImplementedError(
            "computation of requested space of modular symbols not defined or implemented"
        )

    if use_cache:
        _cache[key] = weakref.ref(M)
    return M
Ejemplo n.º 22
0
    def multiple_of_order(self, maxp=None):
        """
        Return a multiple of the order of this torsion group.

        The multiple is computed using characteristic polynomials of Hecke
        operators of odd index not dividing the level.

        INPUT:


        -  ``maxp`` - (default: None) If maxp is None (the
           default), return gcd of best bound computed so far with bound
           obtained by computing GCD's of orders modulo p until this gcd
           stabilizes for 3 successive primes. If maxp is given, just use all
           primes up to and including maxp.


        EXAMPLES::

            sage: J = J0(11)
            sage: G = J.rational_torsion_subgroup()
            sage: G.multiple_of_order(11)
            5
            sage: J = J0(389)
            sage: G = J.rational_torsion_subgroup(); G
            Torsion subgroup of Abelian variety J0(389) of dimension 32
            sage: G.multiple_of_order()
            97
            sage: [G.multiple_of_order(p) for p in prime_range(3,11)]
            [92645296242160800, 7275, 291]
            sage: [G.multiple_of_order(p) for p in prime_range(3,13)]
            [92645296242160800, 7275, 291, 97]
            sage: [G.multiple_of_order(p) for p in prime_range(3,19)]
            [92645296242160800, 7275, 291, 97, 97, 97]

        ::

            sage: J = J0(33) * J0(11) ; J.rational_torsion_subgroup().order()
            Traceback (most recent call last):
            ...
            NotImplementedError: torsion multiple only implemented for Gamma0

        The next example illustrates calling this function with a larger
        input and how the result may be cached when maxp is None::

            sage: T = J0(43)[1].rational_torsion_subgroup()
            sage: T.multiple_of_order()
            14
            sage: T.multiple_of_order(50)
            7
            sage: T.multiple_of_order()
            7
        """
        if maxp is None:
            try:
                return self.__multiple_of_order
            except AttributeError:
                pass
        bnd = ZZ(0)
        A = self.abelian_variety()
        if A.dimension() == 0:
            T = ZZ(1)
            self.__multiple_of_order = T
            return T
        N = A.level()
        if not (len(A.groups()) == 1 and is_Gamma0(A.groups()[0])):
            # to generalize to this case, you'll need to
            # (1) define a charpoly_of_frob function:
            #       this is tricky because I don't know a simple
            #       way to do this for Gamma1 and GammaH.  Will
            #       probably have to compute explicit matrix for
            #       <p> operator (add to modular symbols code),
            #       then compute some charpoly involving
            #       that directly...
            # (2) use (1) -- see my MAGMA code.
            raise NotImplementedError(
                "torsion multiple only implemented for Gamma0")
        cnt = 0
        if maxp is None:
            X = Primes()
        else:
            X = prime_range(maxp + 1)
        for p in X:
            if (2 * N) % p == 0:
                continue

            f = A.hecke_polynomial(p)
            b = ZZ(f(p + 1))

            if bnd == 0:
                bnd = b
            else:
                bnd_last = bnd
                bnd = ZZ(gcd(bnd, b))
                if bnd == bnd_last:
                    cnt += 1
                else:
                    cnt = 0
                if maxp is None and cnt >= 2:
                    break

        # The code below caches the computed bound and
        # will be used if this function is called
        # again with maxp equal to None (the default).
        if maxp is None:
            # maxp is None but self.__multiple_of_order  is
            # not set, since otherwise we would have immediately
            # returned at the top of this function
            self.__multiple_of_order = bnd
        else:
            # maxp is given -- record new info we get as
            # a gcd...
            try:
                self.__multiple_of_order = gcd(self.__multiple_of_order, bnd)
            except AttributeError:
                # ... except in the case when self.__multiple_of_order
                # was never set.  In this case, we just set
                # it as long as the gcd stabilized for 3 in a row.
                if cnt >= 2:
                    self.__multiple_of_order = bnd
        return bnd
Ejemplo n.º 23
0
    def _compute_lattice(self, rational_only=False, rational_subgroup=False):
        r"""
        Return a list of vectors that define elements of the rational
        homology that generate this finite subgroup.
        
        INPUT:
        
        
        -  ``rational_only`` - bool (default: False); if
           ``True``, only use rational cusps.
        
        
        OUTPUT:
        
        
        -  ``list`` - list of vectors
        
        
        EXAMPLES::
        
            sage: J = J0(37)
            sage: C = sage.modular.abvar.cuspidal_subgroup.CuspidalSubgroup(J)
            sage: C._compute_lattice()
            Free module of degree 4 and rank 4 over Integer Ring
            Echelon basis matrix:
            [  1   0   0   0]
            [  0   1   0   0]
            [  0   0   1   0]
            [  0   0   0 1/3]
            sage: J = J0(43)
            sage: C = sage.modular.abvar.cuspidal_subgroup.CuspidalSubgroup(J)
            sage: C._compute_lattice()
            Free module of degree 6 and rank 6 over Integer Ring
            Echelon basis matrix:
            [  1   0   0   0   0   0]
            [  0 1/7   0 6/7   0 5/7]
            [  0   0   1   0   0   0]
            [  0   0   0   1   0   0]
            [  0   0   0   0   1   0]
            [  0   0   0   0   0   1]
            sage: J = J0(22)
            sage: C = sage.modular.abvar.cuspidal_subgroup.CuspidalSubgroup(J)
            sage: C._compute_lattice()
            Free module of degree 4 and rank 4 over Integer Ring
            Echelon basis matrix:
            [1/5 1/5 4/5   0]
            [  0   1   0   0]
            [  0   0   1   0]
            [  0   0   0 1/5]
            sage: J = J1(13)
            sage: C = sage.modular.abvar.cuspidal_subgroup.CuspidalSubgroup(J)
            sage: C._compute_lattice()
            Free module of degree 4 and rank 4 over Integer Ring
            Echelon basis matrix:
            [ 1/19     0     0  9/19]
            [    0  1/19  1/19 18/19]
            [    0     0     1     0]
            [    0     0     0     1]
        
        We compute with and without the optional
        ``rational_only`` option.
        
        ::
        
            sage: J = J0(27); G = sage.modular.abvar.cuspidal_subgroup.CuspidalSubgroup(J)
            sage: G._compute_lattice()
            Free module of degree 2 and rank 2 over Integer Ring
            Echelon basis matrix:
            [1/3   0]
            [  0 1/3]
            sage: G._compute_lattice(rational_only=True)
            Free module of degree 2 and rank 2 over Integer Ring
            Echelon basis matrix:
            [1/3   0]
            [  0   1]
        """
        A = self.abelian_variety()
        Cusp = A.modular_symbols()
        Amb  = Cusp.ambient_module()
        Eis  = Amb.eisenstein_submodule()

        C = Amb.cusps()
        N = Amb.level()
        
        if rational_subgroup:
            # QQ-rational subgroup of cuspidal subgroup
            assert A.is_ambient()
            Q = Cusp.abvarquo_rational_cuspidal_subgroup()
            return Q.V()
        
        if rational_only:
            # subgroup generated by differences of rational cusps
            if not is_Gamma0(A.group()):
                raise NotImplementedError, 'computation of rational cusps only implemented in Gamma0 case.'
            if not N.is_squarefree():
                data = [n for n in range(2,N) if gcd(n,N) == 1]
                C = [c for c in C if is_rational_cusp_gamma0(c, N, data)]

        v = [Amb([infinity, alpha]).element() for alpha in C]
        cusp_matrix = matrix(QQ, len(v), Amb.dimension(), v)

        # TODO -- refactor something out here
        # Now we project onto the cuspidal part.
        B = Cusp.free_module().basis_matrix().stack(Eis.free_module().basis_matrix())
        X = B.solve_left(cusp_matrix)
        X = X.matrix_from_columns(range(Cusp.dimension()))
        lattice = X.row_module(ZZ) + A.lattice()
        return lattice
Ejemplo n.º 24
0
def ModularSymbols(group  = 1,
                   weight = 2,
                   sign   = 0,
                   base_ring = None,
                   use_cache = True,
                   custom_init=None):
    r"""
    Create an ambient space of modular symbols.

    INPUT:

    - ``group`` - A congruence subgroup or a Dirichlet character eps.
    - ``weight`` - int, the weight, which must be = 2.
    - ``sign`` - int, The sign of the involution on modular symbols
      induced by complex conjugation. The default is 0, which means
      "no sign", i.e., take the whole space.
    - ``base_ring`` - the base ring. Defaults to `\QQ` if no character
      is given, or to the minimal extension of `\QQ` containing the
      values of the character.
    - ``custom_init`` - a function that is called with self as input
      before any computations are done using self; this could be used
      to set a custom modular symbols presentation.  If self is
      already in the cache and use_cache=True, then this function is
      not called.

    EXAMPLES: First we create some spaces with trivial character::

        sage: ModularSymbols(Gamma0(11),2).dimension()
        3
        sage: ModularSymbols(Gamma0(1),12).dimension()
        3

    If we give an integer N for the congruence subgroup, it defaults to
    `\Gamma_0(N)`::

        sage: ModularSymbols(1,12,-1).dimension()
        1
        sage: ModularSymbols(11,4, sign=1)
        Modular Symbols space of dimension 4 for Gamma_0(11) of weight 4 with sign 1 over Rational Field

    We create some spaces for `\Gamma_1(N)`.

    ::

        sage: ModularSymbols(Gamma1(13),2)
        Modular Symbols space of dimension 15 for Gamma_1(13) of weight 2 with sign 0 and over Rational Field
        sage: ModularSymbols(Gamma1(13),2, sign=1).dimension()
        13
        sage: ModularSymbols(Gamma1(13),2, sign=-1).dimension()
        2
        sage: [ModularSymbols(Gamma1(7),k).dimension() for k in [2,3,4,5]]
        [5, 8, 12, 16]
        sage: ModularSymbols(Gamma1(5),11).dimension()
        20

    We create a space for `\Gamma_H(N)`::

        sage: G = GammaH(15,[4,13])
        sage: M = ModularSymbols(G,2)
        sage: M.decomposition()
        [
        Modular Symbols subspace of dimension 2 of Modular Symbols space of dimension 5 for Congruence Subgroup Gamma_H(15) with H generated by [4, 13] of weight 2 with sign 0 and over Rational Field,
        Modular Symbols subspace of dimension 3 of Modular Symbols space of dimension 5 for Congruence Subgroup Gamma_H(15) with H generated by [4, 13] of weight 2 with sign 0 and over Rational Field
        ]

    We create a space with character::

        sage: e = (DirichletGroup(13).0)^2
        sage: e.order()
        6
        sage: M = ModularSymbols(e, 2); M
        Modular Symbols space of dimension 4 and level 13, weight 2, character [zeta6], sign 0, over Cyclotomic Field of order 6 and degree 2
        sage: f = M.T(2).charpoly('x'); f
        x^4 + (-zeta6 - 1)*x^3 - 8*zeta6*x^2 + (10*zeta6 - 5)*x + 21*zeta6 - 21
        sage: f.factor()
        (x - zeta6 - 2) * (x - 2*zeta6 - 1) * (x + zeta6 + 1)^2

    We create a space with character over a larger base ring than the values of the character::

        sage: ModularSymbols(e, 2, base_ring = CyclotomicField(24))
        Modular Symbols space of dimension 4 and level 13, weight 2, character [zeta24^4], sign 0, over Cyclotomic Field of order 24 and degree 8

    More examples of spaces with character::

        sage: e = DirichletGroup(5, RationalField()).gen(); e
        Dirichlet character modulo 5 of conductor 5 mapping 2 |--> -1

        sage: m = ModularSymbols(e, 2); m
        Modular Symbols space of dimension 2 and level 5, weight 2, character [-1], sign 0, over Rational Field

    ::

        sage: m.T(2).charpoly('x')
        x^2 - 1
        sage: m = ModularSymbols(e, 6); m.dimension()
        6
        sage: m.T(2).charpoly('x')
        x^6 - 873*x^4 - 82632*x^2 - 1860496

    We create a space of modular symbols with nontrivial character in
    characteristic 2.

    ::

        sage: G = DirichletGroup(13,GF(4,'a')); G
        Group of Dirichlet characters modulo 13 with values in Finite Field in a of size 2^2
        sage: e = G.list()[2]; e
        Dirichlet character modulo 13 of conductor 13 mapping 2 |--> a + 1
        sage: M = ModularSymbols(e,4); M
        Modular Symbols space of dimension 8 and level 13, weight 4, character [a + 1], sign 0, over Finite Field in a of size 2^2
        sage: M.basis()
        ([X*Y,(1,0)], [X*Y,(1,5)], [X*Y,(1,10)], [X*Y,(1,11)], [X^2,(0,1)], [X^2,(1,10)], [X^2,(1,11)], [X^2,(1,12)])
        sage: M.T(2).matrix()
        [    0     0     0     0     0     0     1     1]
        [    0     0     0     0     0     0     0     0]
        [    0     0     0     0     0 a + 1     1     a]
        [    0     0     0     0     0     1 a + 1     a]
        [    0     0     0     0 a + 1     0     1     1]
        [    0     0     0     0     0     a     1     a]
        [    0     0     0     0     0     0 a + 1     a]
        [    0     0     0     0     0     0     1     0]

    We illustrate the custom_init function, which can be used to make
    arbitrary changes to the modular symbols object before its
    presentation is computed::

        sage: ModularSymbols_clear_cache()
        sage: def custom_init(M):
        ....:     M.customize='hi'
        sage: M = ModularSymbols(1,12, custom_init=custom_init)
        sage: M.customize
        'hi'

    We illustrate the relation between custom_init and use_cache::

        sage: def custom_init(M):
        ....:     M.customize='hi2'
        sage: M = ModularSymbols(1,12, custom_init=custom_init)
        sage: M.customize
        'hi'
        sage: M = ModularSymbols(1,12, custom_init=custom_init, use_cache=False)
        sage: M.customize
        'hi2'

    TESTS:

    We test use_cache::

        sage: ModularSymbols_clear_cache()
        sage: M = ModularSymbols(11,use_cache=False)
        sage: sage.modular.modsym.modsym._cache
        {}
        sage: M = ModularSymbols(11,use_cache=True)
        sage: sage.modular.modsym.modsym._cache
        {(Congruence Subgroup Gamma0(11), 2, 0, Rational Field): <weakref at ...; to 'ModularSymbolsAmbient_wt2_g0_with_category' at ...>}
        sage: M is ModularSymbols(11,use_cache=True)
        True
        sage: M is ModularSymbols(11,use_cache=False)
        False
    """
    from . import ambient
    key = canonical_parameters(group, weight, sign, base_ring)

    if use_cache and key in _cache:
         M = _cache[key]()
         if not (M is None): return M

    (group, weight, sign, base_ring) = key

    M = None
    if arithgroup.is_Gamma0(group):
            if weight == 2:
                M = ambient.ModularSymbolsAmbient_wt2_g0(
                    group.level(),sign, base_ring, custom_init=custom_init)
            else:
                M = ambient.ModularSymbolsAmbient_wtk_g0(
                    group.level(), weight, sign, base_ring, custom_init=custom_init)

    elif arithgroup.is_Gamma1(group):

        M = ambient.ModularSymbolsAmbient_wtk_g1(group.level(),
                            weight, sign, base_ring, custom_init=custom_init)

    elif arithgroup.is_GammaH(group):

        M = ambient.ModularSymbolsAmbient_wtk_gamma_h(group,
                            weight, sign, base_ring, custom_init=custom_init)

    elif isinstance(group, tuple):
        eps = group[0]
        M = ambient.ModularSymbolsAmbient_wtk_eps(eps,
                            weight, sign, base_ring, custom_init=custom_init)

    if M is None:
        raise NotImplementedError("computation of requested space of modular symbols not defined or implemented")

    if use_cache:
        _cache[key] = weakref.ref(M)
    return M
Ejemplo n.º 25
0
    def multiple_of_order_using_frobp(self, maxp=None):
        """
        Return a multiple of the order of this torsion group.

        In the `Gamma_0` case, the multiple is computed using characteristic
        polynomials of Hecke operators of odd index not dividing the level. In
        the `Gamma_1` case, the multiple is computed by expressing the
        frobenius polynomial in terms of the characteristic polynomial of left
        multiplication by `a_p` for odd primes p not dividing the level.

        INPUT:


        -  ``maxp`` - (default: None) If maxp is None (the
           default), return gcd of best bound computed so far with bound
           obtained by computing GCD's of orders modulo p until this gcd
           stabilizes for 3 successive primes. If maxp is given, just use all
           primes up to and including maxp.


        EXAMPLES::

            sage: J = J0(11)
            sage: G = J.rational_torsion_subgroup()
            sage: G.multiple_of_order_using_frobp(11)
            5

        Increasing maxp may yield a tighter bound. If maxp=None, then Sage
        will use more primes until the multiple stabilizes for 3 successive
        primes.  ::

            sage: J = J0(389)
            sage: G = J.rational_torsion_subgroup(); G
            Torsion subgroup of Abelian variety J0(389) of dimension 32
            sage: G.multiple_of_order_using_frobp()
            97
            sage: [G.multiple_of_order_using_frobp(p) for p in prime_range(3,11)]
            [92645296242160800, 7275, 291]
            sage: [G.multiple_of_order_using_frobp(p) for p in prime_range(3,13)]
            [92645296242160800, 7275, 291, 97]
            sage: [G.multiple_of_order_using_frobp(p) for p in prime_range(3,19)]
            [92645296242160800, 7275, 291, 97, 97, 97]

        We can compute the multiple of order of the torsion subgroup for Gamma0
        and Gamma1 varieties, and their products. ::

            sage: A = J0(11) * J0(33)
            sage: A.rational_torsion_subgroup().multiple_of_order_using_frobp()
            1000

            sage: A = J1(23)
            sage: A.rational_torsion_subgroup().multiple_of_order_using_frobp()
            9406793
            sage: A.rational_torsion_subgroup().multiple_of_order_using_frobp(maxp=50)
            408991

            sage: A = J1(19) * J0(21)
            sage: A.rational_torsion_subgroup().multiple_of_order_using_frobp()
            35064

        The next example illustrates calling this function with a larger
        input and how the result may be cached when maxp is None::

            sage: T = J0(43)[1].rational_torsion_subgroup()
            sage: T.multiple_of_order_using_frobp()
            14
            sage: T.multiple_of_order_using_frobp(50)
            7
            sage: T.multiple_of_order_using_frobp()
            7

        This function is not implemented for general congruence subgroups
        unless the dimension is zero. ::

            sage: A = JH(13,[2]); A
            Abelian variety J0(13) of dimension 0
            sage: A.rational_torsion_subgroup().multiple_of_order_using_frobp()
            1

            sage: A = JH(15, [2]); A
            Abelian variety JH(15,[2]) of dimension 1
            sage: A.rational_torsion_subgroup().multiple_of_order_using_frobp()
            Traceback (most recent call last):
            ...
            NotImplementedError: torsion multiple only implemented for Gamma0 and Gamma1
        """
        if maxp is None:
            try:
                return self.__multiple_of_order_using_frobp
            except AttributeError:
                pass
        A = self.abelian_variety()
        if A.dimension() == 0:
            T = ZZ(1)
            self.__multiple_of_order_using_frobp = T
            return T
        if not all((is_Gamma0(G) or is_Gamma1(G) for G in A.groups())):
            raise NotImplementedError("torsion multiple only implemented for Gamma0 and Gamma1")

        bnd = ZZ(0)
        N = A.level()
        cnt = 0
        if maxp is None:
            X = Primes()
        else:
            X = prime_range(maxp+1)
        for p in X:
            if (2*N) % p == 0:
                continue

            if (len(A.groups()) == 1 and is_Gamma0(A.groups()[0])):
                f = A.hecke_polynomial(p)
                b = ZZ(f(p+1))
            else:
                from .constructor import AbelianVariety
                D = [AbelianVariety(f) for f in
                     A.newform_decomposition('a')]
                b = 1
                for simple in D:
                    G = simple.newform_level()[1]
                    if is_Gamma0(G):
                        f = simple.hecke_polynomial(p)
                        b *= ZZ(f(p+1))
                    else:
                        f = simple.newform('a')
                        Kf = f.base_ring()
                        eps = f.character()
                        Qe = eps.base_ring()

                        if Kf != QQ:
                            # relativize number fields to compute charpoly of
                            # left multiplication of ap on Kf as a Qe-vector
                            # space.
                            Lf = Kf.relativize(Qe.gen(), 'a')
                            to_Lf = Lf.structure()[1]

                            name = Kf._names[0]
                            ap = to_Lf(f.modular_symbols(1).eigenvalue(p, name))

                            G_ps = ap.matrix().charpoly()
                            b *= ZZ(Qe(G_ps(1 + to_Lf(eps(p))*p)).norm())
                        else:
                            ap = f.modular_symbols(1).eigenvalue(p)
                            b *= ZZ(1 + eps(p)*p - ap)

            if bnd == 0:
                bnd = b
            else:
                bnd_last = bnd
                bnd = ZZ(gcd(bnd, b))
                if bnd == bnd_last:
                    cnt += 1
                else:
                    cnt = 0
                if maxp is None and cnt >= 2:
                    break

        # The code below caches the computed bound and
        # will be used if this function is called
        # again with maxp equal to None (the default).
        if maxp is None:
            # maxp is None but self.__multiple_of_order_using_frobp  is
            # not set, since otherwise we would have immediately
            # returned at the top of this function
            self.__multiple_of_order_using_frobp = bnd
        else:
            # maxp is given -- record new info we get as
            # a gcd...
            try:
                self.__multiple_of_order_using_frobp = \
                        gcd(self.__multiple_of_order_using_frobp, bnd)
            except AttributeError:
                # ... except in the case when
                # self.__multiple_of_order_using_frobp was never set.  In this
                # case, we just set it as long as the gcd stabilized for 3 in a
                # row.
                if cnt >= 2:
                    self.__multiple_of_order_using_frobp = bnd
        return bnd
Ejemplo n.º 26
0
    def _compute_lattice(self, rational_only=False, rational_subgroup=False):
        r"""
        Return a list of vectors that define elements of the rational
        homology that generate this finite subgroup.

        INPUT:


        -  ``rational_only`` - bool (default: False); if
           ``True``, only use rational cusps.


        OUTPUT:


        -  ``list`` - list of vectors


        EXAMPLES::

            sage: J = J0(37)
            sage: C = sage.modular.abvar.cuspidal_subgroup.CuspidalSubgroup(J)
            sage: C._compute_lattice()
            Free module of degree 4 and rank 4 over Integer Ring
            Echelon basis matrix:
            [  1   0   0   0]
            [  0   1   0   0]
            [  0   0   1   0]
            [  0   0   0 1/3]
            sage: J = J0(43)
            sage: C = sage.modular.abvar.cuspidal_subgroup.CuspidalSubgroup(J)
            sage: C._compute_lattice()
            Free module of degree 6 and rank 6 over Integer Ring
            Echelon basis matrix:
            [  1   0   0   0   0   0]
            [  0 1/7   0 6/7   0 5/7]
            [  0   0   1   0   0   0]
            [  0   0   0   1   0   0]
            [  0   0   0   0   1   0]
            [  0   0   0   0   0   1]
            sage: J = J0(22)
            sage: C = sage.modular.abvar.cuspidal_subgroup.CuspidalSubgroup(J)
            sage: C._compute_lattice()
            Free module of degree 4 and rank 4 over Integer Ring
            Echelon basis matrix:
            [1/5 1/5 4/5   0]
            [  0   1   0   0]
            [  0   0   1   0]
            [  0   0   0 1/5]
            sage: J = J1(13)
            sage: C = sage.modular.abvar.cuspidal_subgroup.CuspidalSubgroup(J)
            sage: C._compute_lattice()
            Free module of degree 4 and rank 4 over Integer Ring
            Echelon basis matrix:
            [1/19    0 9/19 9/19]
            [   0 1/19    0 9/19]
            [   0    0    1    0]
            [   0    0    0    1]

        We compute with and without the optional
        ``rational_only`` option.

        ::

            sage: J = J0(27); G = sage.modular.abvar.cuspidal_subgroup.CuspidalSubgroup(J)
            sage: G._compute_lattice()
            Free module of degree 2 and rank 2 over Integer Ring
            Echelon basis matrix:
            [1/3   0]
            [  0 1/3]
            sage: G._compute_lattice(rational_only=True)
            Free module of degree 2 and rank 2 over Integer Ring
            Echelon basis matrix:
            [1/3   0]
            [  0   1]
        """
        A = self.abelian_variety()
        Cusp = A.modular_symbols()
        Amb = Cusp.ambient_module()
        Eis = Amb.eisenstein_submodule()

        C = Amb.cusps()
        N = Amb.level()

        if rational_subgroup:
            # QQ-rational subgroup of cuspidal subgroup
            assert A.is_ambient()
            Q = Cusp.abvarquo_rational_cuspidal_subgroup()
            return Q.V()

        if rational_only:
            # subgroup generated by differences of rational cusps
            if not is_Gamma0(A.group()):
                raise NotImplementedError(
                    'computation of rational cusps only implemented in Gamma0 case.'
                )
            if not N.is_squarefree():
                data = [n for n in N.coprime_integers(N) if n >= 2]
                C = [c for c in C if is_rational_cusp_gamma0(c, N, data)]

        v = [Amb([infinity, alpha]).element() for alpha in C]
        cusp_matrix = matrix(QQ, len(v), Amb.dimension(), v)

        # TODO -- refactor something out here
        # Now we project onto the cuspidal part.
        B = Cusp.free_module().basis_matrix().stack(
            Eis.free_module().basis_matrix())
        X = B.solve_left(cusp_matrix)
        X = X.matrix_from_columns(range(Cusp.dimension()))
        lattice = X.row_module(ZZ) + A.lattice()
        return lattice
Ejemplo n.º 27
0
    def multiple_of_order(self, maxp=None):
        """
        Return a multiple of the order of this torsion group.

        The multiple is computed using characteristic polynomials of Hecke
        operators of odd index not dividing the level.

        INPUT:


        -  ``maxp`` - (default: None) If maxp is None (the
           default), return gcd of best bound computed so far with bound
           obtained by computing GCD's of orders modulo p until this gcd
           stabilizes for 3 successive primes. If maxp is given, just use all
           primes up to and including maxp.


        EXAMPLES::

            sage: J = J0(11)
            sage: G = J.rational_torsion_subgroup()
            sage: G.multiple_of_order(11)
            5
            sage: J = J0(389)
            sage: G = J.rational_torsion_subgroup(); G
            Torsion subgroup of Abelian variety J0(389) of dimension 32
            sage: G.multiple_of_order()
            97
            sage: [G.multiple_of_order(p) for p in prime_range(3,11)]
            [92645296242160800, 7275, 291]
            sage: [G.multiple_of_order(p) for p in prime_range(3,13)]
            [92645296242160800, 7275, 291, 97]
            sage: [G.multiple_of_order(p) for p in prime_range(3,19)]
            [92645296242160800, 7275, 291, 97, 97, 97]

        ::

            sage: J = J0(33) * J0(11) ; J.rational_torsion_subgroup().order()
            Traceback (most recent call last):
            ...
            NotImplementedError: torsion multiple only implemented for Gamma0

        The next example illustrates calling this function with a larger
        input and how the result may be cached when maxp is None::

            sage: T = J0(43)[1].rational_torsion_subgroup()
            sage: T.multiple_of_order()
            14
            sage: T.multiple_of_order(50)
            7
            sage: T.multiple_of_order()
            7
        """
        if maxp is None:
            try:
                return self.__multiple_of_order
            except AttributeError:
                pass
        bnd = ZZ(0)
        A = self.abelian_variety()
        if A.dimension() == 0:
            T = ZZ(1)
            self.__multiple_of_order = T
            return T
        N = A.level()
        if not (len(A.groups()) == 1 and is_Gamma0(A.groups()[0])):
            # to generalize to this case, you'll need to
            # (1) define a charpoly_of_frob function:
            #       this is tricky because I don't know a simple
            #       way to do this for Gamma1 and GammaH.  Will
            #       probably have to compute explicit matrix for
            #       <p> operator (add to modular symbols code),
            #       then compute some charpoly involving
            #       that directly...
            # (2) use (1) -- see my MAGMA code.
            raise NotImplementedError("torsion multiple only implemented for Gamma0")
        cnt = 0
        if maxp is None:
            X = Primes()
        else:
            X = prime_range(maxp+1)
        for p in X:
            if (2*N) % p == 0:
                continue

            f = A.hecke_polynomial(p)
            b = ZZ(f(p+1))

            if bnd == 0:
                bnd = b
            else:
                bnd_last = bnd
                bnd = ZZ(gcd(bnd, b))
                if bnd == bnd_last:
                    cnt += 1
                else:
                    cnt = 0
                if maxp is None and cnt >= 2:
                    break

        # The code below caches the computed bound and
        # will be used if this function is called
        # again with maxp equal to None (the default).
        if maxp is None:
            # maxp is None but self.__multiple_of_order  is
            # not set, since otherwise we would have immediately
            # returned at the top of this function
            self.__multiple_of_order = bnd
        else:
            # maxp is given -- record new info we get as
            # a gcd...
            try:
                self.__multiple_of_order = gcd(self.__multiple_of_order, bnd)
            except AttributeError:
                # ... except in the case when self.__multiple_of_order
                # was never set.  In this case, we just set
                # it as long as the gcd stabilized for 3 in a row.
                if cnt >= 2:
                    self.__multiple_of_order = bnd
        return bnd