From e6a0ba5bc4bbc8f532c1a011f899052538ea96d9 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 4 Apr 2023 00:23:49 +0900 Subject: [PATCH] Change so products so the iterator produces 1 + p(x). --- src/sage/data_structures/stream.py | 47 ++++++++++++++++++------------ src/sage/rings/lazy_series_ring.py | 36 ++++++++++++++++++----- 2 files changed, 56 insertions(+), 27 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 652ba51b93b..a4bf3a7fbb9 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -3225,27 +3225,33 @@ class Stream_infinite_product(Stream): r""" Stream defined by an infinite product. - The ``iterator`` returns elements `p_i` to compute the product - `\prod_{i \in I} (1 + p_i)`. The valuation of `p_i` is weakly increasing - as we iterate over `I` and there are only finitely many terms with any - fixed valuation. In particular, this assumes the product is nonzero. + The ``iterator`` returns elements either `1 + p_i` to compute the + product `\prod_{i \in I} (1 + p_i)`. The valuation of `p_i` is weakly + increasing as we iterate over `I` and there are only finitely many + terms with any fixed valuation. In particular, this *assumes* the + product is nonzero. - For the ``_approximate_order``, this assumes the ring is a - lazy series. + .. WARNING:: + + This does not check that the input is valid. + + INPUT: + + - ``iterator`` -- the iterator for the factors + - ``constant_order`` -- the order corresponding to the constant term """ - def __init__(self, iterator, one): + def __init__(self, iterator, constant_order): """ Initialize ``self``. EXAMPLES:: sage: from sage.data_structures.stream import Stream_infinite_product - sage: TestSuite(f2).run() """ self._prod_iter = iterator self._cur = None self._cur_order = -infinity - self._one = one + self._constant_order = constant_order super().__init__(False) @lazy_attribute @@ -3266,11 +3272,16 @@ def _approximate_order(self): def _advance(self): """ - Advance the iterator so that the approximate order increases by one. + Advance the iterator so that the approximate order increases + by at least one. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_infinite_product """ if self._cur is None: temp = next(self._prod_iter) - self._cur = self._one + temp + self._cur = temp self._cur_order = temp._coeff_stream._approximate_order order = self._cur_order while order == self._cur_order: @@ -3278,15 +3289,13 @@ def _advance(self): next_factor = next(self._prod_iter) except StopIteration: self._cur_order = infinity + return + self._cur *= next_factor + # nonzero checks are safer than equality checks (i.e. in lazy series) + if order == self._constant_order and not (next_factor._coeff_stream[order] - 1): + order += 1 break - coeff_stream = next_factor._coeff_stream - while coeff_stream._approximate_order < order: - # This check also updates the next_factor._approximate_order - if coeff_stream[coeff_stream._approximate_order]: - order = coeff_stream._approximate_order - raise ValueError(f"invalid product computation with invalid order {order} < {self._cur_order}") - self._cur *= self._one + next_factor - if not coeff_stream[coeff_stream._approximate_order]: + while not next_factor._coeff_stream[order]: order += 1 self._cur_order = order diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 7a68b140b35..4f527b04a22 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -863,28 +863,44 @@ def prod(self, args, index_set=None): INPUT: - - ``args`` -- a list (or iterable) of elements of ``self`` + - ``args`` -- a list (or iterable) of elements of ``self`` or when + ``index_set`` is given, a function that returns elements of ``self`` - ``index_set`` -- (optional) an indexing set for the product or - or a boolean + or ``True`` If ``index_set`` is an iterable, then ``args`` should be a function - that takes in an index and returns an element `p_i` of ``self`` to + that takes in an index and returns an element `1 + p_i` of ``self`` to compute the product `\prod_{i \in I} (1 + p_i)`. The valuation of `p_i` is weakly increasing as we iterate over `I` and there are only - finitely many terms with any fixed valuation. If ``index=True``, - then this will treat ``args`` as an infinite product indexed by - `0, 1, \ldots`. In particular, this assumes the product is nonzero. + finitely many terms with any fixed valuation. If ``index_set=True``, + then this will treat ``args`` as an iterator for a potentially + infinite product. In particular, this assumes the product is nonzero. + + .. WARNING:: + + If invalid input is given, this may loop forever. EXAMPLES:: sage: L. = LazyLaurentSeriesRing(QQ) - sage: euler = L.prod(lambda n: -t^n, PositiveIntegers()) + sage: euler = L.prod(lambda n: 1 - t^n, PositiveIntegers()) sage: euler 1 - t - t^2 + t^5 + O(t^7) sage: 1 / euler 1 + t + 2*t^2 + 3*t^3 + 5*t^4 + 7*t^5 + 11*t^6 + O(t^7) sage: euler - L.euler() O(t^7) + + sage: L.prod((1 - t^n for n in PositiveIntegers()), index_set=True) + 1 - t - t^2 + t^5 + O(t^7) + + sage: L.prod((1 + t^(n-3) for n in PositiveIntegers()), index_set=True) + 2*t^-3 + 4*t^-2 + 4*t^-1 + 4 + 6*t + 10*t^2 + 16*t^3 + O(t^4) + + sage: D = LazyDirichletSeriesRing(QQ, "s") + sage: ret = D.prod(lambda p: (1+D(1, valuation=p)).inverse(), index_set=Primes()) + sage: ret + 1 - 1/(2^s) - 1/(3^s) + 1/(4^s) - 1/(5^s) + 1/(6^s) - 1/(7^s) + O(1/(8^s)) """ if index_set is None: return super().prod(args) @@ -893,7 +909,11 @@ def prod(self, args, index_set=None): else: it = (args(i) for i in index_set) from sage.data_structures.stream import Stream_infinite_product - coeff_stream = Stream_infinite_product(it, self.one()) + if self._minimal_valuation is not None and self._minimal_valuation > 0: + # Only for Dirichlet series (currently) + coeff_stream = Stream_infinite_product(it, self._minimal_valuation) + else: + coeff_stream = Stream_infinite_product(it, 0) return self.element_class(self, coeff_stream) def _test_invert(self, **options):