Lazy Laurent Series

A lazy Laurent series is a Laurent series whose coefficients are computed as demanded or needed. Unlike the usual Laurent series in Sage, lazy Laurent series do not have precisions because a lazy Laurent series knows (can be computed, lazily) all its coefficients.

EXAMPLES:

Generating functions are Laurent series over the integer ring:

sage: L.<z> = LazyLaurentSeriesRing(ZZ)

This defines the generating function of Fibonacci sequence:

sage: def coeff(s, i):
....:     if i in [0, 1]:
....:         return 1
....:     else:
....:         return s.coefficient(i - 1) + s.coefficient(i - 2)
sage: f = L.series(coeff, valuation=0); f
1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ...

The 100th element of Fibonacci sequence can be obtained from the generating function:

sage: f.coefficient(100)
573147844013817084101

Coefficients are computed and cached only when necessary:

sage: f._cache[100]
573147844013817084101
sage: f._cache[101]
Traceback (most recent call last):
...
KeyError: 101

You can do arithmetic with lazy power series:

sage: f
1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ...
sage: f^-1
1 - z - z^2 + ...
sage: f + f^-1
2 + z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ...
sage: g = (f + f^-1)*(f - f^-1); g
4*z + 6*z^2 + 8*z^3 + 19*z^4 + 38*z^5 + 71*z^6 + ...

You may need to change the base ring:

sage: h = g.change_ring(QQ)
sage: h.parent()
Lazy Laurent Series Ring in z over Rational Field
sage: h
4*z + 6*z^2 + 8*z^3 + 19*z^4 + 38*z^5 + 71*z^6 + ...
sage: h^-1
1/4*z^-1 - 3/8 + 1/16*z - 17/32*z^2 + 5/64*z^3 - 29/128*z^4 + 165/256*z^5 + ...
sage: _.valuation()
-1

AUTHORS:

  • Kwankyu Lee (2019-02-24): initial version

class sage.rings.lazy_laurent_series.LazyLaurentSeries(parent, coefficient=None, valuation=0, constant=None)

Bases: sage.structure.element.ModuleElement

Return a lazy Laurent series.

INPUT:

  • coefficient – Python function that computes coefficients

  • valuation – integer; approximate valuation of the series

  • constant – either None or pair of an element of the base ring and an integer

Let the coefficient of index i mean the coefficient of the term of the series with exponent i.

Python function coefficient returns the value of the coefficient of index i from input s and i where s is the series itself.

Let valuation be n. All coefficients of index below n are zero. If constant is None, then the coefficient function is responsible to compute the values of all coefficients of index n. If constant is a pair (c,m), then the coefficient function is responsible to compute the values of all coefficients of index n and <m and all the coefficients of index m is the constant c.

EXAMPLES:

sage: L = LazyLaurentSeriesRing(ZZ, 'z')
sage: L.series(lambda s, i: i, valuation=-3, constant=(-1,3))
-3*z^-3 - 2*z^-2 - z^-1 + z + 2*z^2 - z^3 - z^4 - z^5 + ...
sage: def coeff(s, i):
....:     if i in [0, 1]:
....:         return 1
....:     else:
....:         return s.coefficient(i - 1) + s.coefficient(i - 2)
sage: f = L.series(coeff, valuation=0); f
1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ...
sage: f.coefficient(100)
573147844013817084101

Lazy Laurent series is picklable:

sage: z = L.gen()
sage: f = 1/(1 - z - z^2)
sage: f
1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ...
sage: g = loads(dumps(f))
sage: g
1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ...
sage: g == f
True
apply_to_coefficients(function)

Return the series with function applied to each coefficient of this series.

INPUT:

  • function – Python function

Python function function returns a new coefficient for input coefficient.

EXAMPLES:

sage: L.<z> = LazyLaurentSeriesRing(ZZ)
sage: s = z/(1 - 2*z)
sage: t = s.apply_to_coefficients(lambda c: c + 1)
sage: s
z + 2*z^2 + 4*z^3 + 8*z^4 + 16*z^5 + 32*z^6 + 64*z^7 + ...
sage: t
2*z + 3*z^2 + 5*z^3 + 9*z^4 + 17*z^5 + 33*z^6 + 65*z^7 + ...
approximate_series(prec, name=None)

Return the Laurent series with absolute precision prec approximated from this series.

INPUT:

  • prec – an integer

  • name – name of the variable; if it is None, the name of the variable of the series is used

