Skip to content

Commit

Permalink
Trac #34374: Use cantor_product for Cartesian products of infinite en…
Browse files Browse the repository at this point in the history
…umerated sets

(split out from #19195)

URL: https://trac.sagemath.org/34374
Reported by: mkoeppe
Ticket author(s): Matthias Koeppe
Reviewer(s): Travis Scrimshaw
  • Loading branch information
Release Manager committed Aug 29, 2022
2 parents 1749b85 + a20341e commit f84d7a4
Showing 1 changed file with 49 additions and 32 deletions.
81 changes: 49 additions & 32 deletions src/sage/categories/sets_cat.py
Original file line number Diff line number Diff line change
Expand Up @@ -2217,7 +2217,14 @@ def example(self):
class ParentMethods:
def __iter__(self):
r"""
Return a lexicographic iterator for the elements of this Cartesian product.
Return an iterator for the elements of this Cartesian product.
If all factors (except possibly the first factor) are known to be finite,
it uses the lexicographic order.
Otherwise, the iterator enumerates the elements in increasing
order of sum-of-ranks, refined by the reverse lexicographic order
(see :func:`~sage.misc.mrange.cantor_product`).
EXAMPLES:
Expand Down Expand Up @@ -2260,29 +2267,38 @@ def __iter__(self):
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 10, 9])
.. WARNING::
The elements are returned in lexicographic order,
which gives a valid enumeration only if all
factors, but possibly the first one, are
finite. So the following one is fine::
sage: it = iter(cartesian_product([ZZ, GF(2)]))
sage: [next(it) for _ in range(10)]
[(0, 0), (0, 1), (1, 0), (1, 1),
(-1, 0), (-1, 1), (2, 0), (2, 1),
(-2, 0), (-2, 1)]
But this one is not::
sage: it = iter(cartesian_product([GF(2), ZZ]))
sage: [next(it) for _ in range(10)]
doctest:...: UserWarning: Sage is not able to determine
whether the factors of this Cartesian product are
finite. The lexicographic ordering might not go through
all elements.
[(0, 0), (0, 1), (0, -1), (0, 2), (0, -2),
(0, 3), (0, -3), (0, 4), (0, -4), (0, 5)]
When all factors (except possibly the first factor) are known to be finite, it
uses the lexicographic order::
sage: it = iter(cartesian_product([ZZ, GF(2)]))
sage: [next(it) for _ in range(10)]
[(0, 0), (0, 1),
(1, 0), (1, 1),
(-1, 0), (-1, 1),
(2, 0), (2, 1),
(-2, 0), (-2, 1)]
When other factors are infinite (or not known to be finite), it enumerates
the elements in increasing order of sum-of-ranks::
sage: NN = NonNegativeIntegers()
sage: it = iter(cartesian_product([NN, NN]))
sage: [next(it) for _ in range(10)]
[(0, 0),
(1, 0), (0, 1),
(2, 0), (1, 1), (0, 2),
(3, 0), (2, 1), (1, 2), (0, 3)]
An example with the first factor finite, the second infinite::
sage: it = iter(cartesian_product([GF(2), ZZ]))
sage: [next(it) for _ in range(11)]
[(0, 0),
(1, 0), (0, 1),
(1, 1), (0, -1),
(1, -1), (0, 2),
(1, 2), (0, -2),
(1, -2), (0, 3)]
.. NOTE::
Expand All @@ -2292,17 +2308,18 @@ def __iter__(self):
ALGORITHM:
Recipe 19.9 in the Python Cookbook by Alex Martelli
and David Ascher.
The lexicographic enumeration follows Recipe 19.9 in the Python Cookbook
by Alex Martelli and David Ascher.
"""
if any(f not in Sets().Finite() for f in self.cartesian_factors()[1:]):
from warnings import warn
warn("Sage is not able to determine whether the factors of "
"this Cartesian product are finite. The lexicographic "
"ordering might not go through all elements.")
factors = list(self.cartesian_factors())
if any(f not in Sets().Finite() for f in factors[1:]):
from sage.misc.mrange import cantor_product
for t in cantor_product(*factors):
yield self._cartesian_product_of_elements(t)
return

# Lexicographic enumeration:
# visualize an odometer, with "wheels" displaying "digits"...:
factors = list(self.cartesian_factors())
wheels = [iter(f) for f in factors]
try:
digits = [next(it) for it in wheels]
Expand Down

0 comments on commit f84d7a4

Please sign in to comment.