Example #1
0
def _step_upper_bound_low_mem(r, m, q, generating_function):
    r"""
    Low memory implementation of :func:`_step_upper_bound_internal`.

    Significantly slower, but the memory footprint does not significantly
    increase even if the series coefficients need to be computed to very high
    degree terms.
    """
    L = LieAlgebra(ZZ, ['X_%d' % k for k in range(r)]).Lyndon()
    dim_fm = L.graded_dimension(m)

    PR = PolynomialRing(ZZ, 't')
    t = PR.gen()
    a = (1 - dim_fm * (1 - t**q)) * t**m
    b = PR.one()
    for k in range(1, m):
        b *= (1 - t**k)**L.graded_dimension(k)

    # extract initial coefficients from a symbolic series expansion
    bd = b.degree()
    id = max(a.degree() + 1, bd)
    offset = id - bd
    quot = SR(a / b)
    sym_t = SR(t)
    qs = quot.series(sym_t, id)

    # check if partial sum is positive already within series expansion
    # store the last offset...id terms to start the linear recurrence
    coeffs = deque()
    cumul = ZZ.zero()
    for s in range(id):
        c = ZZ(qs.coefficient(sym_t, s))
        cumul += c
        if s >= offset:
            coeffs.append(c)
        if cumul > 0:
            if generating_function:
                return s, quot
            return s

    # the rest of the coefficients are defined by a recurrence relation
    multipliers = [-b.monomial_coefficient(t**(bd - k)) for k in range(bd)]
    while cumul <= 0:
        c_next = sum(c * m for c, m in zip(coeffs, multipliers))
        cumul += c_next
        s += 1
        coeffs.append(c_next)
        coeffs.popleft()

    if generating_function:
        return s, quot
    return s