From e5c89c9675bfed04fa99384aa8e7cc07e1702cd8 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Fri, 30 Jul 2021 17:25:26 +0530 Subject: [PATCH 1/2] 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): From 799e22cfe7a4ef35b58f58fdeab13858de04ded2 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Sat, 31 Jul 2021 19:23:12 +0530 Subject: [PATCH 2/2] CS_eventually_geometric is now CS_exact --- .../data_structures/coefficient_stream.py | 155 +----------------- src/sage/rings/lazy_laurent_series.py | 110 +++++++++---- src/sage/rings/lazy_laurent_series_ring.py | 30 +++- 3 files changed, 102 insertions(+), 193 deletions(-) diff --git a/src/sage/data_structures/coefficient_stream.py b/src/sage/data_structures/coefficient_stream.py index 4ec569317de..994c64d2a20 100644 --- a/src/sage/data_structures/coefficient_stream.py +++ b/src/sage/data_structures/coefficient_stream.py @@ -174,10 +174,8 @@ def __getstate__(self): TESTS:: 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_exact(Y(1), True) - sage: g = CoefficientStream_exact(Y([1, -1, -1]), True) + sage: h = CoefficientStream_exact([1], True) + sage: g = CoefficientStream_exact([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)] @@ -205,10 +203,8 @@ def __setstate__(self, d): TESTS:: 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_exact(Y(-1), True) - sage: g = CoefficientStream_exact(Y([1, -1]), True) + sage: h = CoefficientStream_exact([-1], True) + sage: g = CoefficientStream_exact([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)] @@ -457,149 +453,6 @@ def __eq__(self, other): 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. diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 97e61234dcc..95ab0068eac 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -366,23 +366,27 @@ def __call__(self, g): return self if isinstance(self._coeff_stream, CoefficientStream_exact) and not self._coeff_stream._constant: # constant polynomial - if self._coeff_stream._laurent_polynomial.is_constant(): + R = self.parent()._laurent_poly_ring + z = R.gen() + poly = R(sum([self._coeff_stream[i] * z**i for i in range(self._coeff_stream._approximate_valuation, self._coeff_stream._degree)])) + if poly.is_constant(): return self if not isinstance(g, LazyLaurentSeries): - return self._coeff_stream._laurent_polynomial(g) + return poly(g) # g also has finite length, compose the polynomials 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) + R = P._laurent_poly_ring + g_poly = R(sum([g._coeff_stream[i] * z**i for i in range(g._coeff_stream._approximate_valuation, g._coeff_stream._degree)])) + ret = poly(g_poly) if ret.parent() is R: - return P.element_class(P, CoefficientStream_exact([ret], self._coeff_stream._is_sparse, 0)) + p_list = [ret[i] for i in range(ret.valuation(), ret.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(p_list, self._coeff_stream._is_sparse, valuation=ret.valuation())) except TypeError: # the result is not a Laurent polynomial pass # Return the sum since g is not known to be finite or we do not get a Laurent polynomial # TODO: Optimize when f has positive valuation - poly = self._coeff_stream._laurent_polynomial ret = P.zero() gp = P.one() # We build this iteratively so each power can benefit from the caching @@ -414,7 +418,7 @@ def __call__(self, g): return P.element_class(P, CoefficientStream_composition(self._coeff_stream, g._coeff_stream)) - def _cauchy_product_(self, other): + def _mul_(self, other): """ Return the product of this series with ``other``. @@ -455,17 +459,24 @@ def _cauchy_product_(self, other): return P.zero() R = P._laurent_poly_ring + z = R.gen() if isinstance(left, CoefficientStream_exact): if not left._constant: - if left._laurent_polynomial == R.one(): # self == 1 + pl = R(sum([left[i] * z**i for i in range(left._approximate_valuation, left._degree)])) + if pl == R.one(): # self == 1 return other if isinstance(right, CoefficientStream_exact): if not right._constant: - p = left._laurent_polynomial * right._laurent_polynomial + pr = R(sum([right[i] * z**i for i in range(right._approximate_valuation, right._degree)])) + p = pl * pr c = left._constant - 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 + p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=p.valuation(), constant=c)) + elif isinstance(right, CoefficientStream_exact): + if not right._constant: + pr = R(sum([right[i] * z**i for i in range(right._approximate_valuation, right._degree)])) + if pr == R.one(): # other == 1 + return self return P.element_class(P, CoefficientStream_cauchy_product(self._coeff_stream, other._coeff_stream)) def _add_(self, other): @@ -513,16 +524,18 @@ def _add_(self, other): if (isinstance(left, CoefficientStream_exact) and isinstance(right, CoefficientStream_exact)): R = P._laurent_poly_ring + z = R.gen() + pl = R(sum([left[i] * z**i for i in range(left._approximate_valuation, left._degree)])) + pr = R(sum([right[i] * z**i for i in range(right._approximate_valuation, right._degree)])) c = left._constant + right._constant - pl = left._laurent_polynomial - pr = right._laurent_polynomial 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_exact([p], P._sparse, c, d)) + p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=p.valuation(), constant=c, degree=d)) return P.element_class(P, CoefficientStream_add(self._coeff_stream, other._coeff_stream)) def _sub_(self, other): @@ -572,16 +585,18 @@ def _sub_(self, other): right = other._coeff_stream if (isinstance(left, CoefficientStream_exact) and isinstance(right, CoefficientStream_exact)): R = P._laurent_poly_ring + z = R.gen() c = left._constant - right._constant - pl = R(left._initial_coefficients) - pr = R(right._initial_coefficients) + pl = R(sum([left[i] * z**i for i in range(left._approximate_valuation, left._degree)])) + pr = R(sum([right[i] * z**i for i in range(right._approximate_valuation, right._degree)])) 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_exact(p, P._sparse, c, d)) + p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=p.valuation(), constant=c, degree=d)) if left == right: return P.zero() return P.element_class(P, CoefficientStream_sub(self._coeff_stream, other._coeff_stream)) @@ -625,10 +640,15 @@ def _div_(self, other): 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 + R = P._laurent_poly_ring + z = R.gen() + pl = R(sum([left[i] * z**i for i in range(left._approximate_valuation, left._degree)])) + pr = R(sum([right[i] * z**i for i in range(right._approximate_valuation, right._degree)])) + ret = pl / pr try: ret = P._laurent_poly_ring(ret) - return P.element_class(P, CoefficientStream_exact([ret], P._sparse, left._constant)) + p_list = [ret[i] for i in range(ret.valuation(), ret.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=ret.valuation(), constant=left._constant)) except (TypeError, ValueError): # We cannot divide the polynomials, so the result must be a series pass @@ -679,9 +699,13 @@ def _rmul_(self, scalar): return self if isinstance(self._coeff_stream, CoefficientStream_exact): + R = P._laurent_poly_ring + z = R.gen() c = scalar * self._coeff_stream._constant - p = scalar * self._coeff_stream._laurent_polynomial - return P.element_class(P, CoefficientStream_exact([p], P._sparse, c, self._coeff_stream._degree)) + pl = R(sum([self._coeff_stream[i] * z**i for i in range(self._coeff_stream._approximate_valuation, self._coeff_stream._degree)])) + p = scalar * pl + p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=p.valuation(), constant=c, degree=self._coeff_stream._degree)) return P.element_class(P, CoefficientStream_scalar(self._coeff_stream, scalar)) @@ -710,10 +734,14 @@ def _neg_(self): """ P = self.parent() if isinstance(self._coeff_stream, CoefficientStream_exact): - p = -self._coeff_stream._laurent_polynomial + R = P._laurent_poly_ring + z = R.gen() + poly = R(sum([self._coeff_stream[i] * z**i for i in range(self._coeff_stream._approximate_valuation, self._coeff_stream._degree)])) + p = -poly c = -self._coeff_stream._constant d = self._coeff_stream._degree - return P.element_class(P, CoefficientStream_exact([p], P._sparse, c, d)) + p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=p.valuation(), constant=c, degree=d)) # -(-f) = f if isinstance(self._coeff_stream, CoefficientStream_neg): return P.element_class(P, self._coeff_stream._series) @@ -742,9 +770,14 @@ def __invert__(self): 1 - z """ P = self.parent() - 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_exact([ret], P._sparse, self._coeff_stream._constant)) + R = P._laurent_poly_ring + z = R.gen() + if isinstance(self._coeff_stream, CoefficientStream_exact): + poly = R(sum([self._coeff_stream[i] * z**i for i in range(self._coeff_stream._approximate_valuation, self._coeff_stream._degree)])) + if poly == R.gen(): + ret = 1 / poly + p_list = [ret[i] for i in range(ret.valuation(), ret.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=ret.valuation(), constant=self._coeff_stream._constant)) # (f^-1)^-1 = f if isinstance(self._coeff_stream, CoefficientStream_cauchy_inverse): return P.element_class(P, self._coeff_stream._series) @@ -832,14 +865,17 @@ def map_coefficients(self, func, ring=None): 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... """ P = self.parent() - R = P.base_ring() if isinstance(self._coeff_stream, CoefficientStream_exact): + R = P._laurent_poly_ring + z = R.gen() + p = R(sum([self._coeff_stream[i] * z**i for i in range(self._coeff_stream._approximate_valuation, self._coeff_stream._degree)])) p = p.map_coefficients(func) - c = func(c) + c = func(self._coeff_stream._constant) if not p and not c: return P.zero() - 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)) + p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(p_list, self._coeff_stream._is_sparse, valuation=p.valuation(), constant=c)) + return P.element_class(P, CoefficientStream_apply_coeff(self._coeff_stream, func, P.base_ring())) def change_ring(self, ring): """ @@ -913,7 +949,8 @@ 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_exact([p], P._sparse, ZZ.zero(), d)) + p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=p.valuation(), constant=ZZ.zero(), degree=d)) def __pow__(self, n): """ @@ -1144,7 +1181,8 @@ def _repr_(self): elif not self._coeff_stream._constant: # Just a polynonial, so let that print itself 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)])) + z = R.gen() + return repr(R.sum(self._coeff_stream[i] * z**i for i in range(v, self._coeff_stream._degree))) else: m = self._coeff_stream._degree + 3 @@ -1246,7 +1284,11 @@ def __bool__(self): return False 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 + P = self.paren() + R = P._laurent_poly_ring + z = R.gen() + poly = R(sum([self._coeff_stream[i] * z**i for i in range(self._coeff_stream._approximate_valuation, self._coeff_stream._degree)])) + return poly or self._coeff_stream._constant for a in self._coeff_stream._cache: if a: diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index 65856831d8a..49c5a266e96 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -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_exact([R.gen(n)[1]], self._sparse, constant=ZZ.zero(), degree=2) + coeff_stream = CoefficientStream_exact([R.gen(n)[1]], self._sparse, constant=ZZ.zero(), valuation=1, degree=2) return self.element_class(self, coeff_stream) def ngens(self): @@ -213,7 +213,8 @@ 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_exact([R(poly)], self._sparse)) + p_list = [poly[i] for i in range(poly.valuation(), poly.degree() + 1)] + return self.element_class(self, CoefficientStream_exact(p_list, self._sparse, valuation=poly.valuation())) return SetMorphism(Hom(S, self, Sets()), make_series_from) return False @@ -293,7 +294,7 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No if valuation is None: valuation = 0 return self.element_class(self, CoefficientStream_uninitialized(self._sparse, valuation)) - + R = self._laurent_poly_ring BR = self.base_ring() try: @@ -312,8 +313,22 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No if x and valuation: x = x.shift(valuation - x.valuation()) if degree is None and not x: + if valuation is None: + raise ValueError("you must specify the degree for the polynomial 0") degree = valuation - coeff_stream = CoefficientStream_exact([x], self._sparse, constant=constant, degree=degree) + # print('it breaks here') + # print(x) + # x = R(x) + # print(x) + # x = [R(x)] + # print(x) + # print(x) + # print(type(x)) + if x == R.zero(): + coeff_stream = CoefficientStream_exact([x], self._sparse, valuation=degree-1, constant=constant) + return self.element_class(self, coeff_stream) + p_list = [x[i] for i in range(x.valuation(), x.degree() + 1)] + coeff_stream = CoefficientStream_exact(p_list, self._sparse, valuation=x.valuation(), 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,9 +342,8 @@ 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)) 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_exact(p, self._sparse, valuation=valuation, 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") @@ -343,9 +357,9 @@ def _an_element_(self): sage: L.an_element() z^-10 + z^-9 + z^-8 + ... """ - c = self.base_ring().an_element() + c = self.base_ring()(1) R = self._laurent_poly_ring - return self.element_class(self, CoefficientStream_exact([R.zero()], self._sparse, c, -10)) + return self.element_class(self, CoefficientStream_exact([R.zero()], self._sparse, valuation=-11, constant=c)) @cached_method def one(self):