Skip to content

Commit

Permalink
Change so products so the iterator produces 1 + p(x).
Browse files Browse the repository at this point in the history
  • Loading branch information
tscrim committed Apr 3, 2023
1 parent dab9b3a commit e6a0ba5
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 27 deletions.
47 changes: 28 additions & 19 deletions src/sage/data_structures/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -3266,27 +3272,30 @@ 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:
try:
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

Expand Down
36 changes: 28 additions & 8 deletions src/sage/rings/lazy_series_ring.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.<t> = 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)
Expand All @@ -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):
Expand Down

0 comments on commit e6a0ba5

Please sign in to comment.