From e5c89c9675bfed04fa99384aa8e7cc07e1702cd8 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Fri, 30 Jul 2021 17:25:26 +0530 Subject: [PATCH] Started moving eventually_geometric to exact. There are still many issues left. --- .../data_structures/coefficient_stream.py | 391 ++++++++++++------ src/sage/rings/lazy_laurent_series.py | 83 ++-- src/sage/rings/lazy_laurent_series_ring.py | 19 +- 3 files changed, 319 insertions(+), 174 deletions(-) diff --git a/src/sage/data_structures/coefficient_stream.py b/src/sage/data_structures/coefficient_stream.py index f5da2a09023..4ec569317de 100644 --- a/src/sage/data_structures/coefficient_stream.py +++ b/src/sage/data_structures/coefficient_stream.py @@ -41,8 +41,8 @@ Coefficient streams can be multiplied:: - sage: from sage.data_structures.coefficient_stream import CoefficientStream_mul - sage: h = CoefficientStream_mul(f, g) + sage: from sage.data_structures.coefficient_stream import CoefficientStream_cauchy_product + sage: h = CoefficientStream_cauchy_product(f, g) sage: [h[i] for i in range(10)] [0, 1, 3, 6, 10, 15, 21, 28, 36, 45] @@ -77,8 +77,8 @@ The multiplicative inverse of a series can also be obtained:: - sage: from sage.data_structures.coefficient_stream import CoefficientStream_inv - sage: h = CoefficientStream_inv(g) + sage: from sage.data_structures.coefficient_stream import CoefficientStream_cauchy_inverse + sage: h = CoefficientStream_cauchy_inverse(g) sage: [h[i] for i in range(10)] [-2, 1, 0, 0, 0, 0, 0, 0, 0, 0] @@ -116,7 +116,7 @@ class CoefficientStream(): INPUT: - ``sparse`` -- boolean; whether the implementation of the series is sparse - - ``approximate_valuation`` -- the approximate valuation of the series + - ``approximate_valuation`` -- integer; a lower bound for the valuation of the series """ def __init__(self, sparse, approximate_valuation): """ @@ -140,7 +140,7 @@ class CoefficientStream_inexact(CoefficientStream): INPUT: - ``sparse`` -- boolean; whether the implementation of the series is sparse - - ``approximate_valuation`` -- integer; the approximate valuation of the series + - ``approximate_valuation`` -- integer; a lower bound for the valuation of the series """ def __init__(self, is_sparse, approximate_valuation): """ @@ -173,11 +173,11 @@ def __getstate__(self): TESTS:: - sage: from sage.data_structures.coefficient_stream import CoefficientStream_eventually_geometric + sage: from sage.data_structures.coefficient_stream import CoefficientStream_exact sage: from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing sage: Y = LaurentPolynomialRing(QQ, 'z') - sage: h = CoefficientStream_eventually_geometric(Y(1), True) - sage: g = CoefficientStream_eventually_geometric(Y([1, -1, -1]), True) + sage: h = CoefficientStream_exact(Y(1), True) + sage: g = CoefficientStream_exact(Y([1, -1, -1]), True) sage: from sage.data_structures.coefficient_stream import CoefficientStream_div sage: u = CoefficientStream_div(h, g) sage: [u[i] for i in range(10)] @@ -204,11 +204,11 @@ def __setstate__(self, d): TESTS:: - sage: from sage.data_structures.coefficient_stream import CoefficientStream_eventually_geometric + sage: from sage.data_structures.coefficient_stream import CoefficientStream_exact sage: from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing sage: Y = LaurentPolynomialRing(QQ, 'z') - sage: h = CoefficientStream_eventually_geometric(Y(-1), True) - sage: g = CoefficientStream_eventually_geometric(Y([1, -1]), True) + sage: h = CoefficientStream_exact(Y(-1), True) + sage: g = CoefficientStream_exact(Y([1, -1]), True) sage: from sage.data_structures.coefficient_stream import CoefficientStream_div sage: u = CoefficientStream_div(h, g) sage: [u[i] for i in range(10)] @@ -314,75 +314,67 @@ def valuation(self): n += 1 -class CoefficientStream_eventually_geometric(CoefficientStream): +class CoefficientStream_exact(CoefficientStream): r""" - Coefficient stream for a series which is known to be eventually geometric. + A stream of eventually constant coefficients. INPUT: - - ``laurent_polynomial`` -- a Laurent polynomial - - ``is_sparse`` -- boolean; specifies whether the series is sparse - - ``constant`` -- (default: 0) the eventual constant value - - ``degree`` -- (default: the degree of ``laurent_polynomial`` plus 1) - the degree where the coefficient stream becomes ``constant`` + - ``initial_values`` -- a list of initial values - EXAMPLES:: + - ``is_sparse`` -- a boolean, which specifies whether the + series is sparse - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1 + z^2 - sage: f[2] - 1 - - You can do arithmetic operations with eventually geometric series:: - - sage: g = z^3 + 1 - sage: s = f + g - sage: s[2] - 1 - sage: s - 2 + z^2 + z^3 - sage: s = f - g - sage: s[2] - 1 - sage: s - z^2 - z^3 - """ + - ``valuation`` -- (default: 0), an integer, determining the + degree of the first element of ``initial_values`` - def __init__(self, laurent_polynomial, is_sparse, constant=None, degree=None): + - ``degree`` -- (default: None), an integer, determining the + degree of the first element which is known to be equal to + ``constant`` + + - ``constant`` -- (default: 0), an integer, the coefficient + of every index larger than or equal to ``degree`` + """ + def __init__(self, initial_coefficients, is_sparse, constant=None, degree=None, valuation=None): """ - Initialize ``self``. + Initialize a series that is known to be eventually geometric. TESTS:: - sage: R. = LaurentPolynomialRing(QQ) - sage: from sage.data_structures.coefficient_stream import CoefficientStream_eventually_geometric - sage: X = CoefficientStream_eventually_geometric(z^2 + z^-1, False) - sage: [X[i] for i in range(-1,8)] - [1, 0, 0, 1, 0, 0, 0, 0, 0] - sage: X = CoefficientStream_eventually_geometric(z^2 + z^-1, True, 5) - sage: [X[i] for i in range(-1,8)] - [1, 0, 0, 1, 5, 5, 5, 5, 5] - sage: X = CoefficientStream_eventually_geometric(z^2 + z^-1, False, 5, 4) - sage: [X[i] for i in range(-1,8)] - [1, 0, 0, 1, 0, 5, 5, 5, 5] + sage: from sage.data_structures.coefficient_stream import CoefficientStream_exact + sage: CoefficientStream_exact([], False)[0] + 0 + """ if constant is None: - constant = ZZ.zero() + self._constant = ZZ.zero() + else: + self._constant = constant + if valuation is None: + valuation = 0 if degree is None: - if not laurent_polynomial: - raise ValueError("you must specify the degree for the polynomial 0") - degree = laurent_polynomial.degree() + 1 + self._degree = valuation + len(initial_coefficients) else: - # Consistency check - assert not laurent_polynomial or laurent_polynomial.degree() < degree - - self._constant = constant - self._degree = degree - self._laurent_polynomial = laurent_polynomial - if not laurent_polynomial: - valuation = degree + self._degree = degree + + assert valuation + len(initial_coefficients) <= self._degree + + for i, v in enumerate(initial_coefficients): + if v: + valuation += i + initial_coefficients = initial_coefficients[i:] + for j, w in enumerate(reversed(initial_coefficients)): + if w: + break + initial_coefficients.pop() + self._initial_coefficients = tuple(initial_coefficients) + break else: - valuation = laurent_polynomial.valuation() + valuation = self._degree + self._initial_coefficients = tuple() + + assert self._initial_coefficients or self._constant, "CoefficientStream_exact should only be used for non-zero streams" + super().__init__(is_sparse, valuation) def __getitem__(self, n): @@ -391,35 +383,43 @@ def __getitem__(self, n): INPUT: - - ``n`` -- integer; the degree for which the coefficient is required + - ``n`` -- integer, the degree for which the coefficient is required EXAMPLES:: - sage: R. = LaurentPolynomialRing(QQ) - sage: from sage.data_structures.coefficient_stream import CoefficientStream_eventually_geometric - sage: f = CoefficientStream_eventually_geometric(z^2 + z^-1, False, 3, 10) - sage: f[2] - 1 - sage: f[8] - 0 - sage: f[15] - 3 + sage: from sage.data_structures.coefficient_stream import CoefficientStream_exact + sage: s = CoefficientStream_exact([], False) + sage: [s[i] for i in range(-2, 5)] + [0, 0, 0, 0, 0, 0, 0] + + sage: s = CoefficientStream_exact([], False, constant=1) + sage: [s[i] for i in range(-2, 5)] + [0, 0, 1, 1, 1, 1, 1] + + sage: s = CoefficientStream_exact([2], False, constant=1) + sage: [s[i] for i in range(-2, 5)] + [0, 0, 2, 1, 1, 1, 1] + + sage: s = CoefficientStream_exact([2], False, valuation=-1, constant=1) + sage: [s[i] for i in range(-2, 5)] + [0, 2, 1, 1, 1, 1, 1] + + sage: s = CoefficientStream_exact([2], False, valuation=-1, degree=2, constant=1) + sage: [s[i] for i in range(-2, 5)] + [0, 2, 0, 0, 1, 1, 1] + + sage: t = CoefficientStream_exact([0, 2, 0], False, valuation=-2, degree=2, constant=1) + sage: t == s + True """ if n >= self._degree: return self._constant - return self._laurent_polynomial[n] + i = n - self._approximate_valuation + if i < 0 or i >= len(self._initial_coefficients): + return 0 + return self._initial_coefficients[i] def valuation(self): - """ - Return the valuation of the series. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(QQ) - sage: f = 1 + z + z^2 + z^3 - sage: f.valuation() - 0 - """ return self._approximate_valuation def __hash__(self): @@ -433,7 +433,7 @@ def __hash__(self): sage: {f: 1} {1 + z + z^2 + z^3: 1} """ - return hash((self._laurent_polynomial, self._degree, self._constant)) + return hash((self._initial_coefficients, self._degree, self._constant)) def __eq__(self, other): """ @@ -441,7 +441,7 @@ def __eq__(self, other): INPUT: - - ``other`` -- a stream for a series which is known to be eventually geometric + - ``other`` -- a lazy Laurent series which is known to be eventaully geometric EXAMPLES:: @@ -453,10 +453,153 @@ def __eq__(self, other): """ return (isinstance(other, type(self)) and self._degree == other._degree - and self._laurent_polynomial == other._laurent_polynomial + and self._initial_coefficients == other._initial_coefficients and self._constant == other._constant) +# class CoefficientStream_exact(CoefficientStream): +# r""" +# Coefficient stream for a series which is known to be eventually geometric. + +# INPUT: + +# - ``laurent_polynomial`` -- a Laurent polynomial +# - ``is_sparse`` -- boolean; specifies whether the series is sparse +# - ``constant`` -- (default: 0) the eventual constant value +# - ``degree`` -- (default: the degree of ``laurent_polynomial`` plus 1) +# the degree where the coefficient stream becomes ``constant`` + +# EXAMPLES:: + +# sage: L. = LazyLaurentSeriesRing(ZZ) +# sage: f = 1 + z^2 +# sage: f[2] +# 1 + +# You can do arithmetic operations with eventually geometric series:: + +# sage: g = z^3 + 1 +# sage: s = f + g +# sage: s[2] +# 1 +# sage: s +# 2 + z^2 + z^3 +# sage: s = f - g +# sage: s[2] +# 1 +# sage: s +# z^2 - z^3 +# """ + +# def __init__(self, laurent_polynomial, is_sparse, constant=None, degree=None): +# """ +# Initialize ``self``. + +# TESTS:: + +# sage: R. = LaurentPolynomialRing(QQ) +# sage: from sage.data_structures.coefficient_stream import CoefficientStream_eventually_geometric +# sage: X = CoefficientStream_eventually_geometric(z^2 + z^-1, False) +# sage: [X[i] for i in range(-1,8)] +# [1, 0, 0, 1, 0, 0, 0, 0, 0] +# sage: X = CoefficientStream_eventually_geometric(z^2 + z^-1, True, 5) +# sage: [X[i] for i in range(-1,8)] +# [1, 0, 0, 1, 5, 5, 5, 5, 5] +# sage: X = CoefficientStream_eventually_geometric(z^2 + z^-1, False, 5, 4) +# sage: [X[i] for i in range(-1,8)] +# [1, 0, 0, 1, 0, 5, 5, 5, 5] +# """ +# if constant is None: +# constant = ZZ.zero() +# if degree is None: +# if not laurent_polynomial: +# raise ValueError("you must specify the degree for the polynomial 0") +# degree = laurent_polynomial.degree() + 1 +# else: +# # Consistency check +# assert not laurent_polynomial or laurent_polynomial.degree() < degree + +# self._constant = constant +# self._degree = degree +# self._laurent_polynomial = laurent_polynomial +# if not laurent_polynomial: +# valuation = degree +# else: +# valuation = laurent_polynomial.valuation() +# super().__init__(is_sparse, valuation) + +# def __getitem__(self, n): +# """ +# Return the coefficient of the term with exponent ``n`` of the series. + +# INPUT: + +# - ``n`` -- integer; the degree for which the coefficient is required + +# EXAMPLES:: + +# sage: R. = LaurentPolynomialRing(QQ) +# sage: from sage.data_structures.coefficient_stream import CoefficientStream_eventually_geometric +# sage: f = CoefficientStream_eventually_geometric(z^2 + z^-1, False, 3, 10) +# sage: f[2] +# 1 +# sage: f[8] +# 0 +# sage: f[15] +# 3 +# """ +# if n >= self._degree: +# return self._constant +# return self._laurent_polynomial[n] + +# def valuation(self): +# """ +# Return the valuation of the series. + +# EXAMPLES:: + +# sage: L. = LazyLaurentSeriesRing(QQ) +# sage: f = 1 + z + z^2 + z^3 +# sage: f.valuation() +# 0 +# """ +# return self._approximate_valuation + +# def __hash__(self): +# """ +# Return the hash of ``self``. + +# EXAMPLES:: + +# sage: L. = LazyLaurentSeriesRing(QQ) +# sage: f = 1 + z + z^2 + z^3 +# sage: {f: 1} +# {1 + z + z^2 + z^3: 1} +# """ +# return hash((self._laurent_polynomial, self._degree, self._constant)) + +# def __eq__(self, other): +# """ +# Test the equality between ``self`` and ``other``. + +# INPUT: + +# - ``other`` -- a stream for a series which is known to be eventually geometric + +# EXAMPLES:: + +# sage: L. = LazyLaurentSeriesRing(QQ) +# sage: f = 1 + z + z^2 + z^3 +# sage: m = 1 + z + z^2 + z^3 +# sage: f == m +# True +# """ +# return (isinstance(other, type(self)) +# and self._degree == other._degree +# and self._laurent_polynomial == other._laurent_polynomial +# and self._constant == other._constant) + + class CoefficientStream_coefficient_function(CoefficientStream_inexact): r""" Class that returns the elements in the coefficient stream. @@ -467,7 +610,7 @@ class CoefficientStream_coefficient_function(CoefficientStream_inexact): coefficients of the series - ``ring`` -- the base ring of the series - ``is_sparse`` -- boolean; specifies whether the series is sparse - - ``approximate_valuation`` -- the approximate valuation of the series + - ``approximate_valuation`` -- integer; a lower bound for the valuation of the series EXAMPLES:: @@ -535,7 +678,7 @@ class CoefficientStream_uninitialized(CoefficientStream_inexact): INPUT: - ``is_sparse`` -- boolean; which specifies whether the series is sparse - - ``approximate_valuation`` -- the approximate valuation of the series + - ``approximate_valuation`` -- integer; a lower bound for the valuation of the series EXAMPLES:: @@ -607,9 +750,9 @@ class CoefficientStream_unary(CoefficientStream_inexact): EXAMPLES:: - sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_inv, CoefficientStream_scalar) + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_cauchy_inverse, CoefficientStream_scalar) sage: f = CoefficientStream_coefficient_function(lambda n: 2*n, ZZ, False, 1) - sage: g = CoefficientStream_inv(f) + sage: g = CoefficientStream_cauchy_inverse(f) sage: [g[i] for i in range(10)] [-1, 1/2, 0, 0, 0, 0, 0, 0, 0, 0] sage: g = CoefficientStream_scalar(f, 2) @@ -696,8 +839,8 @@ def __init__(self, left, right, *args, **kwargs): TESTS:: - sage: from sage.data_structures.coefficient_stream import CoefficientStream_mul - sage: (CoefficientStream_mul.__base__).__base__ + sage: from sage.data_structures.coefficient_stream import CoefficientStream_cauchy_product + sage: (CoefficientStream_cauchy_product.__base__).__base__ """ self._left = left @@ -727,13 +870,13 @@ def __eq__(self, other): TESTS:: - sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_mul) + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_cauchy_product) sage: f = CoefficientStream_coefficient_function(lambda n: 2*n, ZZ, False, 1) sage: g = CoefficientStream_coefficient_function(lambda n: n, ZZ, False, 1) sage: h = CoefficientStream_coefficient_function(lambda n: 1, ZZ, False, 1) - sage: t = CoefficientStream_mul(f, g) - sage: u = CoefficientStream_mul(g, h) - sage: v = CoefficientStream_mul(h, f) + sage: t = CoefficientStream_cauchy_product(f, g) + sage: u = CoefficientStream_cauchy_product(g, h) + sage: v = CoefficientStream_cauchy_product(h, f) sage: t == u False sage: t == t @@ -791,13 +934,13 @@ def __eq__(self, other): - ``other`` -- a stream of coefficients TESTS:: - sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_mul) + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_cauchy_product) sage: f = CoefficientStream_coefficient_function(lambda n: 2*n, ZZ, True, 0) sage: g = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) - sage: h = CoefficientStream_mul(f, g) + sage: h = CoefficientStream_cauchy_product(f, g) sage: [h[i] for i in range(10)] [0, 0, 2, 8, 20, 40, 70, 112, 168, 240] - sage: u = CoefficientStream_mul(g, f) + sage: u = CoefficientStream_cauchy_product(g, f) sage: [u[i] for i in range(10)] [0, 0, 2, 8, 20, 40, 70, 112, 168, 240] sage: h == u @@ -1054,7 +1197,7 @@ def iterate_coefficients(self): n += 1 -class CoefficientStream_mul(CoefficientStream_binary_commutative): +class CoefficientStream_cauchy_product(CoefficientStream_binary_commutative): """ Operator for multiplication of two coefficient streams. @@ -1067,13 +1210,13 @@ class CoefficientStream_mul(CoefficientStream_binary_commutative): EXAMPLES:: - sage: from sage.data_structures.coefficient_stream import (CoefficientStream_mul, CoefficientStream_coefficient_function) + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_cauchy_product, CoefficientStream_coefficient_function) sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 0) sage: g = CoefficientStream_coefficient_function(lambda n: 1, ZZ, True, 0) - sage: h = CoefficientStream_mul(f, g) + sage: h = CoefficientStream_cauchy_product(f, g) sage: [h[i] for i in range(10)] [0, 1, 3, 6, 10, 15, 21, 28, 36, 45] - sage: u = CoefficientStream_mul(g, f) + sage: u = CoefficientStream_cauchy_product(g, f) sage: [u[i] for i in range(10)] [0, 1, 3, 6, 10, 15, 21, 28, 36, 45] """ @@ -1083,10 +1226,10 @@ def __init__(self, left, right): TESTS:: - sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_mul) + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_cauchy_product) sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, True, 0) sage: g = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, True, 0) - sage: h = CoefficientStream_mul(f, g) + sage: h = CoefficientStream_cauchy_product(f, g) """ if left._is_sparse != right._is_sparse: raise NotImplementedError @@ -1104,10 +1247,10 @@ def get_coefficient(self, n): EXAMPLES:: - sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_mul) + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_cauchy_product) sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 0) sage: g = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, True, 0) - sage: h = CoefficientStream_mul(f, g) + sage: h = CoefficientStream_cauchy_product(f, g) sage: h.get_coefficient(5) 50 sage: [h.get_coefficient(i) for i in range(10)] @@ -1127,10 +1270,10 @@ def iterate_coefficients(self): EXAMPLES:: - sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_mul) + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_cauchy_product) sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, False, 0) sage: g = CoefficientStream_coefficient_function(lambda n: n^3, ZZ, False, 0) - sage: h = CoefficientStream_mul(f, g) + sage: h = CoefficientStream_cauchy_product(f, g) sage: n = h.iterate_coefficients() sage: [next(n) for i in range(10)] [0, 1, 9, 36, 100, 225, 441, 784, 1296, 2025] @@ -1282,12 +1425,12 @@ def __init__(self, f, g): self._fv = f._approximate_valuation self._gv = g._approximate_valuation if self._fv < 0: - ginv = CoefficientStream_inv(g) + ginv = CoefficientStream_cauchy_inverse(g) # the constant part makes no contribution to the negative # we need this for the case so self._neg_powers[0][n] => 0 self._neg_powers = [CoefficientStream_zero(f._is_sparse), ginv] for i in range(1, -self._fv): - self._neg_powers.append(CoefficientStream_mul(self._neg_powers[-1], ginv)) + self._neg_powers.append(CoefficientStream_cauchy_product(self._neg_powers[-1], ginv)) # Placeholder None to make this 1-based self._pos_powers = [None, g] val = self._fv * self._gv @@ -1316,7 +1459,7 @@ def get_coefficient(self, n): return sum(self._left[i] * self._neg_powers[-i][n] for i in range(self._fv, n // self._gv + 1)) # n > 0 while len(self._pos_powers) <= n // self._gv: - self._pos_powers.append(CoefficientStream_mul(self._pos_powers[-1], self._right)) + self._pos_powers.append(CoefficientStream_cauchy_product(self._pos_powers[-1], self._right)) ret = sum(self._left[i] * self._neg_powers[-i][n] for i in range(self._fv, 0)) if n == 0: ret += self._left[0] @@ -1485,7 +1628,7 @@ def iterate_coefficients(self): n += 1 -class CoefficientStream_inv(CoefficientStream_unary): +class CoefficientStream_cauchy_inverse(CoefficientStream_unary): """ Operator for multiplicative inverse of the stream. @@ -1495,9 +1638,9 @@ class CoefficientStream_inv(CoefficientStream_unary): EXAMPLES:: - sage: from sage.data_structures.coefficient_stream import (CoefficientStream_inv, CoefficientStream_coefficient_function) + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_cauchy_inverse, CoefficientStream_coefficient_function) sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, True, 1) - sage: g = CoefficientStream_inv(f) + sage: g = CoefficientStream_cauchy_inverse(f) sage: [g[i] for i in range(10)] [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0] """ @@ -1507,9 +1650,9 @@ def __init__(self, series): TESTS:: - sage: from sage.data_structures.coefficient_stream import (CoefficientStream_inv, CoefficientStream_coefficient_function) + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_cauchy_inverse, CoefficientStream_coefficient_function) sage: f = CoefficientStream_coefficient_function(lambda n: -1, ZZ, True, 0) - sage: g = CoefficientStream_inv(f) + sage: g = CoefficientStream_cauchy_inverse(f) sage: [g[i] for i in range(10)] [-1, 1, 0, 0, 0, 0, 0, 0, 0, 0] """ @@ -1529,9 +1672,9 @@ def get_coefficient(self, n): EXAMPLES:: - sage: from sage.data_structures.coefficient_stream import (CoefficientStream_inv, CoefficientStream_coefficient_function) + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_cauchy_inverse, CoefficientStream_coefficient_function) sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) - sage: g = CoefficientStream_inv(f) + sage: g = CoefficientStream_cauchy_inverse(f) sage: g.get_coefficient(5) 0 sage: [g.get_coefficient(i) for i in range(10)] @@ -1551,9 +1694,9 @@ def iterate_coefficients(self): EXAMPLES:: - sage: from sage.data_structures.coefficient_stream import (CoefficientStream_inv, CoefficientStream_coefficient_function) + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_cauchy_inverse, CoefficientStream_coefficient_function) sage: f = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, False, 1) - sage: g = CoefficientStream_inv(f) + sage: g = CoefficientStream_cauchy_inverse(f) sage: n = g.iterate_coefficients() sage: [next(n) for i in range(10)] [1, -4, 7, -8, 8, -8, 8, -8, 8, -8] diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 8e0c2801b22..97e61234dcc 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -81,18 +81,18 @@ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing from sage.data_structures.coefficient_stream import ( CoefficientStream_add, - CoefficientStream_mul, + CoefficientStream_cauchy_product, CoefficientStream_sub, CoefficientStream_div, CoefficientStream_composition, CoefficientStream_scalar, CoefficientStream_neg, - CoefficientStream_inv, + CoefficientStream_cauchy_inverse, CoefficientStream_apply_coeff, CoefficientStream, CoefficientStream_inexact, CoefficientStream_zero, - CoefficientStream_eventually_geometric, + CoefficientStream_exact, CoefficientStream_coefficient_function, CoefficientStream_uninitialized ) @@ -364,19 +364,19 @@ def __call__(self, g): # f has finite length if isinstance(self._coeff_stream, CoefficientStream_zero): # constant 0 return self - if isinstance(self._coeff_stream, CoefficientStream_eventually_geometric) and not self._coeff_stream._constant: + if isinstance(self._coeff_stream, CoefficientStream_exact) and not self._coeff_stream._constant: # constant polynomial if self._coeff_stream._laurent_polynomial.is_constant(): return self if not isinstance(g, LazyLaurentSeries): return self._coeff_stream._laurent_polynomial(g) # g also has finite length, compose the polynomials - if isinstance(g._coeff_stream, CoefficientStream_eventually_geometric) and not g._coeff_stream._constant: + if isinstance(g._coeff_stream, CoefficientStream_exact) and not g._coeff_stream._constant: R = P._laurent_poly_ring try: ret = self._coeff_stream._laurent_polynomial(g._coeff_stream._laurent_polynomial) if ret.parent() is R: - return P.element_class(P, CoefficientStream_eventually_geometric(ret, self._coeff_stream._is_sparse, 0)) + return P.element_class(P, CoefficientStream_exact([ret], self._coeff_stream._is_sparse, 0)) except TypeError: # the result is not a Laurent polynomial pass @@ -414,7 +414,7 @@ def __call__(self, g): return P.element_class(P, CoefficientStream_composition(self._coeff_stream, g._coeff_stream)) - def _mul_(self, other): + def _cauchy_product_(self, other): """ Return the product of this series with ``other``. @@ -455,18 +455,18 @@ def _mul_(self, other): return P.zero() R = P._laurent_poly_ring - if isinstance(left, CoefficientStream_eventually_geometric): + if isinstance(left, CoefficientStream_exact): if not left._constant: if left._laurent_polynomial == R.one(): # self == 1 return other - if isinstance(right, CoefficientStream_eventually_geometric): + if isinstance(right, CoefficientStream_exact): if not right._constant: p = left._laurent_polynomial * right._laurent_polynomial c = left._constant - return P.element_class(P, CoefficientStream_eventually_geometric(p, P._sparse, c)) - elif isinstance(right, CoefficientStream_eventually_geometric) and not right._constant and right._laurent_polynomial == R.one(): # other == 1 + return P.element_class(P, CoefficientStream_exact([p], P._sparse, c)) + elif isinstance(right, CoefficientStream_exact) and not right._constant and right._laurent_polynomial == R.one(): # other == 1 return self - return P.element_class(P, CoefficientStream_mul(self._coeff_stream, other._coeff_stream)) + return P.element_class(P, CoefficientStream_cauchy_product(self._coeff_stream, other._coeff_stream)) def _add_(self, other): """ @@ -510,8 +510,8 @@ def _add_(self, other): P = self.parent() left = self._coeff_stream right = other._coeff_stream - if (isinstance(left, CoefficientStream_eventually_geometric) - and isinstance(right, CoefficientStream_eventually_geometric)): + if (isinstance(left, CoefficientStream_exact) + and isinstance(right, CoefficientStream_exact)): R = P._laurent_poly_ring c = left._constant + right._constant pl = left._laurent_polynomial @@ -522,7 +522,7 @@ def _add_(self, other): p = pl + pr if not p and not c: return P.zero() - return P.element_class(P, CoefficientStream_eventually_geometric(p, P._sparse, c, d)) + return P.element_class(P, CoefficientStream_exact([p], P._sparse, c, d)) return P.element_class(P, CoefficientStream_add(self._coeff_stream, other._coeff_stream)) def _sub_(self, other): @@ -570,18 +570,18 @@ def _sub_(self, other): P = self.parent() left = self._coeff_stream right = other._coeff_stream - if (isinstance(left, CoefficientStream_eventually_geometric) and isinstance(right, CoefficientStream_eventually_geometric)): + if (isinstance(left, CoefficientStream_exact) and isinstance(right, CoefficientStream_exact)): R = P._laurent_poly_ring c = left._constant - right._constant - pl = left._laurent_polynomial - pr = right._laurent_polynomial + pl = R(left._initial_coefficients) + pr = R(right._initial_coefficients) d = max(left._degree, right._degree) pl += R([left._constant]*(d-left._degree)).shift(left._degree) pr += R([right._constant]*(d-right._degree)).shift(right._degree) p = pl - pr if not p and not c: return P.zero() - return P.element_class(P, CoefficientStream_eventually_geometric(p, P._sparse, c, d)) + return P.element_class(P, CoefficientStream_exact(p, P._sparse, c, d)) if left == right: return P.zero() return P.element_class(P, CoefficientStream_sub(self._coeff_stream, other._coeff_stream)) @@ -622,18 +622,18 @@ def _div_(self, other): if isinstance(left, CoefficientStream_zero): return P.zero() right = other._coeff_stream - if (isinstance(left, CoefficientStream_eventually_geometric) - and isinstance(right, CoefficientStream_eventually_geometric)): + if (isinstance(left, CoefficientStream_exact) + and isinstance(right, CoefficientStream_exact)): if not left._constant and not right._constant: ret = left._laurent_polynomial / right._laurent_polynomial try: ret = P._laurent_poly_ring(ret) - return P.element_class(P, CoefficientStream_eventually_geometric(ret, P._sparse, left._constant)) + return P.element_class(P, CoefficientStream_exact([ret], P._sparse, left._constant)) except (TypeError, ValueError): # We cannot divide the polynomials, so the result must be a series pass - return P.element_class(P, CoefficientStream_mul(left, CoefficientStream_inv(right))) + return P.element_class(P, CoefficientStream_cauchy_product(left, CoefficientStream_cauchy_inverse(right))) def _rmul_(self, scalar): """ @@ -678,10 +678,10 @@ def _rmul_(self, scalar): if scalar == 1: return self - if isinstance(self._coeff_stream, CoefficientStream_eventually_geometric): + if isinstance(self._coeff_stream, CoefficientStream_exact): c = scalar * self._coeff_stream._constant p = scalar * self._coeff_stream._laurent_polynomial - return P.element_class(P, CoefficientStream_eventually_geometric(p, P._sparse, c, self._coeff_stream._degree)) + return P.element_class(P, CoefficientStream_exact([p], P._sparse, c, self._coeff_stream._degree)) return P.element_class(P, CoefficientStream_scalar(self._coeff_stream, scalar)) @@ -709,11 +709,11 @@ def _neg_(self): -3*z - z^2 + 4*z^3 """ P = self.parent() - if isinstance(self._coeff_stream, CoefficientStream_eventually_geometric): + if isinstance(self._coeff_stream, CoefficientStream_exact): p = -self._coeff_stream._laurent_polynomial c = -self._coeff_stream._constant d = self._coeff_stream._degree - return P.element_class(P, CoefficientStream_eventually_geometric(p, P._sparse, c, d)) + return P.element_class(P, CoefficientStream_exact([p], P._sparse, c, d)) # -(-f) = f if isinstance(self._coeff_stream, CoefficientStream_neg): return P.element_class(P, self._coeff_stream._series) @@ -742,13 +742,13 @@ def __invert__(self): 1 - z """ P = self.parent() - if isinstance(self._coeff_stream, CoefficientStream_eventually_geometric) and self._coeff_stream._laurent_polynomial == P.gen(): + if isinstance(self._coeff_stream, CoefficientStream_exact) and self._coeff_stream._laurent_polynomial == P.gen(): ret = 1 / self._coeff_stream._laurent_polynomial - return P.element_class(P, CoefficientStream_eventually_geometric(ret, P._sparse, self._coeff_stream._constant)) + return P.element_class(P, CoefficientStream_exact([ret], P._sparse, self._coeff_stream._constant)) # (f^-1)^-1 = f - if isinstance(self._coeff_stream, CoefficientStream_inv): + if isinstance(self._coeff_stream, CoefficientStream_cauchy_inverse): return P.element_class(P, self._coeff_stream._series) - return P.element_class(P, CoefficientStream_inv(self._coeff_stream)) + return P.element_class(P, CoefficientStream_cauchy_inverse(self._coeff_stream)) def coefficient(self, n): """ @@ -833,12 +833,12 @@ def map_coefficients(self, func, ring=None): """ P = self.parent() R = P.base_ring() - if isinstance(self._coeff_stream, CoefficientStream_eventually_geometric): + if isinstance(self._coeff_stream, CoefficientStream_exact): p = p.map_coefficients(func) c = func(c) if not p and not c: return P.zero() - return P.element_class(P, CoefficientStream_eventually_geometric(p, self._coeff_stream._is_sparse, c, d)) + return P.element_class(P, CoefficientStream_exact([p], self._coeff_stream._is_sparse, c, d)) return P.element_class(P, CoefficientStream_apply_coeff(self._coeff_stream, func, R)) def change_ring(self, ring): @@ -913,7 +913,7 @@ def truncate(self, d): R = P._laurent_poly_ring z = R.gen() p = R.sum(self[i] * z**i for i in range(self._coeff_stream._approximate_valuation, d)) - return P.element_class(P, CoefficientStream_eventually_geometric(p, P._sparse, ZZ.zero(), d)) + return P.element_class(P, CoefficientStream_exact([p], P._sparse, ZZ.zero(), d)) def __pow__(self, n): """ @@ -1072,7 +1072,7 @@ def polynomial(self, degree=None, name=None): if isinstance(self._coeff_stream, CoefficientStream_zero): from sage.rings.all import PolynomialRing return PolynomialRing(S.base_ring(), name=name).zero() - elif isinstance(self._coeff_stream, CoefficientStream_eventually_geometric) and not self._coeff_stream._constant: + elif isinstance(self._coeff_stream, CoefficientStream_exact) and not self._coeff_stream._constant: m = self._coeff_stream._degree else: raise ValueError("not a polynomial") @@ -1139,11 +1139,12 @@ def _repr_(self): X = self.parent().variable_name() v = self._coeff_stream._approximate_valuation - if not isinstance(self._coeff_stream, CoefficientStream_eventually_geometric): + if not isinstance(self._coeff_stream, CoefficientStream_exact): m = v + 7 # long enough elif not self._coeff_stream._constant: # Just a polynonial, so let that print itself - return repr(self._coeff_stream._laurent_polynomial) + R = self.parent()._laurent_poly_ring + return repr(R([self._coeff_stream[i] for i in range(self._coeff_stream._constant, self._coeff_stream._degree)])) else: m = self._coeff_stream._degree + 3 @@ -1189,8 +1190,8 @@ def _richcmp_(self, other, op): if isinstance(other._coeff_stream, CoefficientStream_zero): # self != 0 but other == 0 return False - if (not isinstance(self._coeff_stream, CoefficientStream_eventually_geometric) - or not isinstance(other._coeff_stream, CoefficientStream_eventually_geometric)): + if (not isinstance(self._coeff_stream, CoefficientStream_exact) + or not isinstance(other._coeff_stream, CoefficientStream_exact)): # One of the lazy laurent series is not known to eventually be constant # Implement the checking of the caches here. n = min(self._coeff_stream._approximate_valuation, other._coeff_stream._approximate_valuation) @@ -1202,7 +1203,7 @@ def _richcmp_(self, other, op): return True raise ValueError("undecidable as lazy Laurent series") - # Both are CoefficientStream_eventually_geometric, which implements a full check + # Both are CoefficientStream_exact, which implements a full check return self._coeff_stream == other._coeff_stream if op is op_NE: @@ -1243,7 +1244,7 @@ def __bool__(self): """ if isinstance(self._coeff_stream, CoefficientStream_zero): return False - if isinstance(self._coeff_stream, CoefficientStream_eventually_geometric): + if isinstance(self._coeff_stream, CoefficientStream_exact): # This should always end up being True, but let's be careful about it for now... return self._coeff_stream._laurent_polynomial or self._coeff_stream._constant diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index 54a381026c7..65856831d8a 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -85,7 +85,7 @@ from sage.data_structures.coefficient_stream import ( CoefficientStream_zero, CoefficientStream_coefficient_function, - CoefficientStream_eventually_geometric, + CoefficientStream_exact, CoefficientStream_uninitialized ) @@ -163,7 +163,7 @@ def gen(self, n=0): if n != 0: raise IndexError("there is only one generator") R = self._laurent_poly_ring - coeff_stream = CoefficientStream_eventually_geometric(R.gen(n), self._sparse, ZZ.zero(), 2) + coeff_stream = CoefficientStream_exact([R.gen(n)[1]], self._sparse, constant=ZZ.zero(), degree=2) return self.element_class(self, coeff_stream) def ngens(self): @@ -213,7 +213,7 @@ def _coerce_map_from_(self, S): R = self._laurent_poly_ring if R.has_coerce_map_from(S): def make_series_from(poly): - return self.element_class(self, CoefficientStream_eventually_geometric(R(poly), self._sparse)) + return self.element_class(self, CoefficientStream_exact([R(poly)], self._sparse)) return SetMorphism(Hom(S, self, Sets()), make_series_from) return False @@ -225,7 +225,7 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No INPUT: - ``x`` -- data used to the define a Laurent series - - ``valuation`` -- integer (optional); the valuation of the series + - ``valuation`` -- integer (optional); integer; a lower bound for the valuation of the series - ``constant`` -- (optional) the eventual constant of the series - ``degree`` -- (optional) the degree when the series is ``constant`` @@ -313,7 +313,7 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No x = x.shift(valuation - x.valuation()) if degree is None and not x: degree = valuation - coeff_stream = CoefficientStream_eventually_geometric(R(x), self._sparse, constant, degree) + coeff_stream = CoefficientStream_exact([x], self._sparse, constant=constant, degree=degree) return self.element_class(self, coeff_stream) if isinstance(x, LazyLaurentSeries): if x._coeff_stream._is_sparse is self._sparse: @@ -327,8 +327,9 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No if constant is None: constant = ZZ.zero() z = R.gen() - p = R.sum(x(i) * z**i for i in range(valuation, degree)) - return self.element_class(self, CoefficientStream_eventually_geometric(p, self._sparse, constant, degree)) + # p = R.sum(x(i) * z**i for i in range(valuation, degree)) + p = [x(i) for i in range(valuation, degree)] + return self.element_class(self, CoefficientStream_exact(p, self._sparse, constant=constant, degree=degree)) return self.element_class(self, CoefficientStream_coefficient_function(x, self.base_ring(), self._sparse, valuation)) raise ValueError(f"unable to convert {x} into a lazy Laurent series") @@ -344,7 +345,7 @@ def _an_element_(self): """ c = self.base_ring().an_element() R = self._laurent_poly_ring - return self.element_class(self, CoefficientStream_eventually_geometric(R.zero(), self._sparse, c, -10)) + return self.element_class(self, CoefficientStream_exact([R.zero()], self._sparse, c, -10)) @cached_method def one(self): @@ -358,7 +359,7 @@ def one(self): 1 """ R = self._laurent_poly_ring - return self.element_class(self, CoefficientStream_eventually_geometric(R.one(), self._sparse, ZZ.zero(), 1)) + return self.element_class(self, CoefficientStream_exact([R.one()], self._sparse, constant=ZZ.zero(), degree=1)) @cached_method def zero(self):