OUTPUT: a Laurent series with absolute precision prec

EXAMPLES:

sage: L = LazyLaurentSeriesRing(ZZ, 'z')
sage: z = L.gen()
sage: f = (z - 2*z^3)^5/(1 - 2*z)
sage: f
z^5 + 2*z^6 - 6*z^7 - 12*z^8 + 16*z^9 + 32*z^10 - 16*z^11 + ...
sage: g = f.approximate_series(10)
sage: g
z^5 + 2*z^6 - 6*z^7 - 12*z^8 + 16*z^9 + O(z^10)
sage: g.parent()
Power Series Ring in z over Integer Ring
sage: h = (f^-1).approximate_series(3)
sage: h
z^-5 - 2*z^-4 + 10*z^-3 - 20*z^-2 + 60*z^-1 - 120 + 280*z - 560*z^2 + O(z^3)
sage: h.parent()
Laurent Series Ring in z over Integer Ring
change_ring(ring)

Return this series with coefficients converted to elements of ring.

INPUT:

  • ring – a ring

EXAMPLES:

sage: L.<z> = LazyLaurentSeriesRing(ZZ)
sage: s = 2 + z
sage: t = s.change_ring(QQ)
sage: t^-1
1/2 - 1/4*z + 1/8*z^2 - 1/16*z^3 + 1/32*z^4 - 1/64*z^5 + 1/128*z^6 + ...
coefficient(n)

Return the coefficient of the term with exponent n of the series.

INPUT:

  • n – integer

EXAMPLES:

sage: L = LazyLaurentSeriesRing(ZZ, 'z')
sage: def g(s, i):
....:     if i == 0:
....:         return 1
....:     else:
....:         return sum(s.coefficient(j)*s.coefficient(i - 1 -j) for j in [0..i-1])
sage: e = L.series(g, valuation=0)
sage: e.coefficient(10)
16796
sage: e
1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ...
polynomial(degree=None, name=None)

Return the polynomial or Laurent polynomial if the series is actually so.

INPUT:

  • degreeNone or an integer

  • name – name of the variable; if it is None, the name of the variable of the series is used

OUTPUT: a Laurent polynomial if the valuation of the series is negative or a polynomial otherwise.

If degree is not None, the terms of the series of degree greater than degree are truncated first. If degree is None and the series is not a polynomial or a Laurent polynomial, a ValueError is raised.

EXAMPLES:

sage: L = LazyLaurentSeriesRing(ZZ, 'z')
sage: f = L.series([1,0,0,2,0,0,0,3], 5); f
z^5 + 2*z^8 + 3*z^12
sage: f.polynomial()
3*z^12 + 2*z^8 + z^5
sage: g = L.series([1,0,0,2,0,0,0,3], -5); g
z^-5 + 2*z^-2 + 3*z^2
sage: g.polynomial()
z^-5 + 2*z^-2 + 3*z^2
sage: z = L.gen()
sage: f = (1 + z)/(z^3 - z^5)
sage: f
z^-3 + z^-2 + z^-1 + 1 + z + z^2 + z^3 + ...
sage: f.polynomial(5)
z^-3 + z^-2 + z^-1 + 1 + z + z^2 + z^3 + z^4 + z^5
sage: f.polynomial(0)
z^-3 + z^-2 + z^-1 + 1
sage: f.polynomial(-5)
0
prec()

Return the precision of the series, which is infinity.

EXAMPLES:

sage: L.<z> = LazyLaurentSeriesRing(ZZ)
sage: f = 1/(1 - z)
sage: f.prec()
+Infinity
truncate(d)

Return this series with its terms of degree >= d truncated.

INPUT:

  • d – integer

EXAMPLES:

sage: L.<z> = LazyLaurentSeriesRing(ZZ)
sage: alpha = 1/(1-z)
sage: alpha
1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ...
sage: beta = alpha.truncate(5)
sage: beta
1 + z + z^2 + z^3 + z^4
sage: alpha - beta
z^5 + z^6 + z^7 + z^8 + z^9 + z^10 + z^11 + ...
valuation()

Return the valuation of the series.

This method determines the valuation of the series by looking for a nonzero coefficient. Hence if the series happens to be zero, then it may run forever.

EXAMPLES:

sage: L.<z> = LazyLaurentSeriesRing(ZZ)
sage: s = 1/(1 - z) - 1/(1 - 2*z)
sage: s.valuation()
1
sage: t = z - z
sage: t.valuation()
+Infinity