示例#1
0
def cyclotomic_to_gamma(cyclo_up, cyclo_down):
    """
    Convert a quotient of products of cyclotomic polynomials
    to a quotient of products of polynomials `x^n - 1`.

    INPUT:

    - ``cyclo_up`` -- list of indices of cyclotomic polynomials in the numerator
    - ``cyclo_down`` -- list of indices of cyclotomic polynomials in the denominator

    OUTPUT:

    a dictionary mapping an integer `n` to the power of `x^n - 1` that
    appears in the given product

    EXAMPLES::

        sage: from sage.modular.hypergeometric_motive import cyclotomic_to_gamma
        sage: cyclotomic_to_gamma([6], [1])
        {2: -1, 3: -1, 6: 1}
    """
    dico = defaultdict(int)
    for d in cyclo_up:
        dico[d] += 1
    for d in cyclo_down:
        dico[d] -= 1

    resu = defaultdict(int)
    for n in dico:
        for d in divisors(n):
            resu[d] += moebius(n / d) * dico[n]

    return {d: resu[d] for d in resu if resu[d]}
示例#2
0
def AllCusps(N):
    r"""
    Return a list of CuspFamily objects corresponding to the cusps of
    `X_0(N)`.

    INPUT:

    -  ``N`` -- (integer): the level

    EXAMPLES::

        sage: AllCusps(18)
        [(Inf), (c_{2}), (c_{3,1}), (c_{3,2}), (c_{6,1}), (c_{6,2}), (c_{9}), (0)]
        sage: AllCusps(0)
        Traceback (most recent call last):
        ...
        ValueError: N must be positive
    """
    N = ZZ(N)
    if N <= 0:
        raise ValueError("N must be positive")

    c = []
    for d in divisors(N):
        n = num_cusps_of_width(N, d)
        if n == 1:
            c.append(CuspFamily(N, d))
        elif n > 1:
            for i in range(n):
                c.append(CuspFamily(N, d, label=str(i + 1)))
    return c
def cyclotomic_to_gamma(cyclo_up, cyclo_down):
    """
    Convert a quotient of products of cyclotomic polynomials
    to a quotient of products of polynomials `x^n - 1`.

    INPUT:

    - ``cyclo_up`` -- list of indices of cyclotomic polynomials in the numerator
    - ``cyclo_down`` -- list of indices of cyclotomic polynomials in the denominator

    OUTPUT:

    a dictionary mapping an integer `n` to the power of `x^n - 1` that
    appears in the given product

    EXAMPLES::

        sage: from sage.modular.hypergeometric_motive import cyclotomic_to_gamma
        sage: cyclotomic_to_gamma([6], [1])
        {2: -1, 3: -1, 6: 1}
    """
    dico = defaultdict(int)
    for d in cyclo_up:
        dico[d] += 1
    for d in cyclo_down:
        dico[d] -= 1

    resu = defaultdict(int)
    for n in dico:
        for d in divisors(n):
            resu[d] += moebius(n / d) * dico[n]

    return {d: resu[d] for d in resu if resu[d]}
def origami_H2_2cyl_iterator(n, reducible=False, output="coordinates"):
    r"""
    INPUT:

    - ``n`` - positive integer - the number of squares

    - ``reducible`` - bool (default: False) - if True returns also the reducible
      origamis.

    - ``output`` - either "coordinates" or "origami" or "standard origami"
    """
    for n1,n2 in OrderedPartitions(n,k=2):
        for w1 in divisors(n1):
          h1 = n1//w1
          for w2 in filter(lambda x: x < w1, divisors(n2)):
            h2 = n2//w2
            if reducible or gcd(h1,h2) == 1:
              d = gcd(w1,w2)
              if d == 1:
                for t1 in xrange(w1):
                  for t2 in xrange(w2):
                    if output == "coordinates":
                      yield w1,h1,t1,w2,h2,t2
                    elif output == "origami":
                      yield origami_H2_2cyl(w1,h1,t1,w2,h2,t2)
                    elif output == "standard_origami":
                      yield origami_H2_2cyl(w1,h1,t1,w2,h2,t2).standard_form()

              else:
                for t1 in xrange(w1):
                  for t2 in xrange(w2):
                      if reducible or gcd(d,h2*t1-h1*t2) == 1:
                        if output == "coordinates":
                          yield w1,h1,t1,w2,h2,t2
                        elif output == "origami":
                          yield origami_H2_2cyl(w1,h1,t1,w2,h2,t2)
                        elif output == "standard_origami":
                          yield origami_H2_2cyl(w1,h1,t1,w2,h2,t2).standard_form()
