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 \(\ge n\). If constant is a pair \((c,m)\), then the coefficient function is responsible to compute the values of all coefficients of index \(\ge n\) and \(< m\) and all the coefficients of index \(\ge 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