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):