示例#5
0
    def reduce_basis(self, long_etas):
        r"""
        Produce a more manageable basis via LLL-reduction.

        INPUT:

        - ``long_etas`` -  a list of EtaGroupElement objects (which
          should all be of the same level)

        OUTPUT:

        - a new list of EtaGroupElement objects having
          hopefully smaller norm

        ALGORITHM: We define the norm of an eta-product to be the
        `L^2` norm of its divisor (as an element of the free
        `\ZZ`-module with the cusps as basis and the
        standard inner product). Applying LLL-reduction to this gives a
        basis of hopefully more tractable elements. Of course we'd like to
        use the `L^1` norm as this is just twice the degree, which
        is a much more natural invariant, but `L^2` norm is easier
        to work with!

        EXAMPLES::

            sage: EtaGroup(4).reduce_basis([ EtaProduct(4, {1:8,2:24,4:-32}), EtaProduct(4, {1:8, 4:-8})])
            [Eta product of level 4 : (eta_1)^8 (eta_4)^-8,
             Eta product of level 4 : (eta_1)^-8 (eta_2)^24 (eta_4)^-16]
        """
        N = self.level()
        cusps = AllCusps(N)
        r = matrix(ZZ,
                   [[et.order_at_cusp(c) for c in cusps] for et in long_etas])
        V = FreeModule(ZZ, r.ncols())
        A = V.submodule_with_basis([V(rw) for rw in r.rows()])
        rred = r.LLL()
        short_etas = []
        for shortvect in rred.rows():
            bv = A.coordinates(shortvect)
            dic = {
                d: sum(bv[i] * long_etas[i].r(d) for i in range(r.nrows()))
                for d in divisors(N)
            }
            short_etas.append(self(dic))
        return short_etas
