示例#1
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")
示例#2
0
    def possible_orders(self, proof=True):
        """
        Return the possible orders of this torsion subgroup. Outside of special
        cases, this is done by computing a divisor and multiple of the order.

        INPUT:

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

        OUTPUT:

        - an array of positive integers

        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: J0(11).rational_torsion_subgroup().possible_orders()
            [5]
            sage: J0(33).rational_torsion_subgroup().possible_orders()
            [100, 200]

            sage: J1(13).rational_torsion_subgroup().possible_orders()
            [19]
            sage: J1(16).rational_torsion_subgroup().possible_orders()
            [1, 2, 4, 5, 10, 20]
        """
        try:
            if proof:
                return self._possible_orders
            else:
                return self._possible_orders_proof_false
        except AttributeError:
            pass

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

        # the elliptic curve case
        if A.dimension() == 1:
            self._possible_orders = [A.elliptic_curve().torsion_order()]
            self._possible_orders_proof_false = self._possible_orders
            return self._possible_orders

        # 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._possible_orders_proof_false = [
                ZZ(N / (2**(N - 3)) * prod(bernoullis))
            ]
            return self._possible_orders_proof_false

        u = self.multiple_of_order()
        l = self.divisor_of_order()

        assert u % l == 0
        O = [l * d for d in divisors(u // l)]
        self._possible_orders = O
        if u == l:
            self._possible_orders_proof_false = O
        return O
示例#3
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
示例#4
0
def hecke_operator_on_basis(B, n, k, eps=None, already_echelonized=False):
    r"""
    Given a basis `B` of `q`-expansions for a space of modular forms
    with character `\varepsilon` to precision at least `\#B\cdot n+1`,
    this function computes the matrix of `T_n` relative to `B`.

    .. note::

       If the elements of B are not known to sufficient precision,
       this function will report that the vectors are linearly
       dependent (since they are to the specified precision).

    INPUT:

    - ``B`` - list of q-expansions

    - ``n`` - an integer >= 1

    - ``k`` - an integer

    - ``eps`` - Dirichlet character

    - ``already_echelonized`` -- bool (default: False); if True, use that the
      basis is already in Echelon form, which saves a lot of time.

    EXAMPLES::

        sage: sage.modular.modform.constructor.ModularForms_clear_cache()
        sage: ModularForms(1,12).q_expansion_basis()
        [
        q - 24*q^2 + 252*q^3 - 1472*q^4 + 4830*q^5 + O(q^6),
        1 + 65520/691*q + 134250480/691*q^2 + 11606736960/691*q^3 + 274945048560/691*q^4 + 3199218815520/691*q^5 + O(q^6)
        ]
        sage: hecke_operator_on_basis(ModularForms(1,12).q_expansion_basis(), 3, 12)
        Traceback (most recent call last):
        ...
        ValueError: The given basis vectors must be linearly independent.

        sage: hecke_operator_on_basis(ModularForms(1,12).q_expansion_basis(30), 3, 12)
        [   252      0]
        [     0 177148]

    TESTS:

    This shows that the problem with finite fields reported at :trac:`8281` is solved::

        sage: bas_mod5 = [f.change_ring(GF(5)) for f in victor_miller_basis(12, 20)]
        sage: hecke_operator_on_basis(bas_mod5, 2, 12)
        [4 0]
        [0 1]

    This shows that empty input is handled sensibly (:trac:`12202`)::

        sage: x = hecke_operator_on_basis([], 3, 12); x
        []
        sage: x.parent()
        Full MatrixSpace of 0 by 0 dense matrices over Cyclotomic Field of order 1 and degree 1
        sage: y = hecke_operator_on_basis([], 3, 12, eps=DirichletGroup(13).0^2); y
        []
        sage: y.parent()
        Full MatrixSpace of 0 by 0 dense matrices over Cyclotomic Field of order 12 and degree 4
    """
    if not isinstance(B, (list, tuple)):
        raise TypeError("B (=%s) must be a list or tuple" % B)
    if len(B) == 0:
        if eps is None:
            R = CyclotomicField(1)
        else:
            R = eps.base_ring()
        return MatrixSpace(R, 0)(0)
    f = B[0]
    R = f.base_ring()
    if eps is None:
        eps = DirichletGroup(1, R)[0]
    all_powerseries = True
    for x in B:
        if not is_PowerSeries(x):
            all_powerseries = False
    if not all_powerseries:
        raise TypeError("each element of B must be a power series")
    n = Integer(n)
    k = Integer(k)
    prec = (f.prec() - 1) // n
    A = R**prec
    V = A.span_of_basis([g.padded_list(prec) for g in B],
                        already_echelonized=already_echelonized)
    return _hecke_operator_on_basis(B, V, n, k, eps)
示例#5
0
def hecke_operator_on_qexp(f,
                           n,
                           k,
                           eps=None,
                           prec=None,
                           check=True,
                           _return_list=False):
    r"""
    Given the `q`-expansion `f` of a modular form with character
    `\varepsilon`, this function computes the image of `f` under the
    Hecke operator `T_{n,k}` of weight `k`.

    EXAMPLES::

        sage: M = ModularForms(1,12)
        sage: hecke_operator_on_qexp(M.basis()[0], 3, 12)
        252*q - 6048*q^2 + 63504*q^3 - 370944*q^4 + O(q^5)
        sage: hecke_operator_on_qexp(M.basis()[0], 1, 12, prec=7)
        q - 24*q^2 + 252*q^3 - 1472*q^4 + 4830*q^5 - 6048*q^6 + O(q^7)
        sage: hecke_operator_on_qexp(M.basis()[0], 1, 12)
        q - 24*q^2 + 252*q^3 - 1472*q^4 + 4830*q^5 - 6048*q^6 - 16744*q^7 + 84480*q^8 - 113643*q^9 - 115920*q^10 + 534612*q^11 - 370944*q^12 - 577738*q^13 + O(q^14)

        sage: M.prec(20)
        20
        sage: hecke_operator_on_qexp(M.basis()[0], 3, 12)
        252*q - 6048*q^2 + 63504*q^3 - 370944*q^4 + 1217160*q^5 - 1524096*q^6 + O(q^7)
        sage: hecke_operator_on_qexp(M.basis()[0], 1, 12)
        q - 24*q^2 + 252*q^3 - 1472*q^4 + 4830*q^5 - 6048*q^6 - 16744*q^7 + 84480*q^8 - 113643*q^9 - 115920*q^10 + 534612*q^11 - 370944*q^12 - 577738*q^13 + 401856*q^14 + 1217160*q^15 + 987136*q^16 - 6905934*q^17 + 2727432*q^18 + 10661420*q^19 - 7109760*q^20 + O(q^21)

        sage: (hecke_operator_on_qexp(M.basis()[0], 1, 12)*252).add_bigoh(7)
        252*q - 6048*q^2 + 63504*q^3 - 370944*q^4 + 1217160*q^5 - 1524096*q^6 + O(q^7)

        sage: hecke_operator_on_qexp(M.basis()[0], 6, 12)
        -6048*q + 145152*q^2 - 1524096*q^3 + O(q^4)

    An example on a formal power series::

        sage: R.<q> = QQ[[]]
        sage: f = q + q^2 + q^3 + q^7 + O(q^8)
        sage: hecke_operator_on_qexp(f, 3, 12)
        q + O(q^3)
        sage: hecke_operator_on_qexp(delta_qexp(24), 3, 12).prec()
        8
        sage: hecke_operator_on_qexp(delta_qexp(25), 3, 12).prec()
        9

    An example of computing `T_{p,k}` in characteristic `p`::

        sage: p = 199
        sage: fp = delta_qexp(prec=p^2+1, K=GF(p))
        sage: tfp = hecke_operator_on_qexp(fp, p, 12)
        sage: tfp == fp[p] * fp
        True
        sage: tf = hecke_operator_on_qexp(delta_qexp(prec=p^2+1), p, 12).change_ring(GF(p))
        sage: tfp == tf
        True
    """
    if eps is None:
        # Need to have base_ring=ZZ to work over finite fields, since
        # ZZ can coerce to GF(p), but QQ can't.
        eps = DirichletGroup(1, base_ring=ZZ)[0]
    if check:
        if not (is_PowerSeries(f) or is_ModularFormElement(f)):
            raise TypeError("f (=%s) must be a power series or modular form" %
                            f)
        if not is_DirichletCharacter(eps):
            raise TypeError("eps (=%s) must be a Dirichlet character" % eps)
        k = Integer(k)
        n = Integer(n)
    v = []

    if prec is None:
        if is_ModularFormElement(f):
            # always want at least three coefficients, but not too many, unless
            # requested
            pr = max(f.prec(), f.parent().prec(), (n + 1) * 3)
            pr = min(pr, 100 * (n + 1))
            prec = pr // n + 1
        else:
            prec = (f.prec() / ZZ(n)).ceil()
            if prec == Infinity: prec = f.parent().default_prec() // n + 1

    if f.prec() < prec:
        f._compute_q_expansion(prec)

    p = Integer(f.base_ring().characteristic())
    if k != 1 and p.is_prime() and n.is_power_of(p):
        # if computing T_{p^a} in characteristic p, use the simpler (and faster)
        # formula
        v = [f[m * n] for m in range(prec)]
    else:
        l = k - 1
        for m in range(prec):
            am = sum([eps(d) * d**l * f[m*n//(d*d)] for \
                      d in divisors(gcd(n, m)) if (m*n) % (d*d) == 0])
            v.append(am)
    if _return_list:
        return v
    if is_ModularFormElement(f):
        R = f.parent()._q_expansion_ring()
    else:
        R = f.parent()
    return R(v, prec)
示例#6
0
def half_integral_weight_modform_basis(chi, k, prec):
    r"""
    A basis for the space of weight `k/2` forms with character
    `\chi`. The modulus of `\chi` must be divisible by
    `16` and `k` must be odd and `>1`.

    INPUT:


    -  ``chi`` - a Dirichlet character with modulus
       divisible by 16

    -  ``k`` - an odd integer = 1

    -  ``prec`` - a positive integer


    OUTPUT: a list of power series

    .. warning::

       1. This code is very slow because it requests computation of a
          basis of modular forms for integral weight spaces, and that
          computation is still very slow.

       2. If you give an input prec that is too small, then the output
          list of power series may be larger than the dimension of the
          space of half-integral forms.

    EXAMPLES:

    We compute some half-integral weight forms of level 16\*7

    ::

        sage: half_integral_weight_modform_basis(DirichletGroup(16*7).0^2,3,30)
        [q - 2*q^2 - q^9 + 2*q^14 + 6*q^18 - 2*q^21 - 4*q^22 - q^25 + O(q^30),
         q^2 - q^14 - 3*q^18 + 2*q^22 + O(q^30),
         q^4 - q^8 - q^16 + q^28 + O(q^30),
         q^7 - 2*q^15 + O(q^30)]

    The following illustrates that choosing too low of a precision can
    give an incorrect answer.

    ::

        sage: half_integral_weight_modform_basis(DirichletGroup(16*7).0^2,3,20)
        [q - 2*q^2 - q^9 + 2*q^14 + 6*q^18 + O(q^20),
         q^2 - q^14 - 3*q^18 + O(q^20),
         q^4 - 2*q^8 + 2*q^12 - 4*q^16 + O(q^20),
         q^7 - 2*q^8 + 4*q^12 - 2*q^15 - 6*q^16 + O(q^20),
         q^8 - 2*q^12 + 3*q^16 + O(q^20)]

    We compute some spaces of low level and the first few possible
    weights.

    ::

        sage: half_integral_weight_modform_basis(DirichletGroup(16,QQ).1, 3, 10)
        []
        sage: half_integral_weight_modform_basis(DirichletGroup(16,QQ).1, 5, 10)
        [q - 2*q^3 - 2*q^5 + 4*q^7 - q^9 + O(q^10)]
        sage: half_integral_weight_modform_basis(DirichletGroup(16,QQ).1, 7, 10)
        [q - 2*q^2 + 4*q^3 + 4*q^4 - 10*q^5 - 16*q^7 + 19*q^9 + O(q^10),
         q^2 - 2*q^3 - 2*q^4 + 4*q^5 + 4*q^7 - 8*q^9 + O(q^10),
         q^3 - 2*q^5 - 2*q^7 + 4*q^9 + O(q^10)]
        sage: half_integral_weight_modform_basis(DirichletGroup(16,QQ).1, 9, 10)
        [q - 2*q^2 + 4*q^3 - 8*q^4 + 14*q^5 + 16*q^6 - 40*q^7 + 16*q^8 - 57*q^9 + O(q^10),
         q^2 - 2*q^3 + 4*q^4 - 8*q^5 - 8*q^6 + 20*q^7 - 8*q^8 + 32*q^9 + O(q^10),
         q^3 - 2*q^4 + 4*q^5 + 4*q^6 - 10*q^7 - 16*q^9 + O(q^10),
         q^4 - 2*q^5 - 2*q^6 + 4*q^7 + 4*q^9 + O(q^10),
         q^5 - 2*q^7 - 2*q^9 + O(q^10)]

    This example once raised an error (see trac #5792).

    ::

        sage: half_integral_weight_modform_basis(trivial_character(16),9,10)
        [q - 2*q^2 + 4*q^3 - 8*q^4 + 4*q^6 - 16*q^7 + 48*q^8 - 15*q^9 + O(q^10),
         q^2 - 2*q^3 + 4*q^4 - 2*q^6 + 8*q^7 - 24*q^8 + O(q^10),
         q^3 - 2*q^4 - 4*q^7 + 12*q^8 + O(q^10),
         q^4 - 6*q^8 + O(q^10)]


    ALGORITHM: Basmaji (page 55 of his Essen thesis, "Ein Algorithmus
    zur Berechnung von Hecke-Operatoren und Anwendungen auf modulare
    Kurven", http://wstein.org/scans/papers/basmaji/).

    Let `S = S_{k+1}(\epsilon)` be the space of cusp forms of
    even integer weight `k+1` and character
    `\varepsilon = \chi \psi^{(k+1)/2}`, where `\psi`
    is the nontrivial mod-4 Dirichlet character. Let `U` be the
    subspace of `S \times S` of elements `(a,b)` such
    that `\Theta_2 a = \Theta_3 b`. Then `U` is
    isomorphic to `S_{k/2}(\chi)` via the map
    `(a,b) \mapsto a/\Theta_3`.
    """

    if chi.modulus() % 16:
        raise ValueError, "the character must have modulus divisible by 16"

    if not k % 2:
        raise ValueError, "k (=%s) must be odd" % k

    if k < 3:
        raise ValueError, "k (=%s) must be at least 3" % k

    chi = chi.minimize_base_ring()
    psi = chi.parent()(DirichletGroup(4, chi.base_ring()).gen())
    eps = chi * psi**((k + 1) // 2)
    eps = eps.minimize_base_ring()
    M = constructor.ModularForms(eps, (k + 1) // 2)
    C = M.cuspidal_subspace()
    B = C.basis()

    # This computation of S below -- of course --dominates the whole function.
    #from sage.misc.all import cputime
    #tm  = cputime()
    #print "Computing basis..."
    S = [f.q_expansion(prec) for f in B]
    #print "Time to compute basis", cputime(tm)

    T2 = theta2_qexp(prec)
    T3 = theta_qexp(prec)
    n = len(S)
    MS = MatrixSpace(M.base_ring(), 2 * n, prec)
    A = copy(MS.zero_matrix())

    for i in range(n):
        T2f = T2 * S[i]
        T3f = T3 * S[i]
        for j in range(prec):
            A[i, j] = T2f[j]
            A[n + i, j] = -T3f[j]

    B = A.kernel().basis()
    a_vec = [sum([b[i] * S[i] for i in range(n)]) for b in B]
    if len(a_vec) == 0:
        return []
    R = a_vec[0].parent()
    t3 = R(T3)
    return [R(a) / t3 for a in a_vec]
示例#7
0
    def dimension_of_ordinary_subspace(self, p=None, cusp=False):
        """
        If ``cusp`` is ``True``, return dimension of cuspidal ordinary
        subspace. This does a weight 2 computation with sage's ModularSymbols.
        
        EXAMPLES::
        
            sage: M = OverconvergentModularSymbols(11, 0, sign=-1, p=3, prec_cap=4, base=ZpCA(3, 8))
            sage: M.dimension_of_ordinary_subspace()
            2
            sage: M.dimension_of_ordinary_subspace(cusp=True)
            2
            sage: M = OverconvergentModularSymbols(11, 0, sign=1, p=3, prec_cap=4, base=ZpCA(3, 8))
            sage: M.dimension_of_ordinary_subspace(cusp=True)
            2
            sage: M.dimension_of_ordinary_subspace()
            4
            sage: M = OverconvergentModularSymbols(11, 0, sign=0, p=3, prec_cap=4, base=ZpCA(3, 8))
            sage: M.dimension_of_ordinary_subspace()
            6
            sage: M.dimension_of_ordinary_subspace(cusp=True)
            4
            sage: M = OverconvergentModularSymbols(11, 0, sign=1, p=11, prec_cap=4, base=ZpCA(11, 8))
            sage: M.dimension_of_ordinary_subspace(cusp=True)
            1
            sage: M.dimension_of_ordinary_subspace()
            2
            sage: M = OverconvergentModularSymbols(11, 2, sign=1, p=11, prec_cap=4, base=ZpCA(11, 8))
            sage: M.dimension_of_ordinary_subspace(cusp=True)
            0
            sage: M.dimension_of_ordinary_subspace()
            1
            sage: M = OverconvergentModularSymbols(11, 10, sign=1, p=11, prec_cap=4, base=ZpCA(11, 8))
            sage: M.dimension_of_ordinary_subspace(cusp=True)
            1
            sage: M.dimension_of_ordinary_subspace()
            2
        
        An example with odd weight and hence non-trivial character::
        
            sage: K = Qp(11, 6)
            sage: DG = DirichletGroup(11, K)
            sage: chi = DG([K(378703)])
            sage: MM = FamiliesOfOMS(chi, 1, p=11, prec_cap=[4, 4], base_coeffs=ZpCA(11, 4), sign=-1)
            sage: MM.dimension_of_ordinary_subspace()
            1
        """
        try:
            p = self.prime()
        except AttributeError:
            if p is None:
                raise ValueError(
                    "If self doesn't have a prime, must specify p.")
        try:
            return self._ord_dim_dict[(p, cusp)]
        except AttributeError:
            self._ord_dim_dict = {}
        except KeyError:
            pass
        from sage.modular.dirichlet import DirichletGroup
        from sage.rings.finite_rings.constructor import GF
        try:
            chi = self.character()
        except AttributeError:
            chi = DirichletGroup(self.level(), GF(p))[0]
        if chi is None:
            chi = DirichletGroup(self.level(), GF(p))[0]

        from sage.modular.modsym.modsym import ModularSymbols
        r = self.weight() % (p - 1)
        if chi.is_trivial():
            N = chi.modulus()
            if N % p != 0:
                N *= p
            else:
                e = N.valuation(p)
                N.divide_knowing_divisible_by(p**(e - 1))
            chi = DirichletGroup(N, GF(p))[0]
        elif chi.modulus() % p != 0:
            chi = DirichletGroup(chi.modulus() * p, GF(p))(chi)
        DG = DirichletGroup(chi.modulus(), GF(p))
        if r == 0:
            from sage.modular.arithgroup.congroup_gamma0 import Gamma0_constructor as Gamma0
            verbose("in dim: %s, %s, %s" % (self.sign(), chi, p))
            M = ModularSymbols(DG(chi), 2, self.sign(), GF(p))
        else:
            psi = [GF(p)(u)**r for u in DG.unit_gens()]  #mod p Teichmuller^r
            psi = DG(psi)
            M = ModularSymbols(DG(chi) * psi, 2, self.sign(), GF(p))
        if cusp:
            M = M.cuspidal_subspace()
        hecke_poly = M.hecke_polynomial(p)
        verbose("in dim: %s" % (hecke_poly))
        x = hecke_poly.parent().gen()
        d = hecke_poly.degree() - hecke_poly.ord(x)
        self._ord_dim_dict[(p, cusp)] = d
        return d
示例#8
0
    def dimension_eis(self, k=2, eps=None, algorithm="CohenOesterle"):
        r"""
        Return the dimension of the space of Eisenstein series forms for self,
        or the dimension of the subspace corresponding to the given character
        if one is supplied.

        INPUT:

        - ``k`` - an integer (default: 2), the weight.

        - ``eps`` - either None or a Dirichlet character modulo N, where N is
          the level of this group. If this is None, then the dimension of the
          whole space is returned; otherwise, the dimension of the subspace of
          Eisenstein series of character eps.

        - ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This
          specifies the method to use in the case of nontrivial character:
          either the Cohen--Oesterle formula as described in Stein's book, or
          by Möbius inversion using the subgroups GammaH (a method due to
          Jordi Quer).

        AUTHORS:

        - William Stein - Cohen--Oesterle algorithm

        - Jordi Quer - algorithm based on GammaH subgroups

        - David Loeffler (2009) - code refactoring

        EXAMPLES:

        The following two computations use different algorithms::

            sage: [Gamma1(36).dimension_eis(1,eps) for eps in DirichletGroup(36)]
            [0, 4, 3, 0, 0, 2, 6, 0, 0, 2, 3, 0]
            sage: [Gamma1(36).dimension_eis(1,eps,algorithm="Quer") for eps in DirichletGroup(36)]
            [0, 4, 3, 0, 0, 2, 6, 0, 0, 2, 3, 0]

        So do these::

            sage: [Gamma1(48).dimension_eis(3,eps) for eps in DirichletGroup(48)]
            [0, 12, 0, 4, 0, 8, 0, 4, 12, 0, 4, 0, 8, 0, 4, 0]
            sage: [Gamma1(48).dimension_eis(3,eps,algorithm="Quer") for eps in DirichletGroup(48)]
            [0, 12, 0, 4, 0, 8, 0, 4, 12, 0, 4, 0, 8, 0, 4, 0]
        """
        from .all import Gamma0

        # first deal with special cases

        if eps is None:
            return GammaH_class.dimension_eis(self, k)

        N = self.level()
        K = eps.base_ring()
        eps = DirichletGroup(N, K)(eps)

        if eps.is_trivial():
            return Gamma0(N).dimension_eis(k)

        # Note case of k = 0 and trivial character already dealt with separately, so k <= 0 here is valid:
        if (k <= 0) or ((k % 2) == 1 and eps.is_even()) or ((k%2) == 0 and eps.is_odd()):
            return ZZ(0)

        if algorithm == "Quer":
            n = eps.order()
            dim = ZZ(0)
            for d in n.divisors():
                G = GammaH_constructor(N,(eps**d).kernel())
                dim = dim + moebius(d)*G.dimension_eis(k)
            return dim//phi(n)

        elif algorithm == "CohenOesterle":
            from sage.modular.dims import CohenOesterle
            j = 2-k
            # We use the Cohen-Oesterle formula in a subtle way to
            # compute dim M_k(N,eps) (see Ch. 6 of William Stein's book on
            # computing with modular forms).
            alpha = -ZZ( K(Gamma0(N).index()*(j-1)/ZZ(12)) + CohenOesterle(eps,j) )
            if k == 1:
                return alpha
            else:
                return alpha - self.dimension_cusp_forms(k, eps)

        else: #algorithm not in ["CohenOesterle", "Quer"]:
            raise ValueError("Unrecognised algorithm in dimension_eis")
示例#9
0
    def dimension_new_cusp_forms(self, k=2, eps=None, p=0, algorithm="CohenOesterle"):
        r"""
        Dimension of the new subspace (or `p`-new subspace) of cusp forms of
        weight `k` and character `\varepsilon`.

        INPUT:

        - ``k`` - an integer (default: 2)

        - ``eps`` - a Dirichlet character

        -  ``p`` - a prime (default: 0); just the `p`-new subspace if given

        - ``algorithm`` - either "CohenOesterle" (the default) or "Quer". This
          specifies the method to use in the case of nontrivial character:
          either the Cohen--Oesterle formula as described in Stein's book, or
          by Möbius inversion using the subgroups GammaH (a method due to
          Jordi Quer).

        EXAMPLES::

            sage: G = DirichletGroup(9)
            sage: eps = G.0^3
            sage: eps.conductor()
            3
            sage: [Gamma1(9).dimension_new_cusp_forms(k, eps) for k in [2..10]]
            [0, 0, 0, 2, 0, 2, 0, 2, 0]
            sage: [Gamma1(9).dimension_cusp_forms(k, eps) for k in [2..10]]
            [0, 0, 0, 2, 0, 4, 0, 6, 0]
            sage: [Gamma1(9).dimension_new_cusp_forms(k, eps, 3) for k in [2..10]]
            [0, 0, 0, 2, 0, 2, 0, 2, 0]

        Double check using modular symbols (independent calculation)::

            sage: [ModularSymbols(eps,k,sign=1).cuspidal_subspace().new_subspace().dimension()  for k in [2..10]]
            [0, 0, 0, 2, 0, 2, 0, 2, 0]
            sage: [ModularSymbols(eps,k,sign=1).cuspidal_subspace().new_subspace(3).dimension()  for k in [2..10]]
            [0, 0, 0, 2, 0, 2, 0, 2, 0]

        Another example at level 33::

            sage: G = DirichletGroup(33)
            sage: eps = G.1
            sage: eps.conductor()
            11
            sage: [Gamma1(33).dimension_new_cusp_forms(k, G.1) for k in [2..4]]
            [0, 4, 0]
            sage: [Gamma1(33).dimension_new_cusp_forms(k, G.1, algorithm="Quer") for k in [2..4]]
            [0, 4, 0]
            sage: [Gamma1(33).dimension_new_cusp_forms(k, G.1^2) for k in [2..4]]
            [2, 0, 6]
            sage: [Gamma1(33).dimension_new_cusp_forms(k, G.1^2, p=3) for k in [2..4]]
            [2, 0, 6]

        """

        if eps is None:
            return GammaH_class.dimension_new_cusp_forms(self, k, p)

        N = self.level()
        eps = DirichletGroup(N, eps.base_ring())(eps)

        if eps.is_trivial():
            from .all import Gamma0
            return Gamma0(N).dimension_new_cusp_forms(k, p)

        from .congroup_gammaH import mumu

        if p == 0 or N%p != 0 or eps.conductor().valuation(p) == N.valuation(p):
            D = [eps.conductor()*d for d in divisors(N//eps.conductor())]
            return sum([Gamma1_constructor(M).dimension_cusp_forms(k, eps.restrict(M), algorithm)*mumu(N//M) for M in D])
        eps_p = eps.restrict(N//p)
        old = Gamma1_constructor(N//p).dimension_cusp_forms(k, eps_p, algorithm)
        return self.dimension_cusp_forms(k, eps, algorithm) - 2*old
示例#10
0
    def dimension_cusp_forms(self, k=2, eps=None, algorithm="CohenOesterle"):
        r"""
        Return the dimension of the space of cusp forms for self, or the
        dimension of the subspace corresponding to the given character if one
        is supplied.

        INPUT:

        - ``k`` - an integer (default: 2), the weight.

        - ``eps`` - either None or a Dirichlet character modulo N, where N is
          the level of this group. If this is None, then the dimension of the
          whole space is returned; otherwise, the dimension of the subspace of
          forms of character eps.

        - ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This
          specifies the method to use in the case of nontrivial character:
          either the Cohen--Oesterle formula as described in Stein's book, or
          by Möbius inversion using the subgroups GammaH (a method due to
          Jordi Quer).

        EXAMPLES:

        We compute the same dimension in two different ways ::

            sage: K = CyclotomicField(3)
            sage: eps = DirichletGroup(7*43,K).0^2
            sage: G = Gamma1(7*43)

        Via Cohen--Oesterle::

            sage: Gamma1(7*43).dimension_cusp_forms(2, eps)
            28

        Via Quer's method::

            sage: Gamma1(7*43).dimension_cusp_forms(2, eps, algorithm="Quer")
            28

        Some more examples::

            sage: G.<eps> = DirichletGroup(9)
            sage: [Gamma1(9).dimension_cusp_forms(k, eps) for k in [1..10]]
            [0, 0, 1, 0, 3, 0, 5, 0, 7, 0]
            sage: [Gamma1(9).dimension_cusp_forms(k, eps^2) for k in [1..10]]
            [0, 0, 0, 2, 0, 4, 0, 6, 0, 8]
        """
        from .all import Gamma0

        # first deal with special cases

        if eps is None:
            return GammaH_class.dimension_cusp_forms(self, k)

        N = self.level()
        K = eps.base_ring()
        eps = DirichletGroup(N, K)(eps)

        if K.characteristic() != 0:
            raise NotImplementedError('dimension_cusp_forms() is only implemented for rings of characteristic 0')

        if eps.is_trivial():
            return Gamma0(N).dimension_cusp_forms(k)

        if (k <= 0) or ((k % 2) == 1 and eps.is_even()) or ((k%2) == 0 and eps.is_odd()):
            return ZZ(0)

        if k == 1:
            try:
                n = self.dimension_cusp_forms(1)
                if n == 0:
                    return ZZ(0)
                else: # never happens at present
                    raise NotImplementedError("Computations of dimensions of spaces of weight 1 cusp forms not implemented at present")
            except NotImplementedError:
                raise

        # now the main part

        if algorithm == "Quer":
            n = eps.order()
            dim = ZZ(0)
            for d in n.divisors():
                G = GammaH_constructor(N,(eps**d).kernel())
                dim = dim + moebius(d)*G.dimension_cusp_forms(k)
            return dim//phi(n)

        elif algorithm == "CohenOesterle":
            from sage.modular.dims import CohenOesterle
            return ZZ( K(Gamma0(N).index() * (k-1)/ZZ(12)) + CohenOesterle(eps,k) )

        else: #algorithm not in ["CohenOesterle", "Quer"]:
            raise ValueError("Unrecognised algorithm in dimension_cusp_forms")
示例#11
0
def make_table_of_spaces_fixed_level(level=1,
                                     character=0,
                                     weight_block=0,
                                     **kwds):
    r"""
    """
    wlen = 15
    print "make table: ", level, character, weight_block
    w_start = wlen * weight_block
    w_stop = wlen * (weight_block + 1)
    s = "<table><thead></thead><tbody>\n"
    s += "<tr><td>Weight \(k\):</td>"
    dims = dict()
    links = dict()
    character = int(character)
    x = trivial_character(level)
    if character > 0:
        D = DirichletGroup(level).list()
        x = D[int(character)]
    if x.is_even() and is_odd(w_start):
        w_start = w_start + 1
        w_stop = w_stop + 1
    if x.is_odd() and is_even(w_start):
        w_start = w_start + 1
        w_stop = w_stop + 1
    weights = list()
    for weight in range(w_start, w_start + 2 * wlen, 2):
        weights.append(weight)
    for weight in weights:
        s += "<td> %s </td>" % weight
    s += "</tr><tr>"
    if character > 0:
        s += "<td>Dimension:"  # of  \(S^{\\textrm{new}}_{k}(%s),\chi_{%s}\):" % (level,character)
    else:
        s += "<td>Dimension:"  # of \(S^{\\textrm{new}}_{k}(%s)\):" % (level)
    for weight in weights:
        if character > 0:
            dims[weight] = dimension_new_cusp_forms(x, weight)
        else:
            dims[weight] = dimension_new_cusp_forms(level, weight)
        s += "<td> %s </td>" % dims[weight]
    j = 0  # we display ony even weight if the character is even
    print "w_start=", w_start
    print "w_stop=", w_stop
    for weight in weights:
        if weight not in dims:
            continue
        if dims[weight] > 0:
            url = url_for('emf.render_elliptic_modular_forms',
                          level=level,
                          weight=weight)
            if character > 0:
                lab = "\(S^{\\textrm{}}_{%s}(%s,\chi_{%s})\)" % (weight, level,
                                                                 character)
            else:
                # lab = " \(S_{%s}(%s)\)" %(weight,level)
                lab = " S<sup><small></small></sup><sub><small>%s</small></sub>(%s)" % (
                    weight, level)
            links[
                weight] = "<a  style=\"display:inline\" href=\"%s\">%s</a>" % (
                    url, lab)
        else:
            links[weight] = ""
        j += 1
        if j >= wlen:
            exit
    l = max(map(len_as_printed, map(str, links))) * 10.0
    s += "</tr><tr>"
    s += "<td>Link to space:</td>"
    for weight in weights:
        if weight in links:
            s += "<td width=\"%s\">%s</td>" % (l + 50, links[weight])
    s += "</tr></tbody></table>"
    # print s
    return s
示例#12
0
def set_table(
        info,
        is_set,
        make_link=True):  # level_min,level_max,weight=2,chi=0,make_link=True):
    r"""
    make a bunch of html tables with information about spaces of modular forms
    with parameters in the given ranges.
    Should use database in the future...
    """
    D = 0
    rowlen = 10  # split into rows of this length...
    rowlen0 = rowlen
    rowlen1 = rowlen
    characters = dict()
    if ('level_min' in info):
        level_min = int(info['level_min'])
    else:
        level_min = 1
    if ('level_max' in info):
        level_max = int(info['level_max'])
    else:
        level_max = 50
    if (level_max - level_min + 1) < rowlen:
        rowlen0 = level_max - level_min + 1
    if (info['list_chars'] != '0'):
        char1 = 1
    else:
        char1 = 0
    if (is_set['weight']):
        weight = int(info['weight'])
    else:
        weight = 2
    ## setup the table
    # print "char11=",char1
    tbl = dict()
    if (char1 == 1):
        tbl['header'] = 'Dimension of \( S_{' + str(weight) + '}(N,\chi_{n})\)'
    else:
        tbl['header'] = 'Dimension of \( S_{' + str(weight) + '}(N)\)'
    tbl['headersv'] = list()
    tbl['headersh'] = list()
    tbl['corner_label'] = ""
    tbl['data'] = list()
    tbl['data_format'] = 'html'
    tbl['class'] = "dimension_table"
    tbl['atts'] = "border=\"0\" class=\"data_table\""
    num_rows = ceil(QQ(level_max - level_min + 1) / QQ(rowlen0))
    print "num_rows=", num_rows
    for i in range(1, rowlen0 + 1):
        tbl['headersh'].append(i + level_min - 1)

    for r in range(num_rows):
        tbl['headersv'].append(r * rowlen0)
    print "level_min=", level_min
    print "level_max=", level_max
    print "char=", char1
    for r in range(num_rows):
        row = list()
        for k in range(1, rowlen0 + 1):
            row.append("")
        # print "row nr. ",r
        for k in range(1, rowlen0 + 1):
            N = level_min - 1 + r * rowlen0 + k
            s = "<a name=\"#" + str(N) + "\"></a>"
            # print "col ",k,"=",N
            if (N > level_max or N < 1):
                continue
            if (char1 == 0):
                d = dimension_cusp_forms(N, weight)
                print "d=", d
                if (make_link):
                    url = "?weight=" + str(weight) + "&level=" + str(
                        N) + "&character=0"
                    row.append(s + "<a target=\"mainWindow\" href=\"" + url +
                               "\">" + str(d) + "</a>")
                else:
                    row.append(s + str(d))

                # print "dim(",N,weight,")=",d
            else:

                D = DirichletGroup(N)
                print "D=", D
                s = "<a name=\"#" + str(N) + "\"></a>"
                small_tbl = dict()
                # small_tbl['header']='Dimension of \( S_{'+str(weight)+'}(N)\)'
                small_tbl['headersv'] = ['\( d \)']
                small_tbl['headersh'] = list()
                small_tbl['corner_label'] = "\( n \)"
                small_tbl['data'] = list()
                small_tbl['atts'] = "border=\"1\" padding=\"1\""
                small_tbl['data_format'] = 'html'
                row1 = list()
                # num_small_rows = ceil(QQ(level_max) / QQ(rowlen))
                ii = 0
                for chi in range(0, len(D.list())):
                    x = D[chi]
                    S = CuspForms(x, weight)
                    d = S.dimension()
                    if (d == 0):
                        continue
                    small_tbl['headersh'].append(chi)
                    if (make_link):
                        url = "?weight=" + str(weight) + "&level=" + str(
                            N) + "&character=" + str(chi)
                        row1.append("<a target=\"mainWindow\" href=\"" + url +
                                    "\">" + str(d) + "</a>")
                    else:
                        row1.append(d)
                    ii = ii + 1
                    print "d=", d
                    if (ii > rowlen1 and len(row1) > 0):
                        ## we make a new table since we may not have regularly dstributed labels
                        # print "Break line! Make new table!"
                        small_tbl['data'].append(row1)
                        s = s + html_table(small_tbl)
                        small_tbl['headersh'] = list()
                        small_tbl['data'] = list()
                        row1 = list()
                        ii = 0

                if (len(row1) > 0):
                    small_tbl['data'].append(row1)
                if (len(row1) > 0 or len(small_tbl['data']) > 0):
                    # print "small_tbl=",small_tbl
                    ss = html_table(small_tbl)
                    # print "ss=",ss
                    s = s + ss
                    # s=s+"\( \chi_{"+str(chi)+"}\) :"+str(d)

                    # print N,k,chi,d
                # print s
                else:
                    s = "All spaces are zero-dimensional!"
                row.append(s)
        print "row=", row
        tbl['data'].append(row)
    s = html_table(tbl)
    s = s + "\n <br> \(N=" + str(rowlen0) + "\cdot row+col\)"
    print "Whole table=", s
    ## ugly solution. but we have latex in the data fields...
    ss = re.sub('texttt', '', s)
    info['popup_table'] = ss
    # info['sidebar']=set_sidebar([navigation,parents,siblings,friends,lifts])
    return info
示例#13
0
def make_table_of_dimensions(level_start=1,
                             level_stop=50,
                             weight_start=1,
                             weight_stop=24,
                             char=0,
                             **kwds):
    r"""
    make an html table with information about spaces of modular forms
    with parameters in the given ranges. using a fixed character.
    Should use database in the future...
    """
    D = 0
    rowlen = 15  # split into rows of this length...
    rowlen0 = rowlen
    rowlen1 = rowlen
    characters = dict()
    level = 'N'
    weight = 'k'
    print "char=", char
    if level_start == level_stop:
        level = level_start
        count_min = weight_start
        count_max = weight_stop
        if (weight_stop - weight_start + 1) < rowlen:
            rowlen0 = weight_stop - weight_start + 1
    if weight_start == weight_stop:
        weight = weight_start
        count_min = level_start
        count_max = level_stop
        if (level_stop - level_start + 1) < rowlen:
            rowlen0 = level_stop - level_start + 1
    # else:
    #    return ""
    tbl = dict()
    if (char == 0):
        tbl['header'] = ''  # Dimension of \( S_{'+str(weight)+'}('+str(level)+',\chi_{n})\)'
        charst = ""
    else:
        # s = 'Dimension of \( S_{'+str(weight)+'}('+str(level)+')\)'
        # s += ' (trivial character)'
        charst = ",\chi_{%s}" % char
        tbl['header'] = ''
    tbl['headersv'] = list()
    tbl['headersh'] = list()
    if weight == 'k':
        tbl['corner_label'] = "weight \(k\):"
    else:
        tbl['corner_label'] = "level \(N\):"
    tbl['data'] = list()
    tbl['data_format'] = 'html'
    tbl['class'] = "dimension_table"
    tbl['atts'] = "border=\"1\" class=\"nt_data\" padding=\"25\" width=\"100%\""
    num_rows = ceil(QQ(count_max - count_min + 1) / QQ(rowlen0))
    print "num_rows=", num_rows
    for i in range(1, rowlen0 + 1):
        tbl['headersh'].append(i + count_min - 1)
    if level_start == level_stop:
        st = "Dimension of \(S_{k}(%s%s) \):" % (level, charst)
        tbl['headersv'] = [st]
    else:
        st = "Dimension of \(S_{%s}(N%s) \):" % (weight, charst)
        tbl['headersv'] = [st]
    tbl['headersv'].append('Link to space:')
    # make a dummy table first
    # num_rows = (num_rows-1)*2
    for r in range(num_rows * 2):
        row = []
        for k in range(1, rowlen0 + 1):
            row.append("")
        tbl['data'].append(row)
    tbl['data_format'] = dict()
    for k in range(0, rowlen0):
        tbl['data_format'][k] = 'html'

    print "nu_rows=", len(tbl['data'])
    print "num_cols=", rowlen0
    print "num_cols=", [len(r) for r in tbl['data']]
    for r in range(num_rows):
        for k in range(0, rowlen0):
            cnt = count_min + r * rowlen0 + k
            if level_start == level_stop:
                weight = cnt
            else:
                level = cnt
            url = url_for('emf.render_elliptic_modular_forms',
                          level=level,
                          weight=weight)
            if (cnt > count_max or cnt < count_min):
                tbl['data'][2 * r][k] = ""
                continue
            # s="<a name=\"#%s,%s\"></a>" % (level,weight)
            if (char == 0):
                d = dimension_cusp_forms(level, weight)
            else:
                x = DirichletGroup(level)[char]
                d = dimension_cusp_forms(x, weight)
            tbl['data'][2 * r][k] = str(d)
            if d > 0:
                s = "\(S_{%s}(%s)\)" % (weight, level)
                ss = "<a  href=\"" + url + "\">" + s + "</a>"
                tbl['data'][2 * r + 1][k] = ss
            # else:
            #    tbl['data'][2*r+1][k]="\(\emptyset\)"
            #    ss = make_table_of_characters(level,weight)
            #    tbl['data'][2*r+1][k]=ss
            # tbl['data'][r][k]=s
            # print "row=",row
            # tbl['data'][r]=row
    # print "tbl=",tbl
    s = html_table(tbl)
    # s=s+"\n <br> \(N="+str(rowlen0)+"\cdot row+col\)"
    # print "SS=",s
    return s
def product_space(chi, k, weights=False, base_ring=None, verbose=False):
    r"""
    Computes all eisenstein series, and products of pairs of eisenstein series
    of lower weight, lying in the space of modular forms of weight $k$ and
    nebentypus $\chi$.
    INPUT:
     - chi - Dirichlet character, the nebentypus of the target space
     - k - an integer, the weight of the target space
    OUTPUT:
     - a matrix of coefficients of q-expansions, which are the products of
     Eisenstein series in M_k(chi).

    WARNING: It is only for principal chi that we know that the resulting
    space is the whole space of modular forms.
    """

    if weights == False:
        weights = srange(1, k / 2 + 1)
    weight_dict = {}
    weight_dict[-1] = [w for w in weights if w % 2]  # Odd weights
    weight_dict[1] = [w for w in weights if not w % 2]  # Even weights

    try:
        N = chi.modulus()
    except AttributeError:
        if chi.parent() == ZZ:
            N = chi
            chi = DirichletGroup(N)[0]

    Id = DirichletGroup(1)[0]
    if chi(-1) != (-1)**k:
        raise ValueError('chi(-1)!=(-1)^k')
    sturm = ModularForms(N, k).sturm_bound() + 1
    if N > 1:
        target_dim = dimension_modular_forms(chi, k)
    else:
        target_dim = dimension_modular_forms(1, k)
    D = DirichletGroup(N)
    # product_space should ideally be called over number fields. Over complex
    # numbers the exact linear algebra solutions might not exist.
    if base_ring == None:
        base_ring = CyclotomicField(euler_phi(N))

    Q = PowerSeriesRing(base_ring, 'q')
    q = Q.gen()

    d = len(D)
    prim_chars = [phi.primitive_character() for phi in D]
    divs = divisors(N)

    products = Matrix(base_ring, [])
    indexlist = []
    rank = 0
    if verbose:
        print(D)
        print('Sturm bound', sturm)
        #TODO: target_dim needs refinment in the case of weight 2.
        print('Target dimension', target_dim)
    for i in srange(0, d):  # First character
        phi = prim_chars[i]
        M1 = phi.conductor()
        for j in srange(0, d):  # Second character
            psi = prim_chars[j]
            M2 = psi.conductor()
            if not M1 * M2 in divs:
                continue
            parity = psi(-1) * phi(-1)
            for t1 in divs:
                if not M1 * M2 * t1 in divs:
                    continue
                #TODO: THE NEXT CONDITION NEEDS TO BE CORRECTED. THIS IS JUST A TEST
                if phi.bar() == psi and not (
                        k == 2):  #and i==0 and j==0 and t1==1):
                    E = eisenstein_series_at_inf(phi, psi, k, sturm, t1,
                                                 base_ring).padded_list()
                    try:
                        products.T.solve_right(vector(base_ring, E))
                    except ValueError:
                        products = Matrix(products.rows() + [E])
                        indexlist.append([k, i, j, t1])
                        rank += 1
                        if verbose:
                            print('Added ', [k, i, j, t1])
                            print('Rank is now', rank)
                        if rank == target_dim:
                            return products, indexlist
                for t in divs:
                    if not M1 * M2 * t1 * t in divs:
                        continue
                    for t2 in divs:
                        if not M1 * M2 * t1 * t2 * t in divs:
                            continue
                        for l in weight_dict[parity]:
                            if l == 1 and phi.is_odd():
                                continue
                            if i == 0 and j == 0 and (l == 2 or l == k - 2):
                                continue
                            #TODO: THE NEXT CONDITION NEEDS TO BE REMOVED. THIS IS JUST A TEST
                            if l == 2 or l == k - 2:
                                continue
                            E1 = eisenstein_series_at_inf(
                                phi, psi, l, sturm, t1 * t, base_ring)
                            E2 = eisenstein_series_at_inf(
                                phi**(-1), psi**(-1), k - l, sturm, t2 * t,
                                base_ring)
                            #If chi is non-principal this needs to be changed to be something like chi*phi^(-1) instead of phi^(-1)
                            E = (E1 * E2 + O(q**sturm)).padded_list()
                            try:
                                products.T.solve_right(vector(base_ring, E))
                            except ValueError:
                                products = Matrix(products.rows() + [E])
                                indexlist.append([l, k - l, i, j, t1, t2, t])
                                rank += 1
                                if verbose:
                                    print('Added ',
                                          [l, k - l, i, j, t1, t2, t])
                                    print('Rank', rank)
                                if rank == target_dim:
                                    return products, indexlist
    return products, indexlist