示例#6
0
def _hermite_normal_forms(r, det):
    """
    Generate all r x r integer matrices in (left) Hermite normal form
    with the given determinant.
    """
    if r == 0:
        if det == 1:
            yield matrix(ZZ, 0, [])

    else:
        for d in divisors(det):
            for A in _hermite_normal_forms(r - 1, det // d):
                for column in itertools.product(range(d), repeat=r - 1):
                    yield matrix(
                        ZZ, r, r, lambda i, j: A[i, j]
                        if i < r - 1 and j < r - 1 else d
                        if i == r - 1 and j == r - 1 else column[i]
                        if j == r - 1 else 0)
示例#7
0
def capital_M(n):
    """
    Auxiliary function, used to describe the canonical scheme.

    INPUT:

    - ``n`` -- an integer

    OUTPUT:

    a rational

    EXAMPLES::

        sage: from sage.modular.hypergeometric_motive import capital_M
        sage: [capital_M(i) for i in range(1,8)]
        [1, 4, 27, 64, 3125, 432, 823543]
    """
    n = ZZ(n)
    return QQ.prod(d ** (d * moebius(n / d)) for d in divisors(n))
def capital_M(n):
    """
    Auxiliary function, used to describe the canonical scheme.

    INPUT:

    - ``n`` -- an integer

    OUTPUT:

    a rational

    EXAMPLES::

        sage: from sage.modular.hypergeometric_motive import capital_M
        sage: [capital_M(i) for i in range(1,8)]
        [1, 4, 27, 64, 3125, 432, 823543]
    """
    n = ZZ(n)
    return QQ.prod(d ** (d * moebius(n / d)) for d in divisors(n))
示例#9
0
    def DivisorLattice(n, facade=None):
        """
        Return the divisor lattice of an integer.

        Elements of the lattice are divisors of `n` and
        `x < y` in the lattice if `x` divides `y`.

        INPUT:

        - ``n`` -- an integer
        - ``facade`` (boolean) -- whether to make the returned poset a
          facade poset (see :mod:`sage.categories.facade_sets`); the
          default behaviour is the same as the default behaviour of
          the :func:`~sage.combinat.posets.posets.Poset` constructor

        EXAMPLES::

            sage: P = Posets.DivisorLattice(12)
            sage: sorted(P.cover_relations())
            [[1, 2], [1, 3], [2, 4], [2, 6], [3, 6], [4, 12], [6, 12]]

            sage: P = Posets.DivisorLattice(10, facade=False)
            sage: P(2) < P(5)
            False

        TESTS::

            sage: Posets.DivisorLattice(1)
            Finite lattice containing 1 elements
        """
        from sage.arith.misc import divisors
        try:
            n = Integer(n)
        except TypeError:
            raise TypeError(
                "number of elements must be an integer, not {0}".format(n))
        if n <= 0:
            raise ValueError("n must be a positive integer")
        return LatticePoset((divisors(n), lambda x, y: y % x == 0),
                            facade=facade,
                            linear_extension=True)
示例#10
0
    def DivisorLattice(n, facade=None):
        """
        Return the divisor lattice of an integer.

        Elements of the lattice are divisors of `n` and
        `x < y` in the lattice if `x` divides `y`.

        INPUT:

        - ``n`` -- an integer
        - ``facade`` (boolean) -- whether to make the returned poset a
          facade poset (see :mod:`sage.categories.facade_sets`); the
          default behaviour is the same as the default behaviour of
          the :func:`~sage.combinat.posets.posets.Poset` constructor

        EXAMPLES::

            sage: P = Posets.DivisorLattice(12)
            sage: sorted(P.cover_relations())
            [[1, 2], [1, 3], [2, 4], [2, 6], [3, 6], [4, 12], [6, 12]]

            sage: P = Posets.DivisorLattice(10, facade=False)
            sage: P(2) < P(5)
            False

        TESTS::

            sage: Posets.DivisorLattice(1)
            Finite lattice containing 1 elements
        """
        from sage.arith.misc import divisors
        try:
            n = Integer(n)
        except TypeError:
            raise TypeError("number of elements must be an integer, not {0}".format(n))
        if n <= 0:
            raise ValueError("n must be a positive integer")
        return LatticePoset( (divisors(n), lambda x, y: y % x == 0),
                             facade=facade, linear_extension=True)
示例#11
0
def gamma_list_to_cyclotomic(galist):
    r"""
    Convert a quotient of products of polynomials `x^n - 1`
    to a quotient of products of cyclotomic polynomials.

    INPUT:

    - ``galist`` -- a list of integers, where an integer `n` represents
      the power `(x^{|n|} - 1)^{\operatorname{sgn}(n)}`

    OUTPUT:

    a pair of list of integers, where `k` represents the cyclotomic
    polynomial `\Phi_k`

    EXAMPLES::

        sage: from sage.modular.hypergeometric_motive import gamma_list_to_cyclotomic
        sage: gamma_list_to_cyclotomic([-1, -1, 2])
        ([2], [1])

        sage: gamma_list_to_cyclotomic([-1, -1, -1, -3, 6])
        ([2, 6], [1, 1, 1])

        sage: gamma_list_to_cyclotomic([-1, 2, 3, -4])
        ([3], [4])

        sage: gamma_list_to_cyclotomic([8,2,2,2,-6,-4,-3,-1])
        ([2, 2, 8], [3, 3, 6])
    """
    resu = defaultdict(int)
    for n in galist:
        eps = sgn(n)
        for d in divisors(abs(n)):
            resu[d] += eps

    return (sorted(d for d in resu for k in range(resu[d])),
            sorted(d for d in resu for k in range(-resu[d])))
示例#12
0
def origami_H2_1cyl_iterator(n, reducible=False, output='coordinates'):
    r"""
    INPUT:

    - ``n`` - positive integer - the number of squares

    - ``reducible`` - bool (default: False) - if True returns also the reducible
      origamis.

    - ``output`` - either "coordinates" or "origami" or "standard origami"
    """
    if reducible:
        hlist = divisors(n)
    else:
        hlist = [1]

    for h in hlist:  
     for p in Partitions(Integer(n/h),length=3):
      for l in CyclicPermutationsOfPartition([p]):
        l1,l2,l3 = l[0]
        if l1 == l2 and l2 == l3:
          if reducible or l1 == 1:
            for t in xrange(l1):
              if output == "coordinates":
                yield l1,l2,l3,h,t
              elif output == "origami":
                yield origami_H2_1cyl(l1,l2,l3,h,t)
              elif output == "standard_origami":
                yield origami_H2_1cyl(l1,l2,l3,h,t).standard_form()

        elif reducible or gcd([l1,l2,l3]) == 1:
          for t in xrange(n):
            if output == "coordinates":
              yield l1,l2,l3,h,t
            elif output == "origami":
              yield origami_H2_1cyl(l1,l2,l3,h,t)
            elif output == "standard_origami":
              yield origami_H2_1cyl(l1,l2,l3,h,t).standard_form()
def gamma_list_to_cyclotomic(galist):
    r"""
    Convert a quotient of products of polynomials `x^n - 1`
    to a quotient of products of cyclotomic polynomials.

    INPUT:

    - ``galist`` -- a list of integers, where an integer `n` represents
      the power `(x^{|n|} - 1)^{\operatorname{sgn}(n)}`

    OUTPUT:

    a pair of list of integers, where `k` represents the cyclotomic
    polynomial `\Phi_k`

    EXAMPLES::

        sage: from sage.modular.hypergeometric_motive import gamma_list_to_cyclotomic
        sage: gamma_list_to_cyclotomic([-1, -1, 2])
        ([2], [1])

        sage: gamma_list_to_cyclotomic([-1, -1, -1, -3, 6])
        ([2, 6], [1, 1, 1])

        sage: gamma_list_to_cyclotomic([-1, 2, 3, -4])
        ([3], [4])

        sage: gamma_list_to_cyclotomic([8,2,2,2,-6,-4,-3,-1])
        ([2, 2, 8], [3, 3, 6])
    """
    resu = defaultdict(int)
    for n in galist:
        eps = sgn(n)
        for d in divisors(abs(n)):
            resu[d] += eps

    return (sorted(d for d in resu for k in range(resu[d])),
            sorted(d for d in resu for k in range(-resu[d])))
示例#14
0
    def basis(self, reduce=True):
        r"""
        Produce a basis for the free abelian group of eta-products of level
        N (under multiplication), attempting to find basis vectors of the
        smallest possible degree.

        INPUT:

        -  ``reduce`` - a boolean (default True) indicating
           whether or not to apply LLL-reduction to the calculated basis

        EXAMPLES::

            sage: EtaGroup(5).basis()
            [Eta product of level 5 : (eta_1)^6 (eta_5)^-6]
            sage: EtaGroup(12).basis()
            [Eta product of level 12 : (eta_1)^-3 (eta_2)^2 (eta_3)^1 (eta_4)^-1 (eta_6)^-2 (eta_12)^3,
             Eta product of level 12 : (eta_1)^-4 (eta_2)^2 (eta_3)^4 (eta_6)^-2,
             Eta product of level 12 : (eta_1)^6 (eta_2)^-9 (eta_3)^-2 (eta_4)^3 (eta_6)^3 (eta_12)^-1,
             Eta product of level 12 : (eta_1)^-1 (eta_2)^3 (eta_3)^3 (eta_4)^-2 (eta_6)^-9 (eta_12)^6,
             Eta product of level 12 : (eta_1)^3 (eta_3)^-1 (eta_4)^-3 (eta_12)^1]
            sage: EtaGroup(12).basis(reduce=False) # much bigger coefficients
            [Eta product of level 12 : (eta_1)^384 (eta_2)^-576 (eta_3)^-696 (eta_4)^216 (eta_6)^576 (eta_12)^96,
             Eta product of level 12 : (eta_2)^24 (eta_12)^-24,
             Eta product of level 12 : (eta_1)^-40 (eta_2)^116 (eta_3)^96 (eta_4)^-30 (eta_6)^-80 (eta_12)^-62,
             Eta product of level 12 : (eta_1)^-4 (eta_2)^-33 (eta_3)^-4 (eta_4)^1 (eta_6)^3 (eta_12)^37,
             Eta product of level 12 : (eta_1)^15 (eta_2)^-24 (eta_3)^-29 (eta_4)^9 (eta_6)^24 (eta_12)^5]

        ALGORITHM: An eta product of level `N` is uniquely
        determined by the integers `r_d` for `d | N` with
        `d < N`, since `\sum_{d | N} r_d = 0`. The valid
        `r_d` are those that satisfy two congruences modulo 24,
        and one congruence modulo 2 for every prime divisor of N. We beef
        up the congruences modulo 2 to congruences modulo 24 by multiplying
        by 12. To calculate the kernel of the ensuing map
        `\ZZ^m \to (\ZZ/24\ZZ)^n`
        we lift it arbitrarily to an integer matrix and calculate its Smith
        normal form. This gives a basis for the lattice.

        This lattice typically contains "large" elements, so by default we
        pass it to the reduce_basis() function which performs
        LLL-reduction to give a more manageable basis.
        """
        N = self.level()
        divs = divisors(N)[:-1]
        s = len(divs)
        primedivs = prime_divisors(N)

        rows = []
        for di in divs:
            # generate a row of relation matrix
            row = [Mod(di, 24) - Mod(N, 24), Mod(N // di, 24) - Mod(1, 24)]
            for p in primedivs:
                row.append(Mod(12 * (N // di).valuation(p), 24))
            rows.append(row)

        M = matrix(IntegerModRing(24), rows)
        Mlift = M.change_ring(ZZ)
        # now we compute elementary factors of Mlift
        S, U, V = Mlift.smith_form()
        good_vects = []
        for i in range(U.nrows()):
            vect = U.row(i)
            nf = (i < S.ncols() and S[i, i]) or 0  # ?
            good_vects.append((vect * 24 / gcd(nf, 24)).list())
        for v in good_vects:
            v.append(-sum([r for r in v]))
        dicts = []
        for v in good_vects:
            dicts.append({})
            for i in range(s):
                dicts[-1][divs[i]] = v[i]
            dicts[-1][N] = v[-1]
        if reduce:
            return self.reduce_basis([self(d) for d in dicts])
        else:
            return [self(d) for d in dicts]
示例#15
0
def weight_two_basis_from_theta_blocks(N,
                                       prec,
                                       dim,
                                       jacobiforms=None,
                                       verbose=False):
    r"""
    Look for theta blocks of weight two and given index among the infinite families of weight two theta blocks associated to the root systems A_4, B_2+G_2, A_1+B_3, A_1+C_3

    This is not meant to be called directly. Use JacobiForms(N).basis(2) with the optional parameter try_theta_blocks = True.

    INPUT:
    - ``N`` -- the index
    - ``prec`` -- precision
    - ``dim`` -- the dimension of the space of Jacobi forms.
    - ``jacobiforms`` -- a JacobiForms instance for this index. (If none is provided then we construct one now.)
    - ``verbose`` -- boolean (default False); if True then we comment on the computations.

    OUTPUT: a list of JacobiForm's
    """
    if not jacobiforms:
        jacobiforms = JacobiForms(N)
    rank = 0
    #the four families of weight two theta blocks from root lattices
    thetablockQ_1 = QuadraticForm(
        matrix([[4, 3, 2, 1], [3, 6, 4, 2], [2, 4, 6, 3], [1, 2, 3, 4]]))
    thetablockQ_2 = QuadraticForm(
        matrix([[24, 12, 0, 0], [12, 8, 0, 0], [0, 0, 12, 6], [0, 0, 6, 6]]))
    thetablockQ_3 = QuadraticForm(
        matrix([[4, 0, 0, 0], [0, 20, 10, 20], [0, 10, 10, 20],
                [0, 20, 20, 60]]))
    thetablockQ_4 = QuadraticForm(
        matrix([[4, 0, 0, 0], [0, 8, 8, 4], [0, 8, 16, 8], [0, 4, 8, 6]]))
    thetablock_tuple = thetablockQ_1, thetablockQ_2, thetablockQ_3, thetablockQ_4
    from .jacobi_forms_class import theta_block
    thetablock_1 = lambda a, b, c, d, prec: theta_block(
        [a, a + b, a + b + c, a + b + c + d, b, b + c, b + c + d, c, c + d, d],
        -6,
        prec,
        jacobiforms=jacobiforms)
    thetablock_2 = lambda a, b, c, d, prec: theta_block([
        a, 3 * a + b, 3 * a + b + b, a + a + b, a + b, b, c + c, c + c + d, 2 *
        (c + d), d
    ],
                                                        -6,
                                                        prec,
                                                        jacobiforms=jacobiforms
                                                        )
    thetablock_3 = lambda a, b, c, d, prec: theta_block([
        a + a, b + b, b + b + c, 2 * (b + c + d + d), b + b + c + d + d, b + b
        + c + 4 * d, c, c + d + d, c + 4 * d, d + d
    ],
                                                        -6,
                                                        prec,
                                                        jacobiforms=jacobiforms
                                                        )
    thetablock_4 = lambda a, b, c, d, prec: theta_block([
        a + a, b, b + b + c + c + d, b + c, b + c + c + d, b + c + d, c, c + c
        + d, c + d, d
    ],
                                                        -6,
                                                        prec,
                                                        jacobiforms=jacobiforms
                                                        )
    thetablocks = thetablock_1, thetablock_2, thetablock_3, thetablock_4
    basis = []
    basis_vectors = []
    pivots = []
    div_N = divisors(N)
    div_N.reverse()
    for i, Q in enumerate(thetablock_tuple):
        v_list = Q.short_vector_list_up_to_length(N + 1, up_to_sign_flag=True)
        if verbose:
            print(
                'I am looking through the theta block family of the root system %s.'
                % ['A_4', 'B_2+G_2', 'A_1+B_3', 'A_1+C_3'][i])
        for _d in div_N:
            prec_d = prec * (N // _d)
            for v in v_list[_d]:
                a, b, c, d = v
                try:
                    j = thetablocks[i](a, b, c, d, prec_d).hecke_V(N // _d)
                    old_rank = 0 + rank
                    basis, basis_vectors, pivots, rank = update_echelon_form_with(
                        j, basis, basis_vectors, pivots, rank, sage_one_eighth)
                    if verbose and old_rank < rank:
                        if i == 0:
                            L = [
                                abs(x)
                                for x in (a, a + b, a + b + c, a + b + c + d,
                                          b, b + c, b + c + d, c, c + d, d)
                            ]
                        elif i == 1:
                            L = [
                                abs(x) for x in (a, 3 * a + b, 3 * a + b + b,
                                                 a + a + b, a + b, b, c + c,
                                                 c + c + d, 2 * (c + d), d)
                            ]
                        elif i == 2:
                            L = [
                                abs(x)
                                for x in (a + a, b + b, b + b + c,
                                          2 * (b + c + d + d),
                                          b + b + c + d + d, b + b + c + 4 * d,
                                          c, c + d + d, c + 4 * d, d + d)
                            ]
                        else:
                            L = [
                                abs(x)
                                for x in (a + a, b, b + b + c + c + d, b + c,
                                          b + c + c + d, b + c + d, c,
                                          c + c + d, c + d, d)
                            ]
                        L.sort()
                        print('I found the theta block Th_' + str(L) +
                              ' / eta^6.')
                    if rank == dim:
                        return basis
                except ValueError:
                    pass
    if verbose:
        print(
            'I did not find enough theta blocks. Time to try something else.')
    return jacobiforms.basis(2, prec, try_theta_blocks=False, verbose=verbose)
示例#16
0
def weight_three_basis_from_theta_blocks(N,
                                         prec,
                                         dim,
                                         jacobiforms=None,
                                         verbose=False):
    r"""
    Look for theta blocks of weight three and given index among the infinite families of weight three theta blocks associated to the root systems B_3, C_3, A_2+A_3, 3A_2, 2A_1 + A_2 + B_2, 3A_1 + A_3, A_6, A_1+D_5.

    This is not meant to be called directly. Use JacobiForms(N).basis(3) with the optional parameter try_theta_blocks = True.

    INPUT:
    - ``N`` -- the index
    - ``prec`` -- precision
    - ``dim`` -- the dimension of the space of Jacobi forms.
    - ``jacobiforms`` -- a JacobiForms instance for this index. (If none is provided then we construct one now.)
    - ``verbose`` -- boolean (default False); if True then we comment on the computations.

    OUTPUT: a list of JacobiForm's
    """
    if not jacobiforms:
        jacobiforms = JacobiForms(N)
    rank = 0
    thetablockQ_1 = QuadraticForm(
        matrix([[20, 10, 20], [10, 10, 20], [20, 20, 60]]))  #B3
    thetablockQ_2 = QuadraticForm(matrix([[8, 8, 4], [8, 16, 8], [4, 8,
                                                                  6]]))  #C3
    thetablockQ_3 = QuadraticForm(
        matrix([[2, 1, 0, 0, 0], [1, 2, 0, 0, 0], [0, 0, 12, 4, 4],
                [0, 0, 4, 4, 4], [0, 0, 4, 4, 12]]))  #A2 + A3
    thetablockQ_4 = QuadraticForm(
        matrix([[2, 1, 0, 0, 0, 0], [1, 2, 0, 0, 0, 0], [0, 0, 2, 1, 0, 0],
                [0, 0, 1, 2, 0, 0], [0, 0, 0, 0, 2, 1], [0, 0, 0, 0, 1,
                                                         2]]))  #3 A2
    thetablockQ_5 = QuadraticForm(
        matrix([[4, 0, 0, 0, 0, 0], [0, 4, 0, 0, 0, 0], [0, 0, 2, 1, 0, 0],
                [0, 0, 1, 2, 0, 0], [0, 0, 0, 0, 6, 6], [0, 0, 0, 0, 6,
                                                         12]]))  #2A1 + A2 + B2
    thetablockQ_6 = QuadraticForm(
        matrix([[4, 0, 0, 0, 0, 0], [0, 4, 0, 0, 0, 0], [0, 0, 4, 0, 0, 0],
                [0, 0, 0, 12, 4, 4], [0, 0, 0, 4, 4, 4], [0, 0, 0, 4, 4,
                                                          12]]))  #3A1 + A3
    thetablockQ_7 = QuadraticForm(
        matrix([[6, 5, 4, 3, 2, 1], [5, 10, 8, 6, 4, 2], [4, 8, 12, 9, 6, 3],
                [3, 6, 9, 12, 8, 4], [2, 4, 6, 8, 10, 5], [1, 2, 3, 4, 5,
                                                           6]]))  #A6
    thetablockQ_8 = QuadraticForm(
        matrix([[4, 0, 0, 0, 0, 0], [0, 10, 6, 12, 8, 4], [0, 6, 10, 12, 8, 4],
                [0, 12, 12, 24, 16, 8], [0, 8, 8, 16, 16, 8],
                [0, 4, 4, 8, 8, 8]]))  #A1 + D5
    thetablock_tuple = thetablockQ_1, thetablockQ_2, thetablockQ_3, thetablockQ_4, thetablockQ_5, thetablockQ_6, thetablockQ_7, thetablockQ_8
    args1 = lambda b, c, d: [
        b + b, b + b + c, 2 * (b + c + d + d), b + b + c + d + d, b + b + c + 4
        * d, c, c + d + d, c + 4 * d, d + d
    ]
    args2 = lambda b, c, d: [
        b, b + b + c + c + d, b + c, b + c + c + d, b + c + d, c, c + c + d, c
        + d, d
    ]
    args3 = lambda a, b, d, e, f: [
        a, a + b, b, d + d, e, d + d + e, f + f, e + f + f, d + d + e + f + f
    ]
    args4 = lambda a, b, c, d, e, f: [a, a + b, b, c, c + d, d, e, e + f, f]
    args5 = lambda a, b, c, d, e, f: [
        a + a, b + b, c, c + d, d, e, f + f, e + f + f, e + e + f + f
    ]
    args6 = lambda a, b, c, d, e, f: [
        a + a, b + b, c + c, d + d, e, d + d + e, f + f, e + f + f, d + d + e +
        f + f
    ]
    args7 = lambda a, b, c, d, e, f: [
        a, a + b, a + b + c, a + b + c + d, a + b + c + d + e, a + b + c + d +
        e + f, b, b + c, b + c + d, b + c + d + e, b + c + d + e + f, c, c + d,
        c + d + e, c + d + e + f, d, d + e, d + e + f, e, e + f, f
    ]
    args8 = lambda a, b, c, d, e, f: [
        a + a, b, c, d, b + d, c + d, b + c + d, e, d + e, b + d + e, c + d +
        e, b + c + d + e, b + c + 2 * d + e, f, e + f, d + e + f, b + d + e +
        f, c + d + e + f, b + c + d + e + f, b + c + 2 * d + e + f, b + c + 2 *
        d + 2 * e + f
    ]
    args_tuple = args1, args2, args3, args4, args5, args6, args7, args8
    from .jacobi_forms_class import theta_block
    thetablock_1 = lambda b, c, d, prec: theta_block(
        args1(b, c, d), -3, prec, jacobiforms=jacobiforms)
    thetablock_2 = lambda b, c, d, prec: theta_block(
        args2(b, c, d), -3, prec, jacobiforms=jacobiforms)
    thetablock_3 = lambda a, b, d, e, f, prec: theta_block(
        args3(a, b, d, e, f), -3, prec, jacobiforms=jacobiforms)
    thetablock_4 = lambda a, b, c, d, e, f, prec: theta_block(
        args4(a, b, c, d, e, f), -3, prec, jacobiforms=jacobiforms)
    thetablock_5 = lambda a, b, c, d, e, f, prec: theta_block(
        args5(a, b, c, d, e, f), -3, prec, jacobiforms=jacobiforms)
    thetablock_6 = lambda a, b, c, d, e, f, prec: theta_block(
        args6(a, b, c, d, e, f), -3, prec, jacobiforms=jacobiforms)
    thetablock_7 = lambda a, b, c, d, e, f, prec: theta_block(
        args7(a, b, c, d, e, f), -15, prec, jacobiforms=jacobiforms)
    thetablock_8 = lambda a, b, c, d, e, f, prec: theta_block(
        args8(a, b, c, d, e, f), -15, prec, jacobiforms=jacobiforms)
    thetablocks = thetablock_1, thetablock_2, thetablock_3, thetablock_4, thetablock_5, thetablock_6, thetablock_7, thetablock_8
    basis = []
    basis_vectors = []
    pivots = []
    div_N = divisors(N)
    div_N.reverse()
    for i, Q in enumerate(thetablock_tuple):
        v_list = Q.short_vector_list_up_to_length(N + 1, up_to_sign_flag=True)
        if verbose:
            print(
                'I am looking through the theta block family of the root system %s.'
                % [
                    'B_3', 'C_3', 'A_2+A_3', '3A_2', '2A_1+A_2+B_2',
                    '3A_1 + A_3', 'A_6', 'A_1+D_5'
                ][i])
        for _d in div_N:
            prec_d = prec * (N // _d)
            for v in v_list[_d]:
                if all(a for a in args_tuple[i](*v)):
                    try:
                        j = thetablocks[i](*(list(v) + [prec])).hecke_V(N //
                                                                        _d)
                        old_rank = 0 + rank
                        basis, basis_vectors, pivots, rank = update_echelon_form_with(
                            j, basis, basis_vectors, pivots, rank,
                            sage_one_eighth)
                        if verbose and old_rank < rank:
                            L = [abs(x) for x in args_tuple[i](*v)]
                            L.sort()
                            if _d == N:
                                print('I found the theta block Th_' + str(L) +
                                      [' / eta^3.', ' / eta^15.'][i >= 6])
                            else:
                                print(
                                    'I applied the Hecke operator V_%d to the theta block Th_'
                                    % (N // _d) + str(L) +
                                    [' / eta^3.', ' / eta^15.'][i >= 6])
                        if rank == dim:
                            return basis
                    except ValueError:
                        pass
    if verbose:
        print(
            'I did not find enough theta blocks. Time to try something else.')
    return jacobiforms.basis(2, prec, try_theta_blocks=False, verbose=verbose)