Skip to content

Commit

Permalink
Improve is_empty and is_finite in a few cases
Browse files Browse the repository at this point in the history
  • Loading branch information
user202729 committed Feb 4, 2025
1 parent dc99dc8 commit c6fd715
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 11 deletions.
76 changes: 67 additions & 9 deletions src/sage/categories/sets_cat.py
Original file line number Diff line number Diff line change
Expand Up @@ -2373,8 +2373,32 @@ def is_empty(self):
False
sage: cartesian_product([S1,S2,S1]).is_empty()
True
"""
return any(c.is_empty() for c in self.cartesian_factors())
Even when some parent did not implement ``is_empty``,
as long as one element is nonempty, the result can be determined::
sage: C = ConditionSet(QQ, lambda x: x > 0)
sage: C.is_empty()
Traceback (most recent call last):
...
AttributeError...
sage: cartesian_product([C,[]]).is_empty()
True
sage: cartesian_product([C,C]).is_empty()
Traceback (most recent call last):
...
NotImplementedError...
"""
last_exception = None
for c in self.cartesian_factors():
try:
if c.is_empty():
return True
except (AttributeError, NotImplementedError) as e:
last_exception = e
if last_exception is not None:
raise NotImplementedError from last_exception
return False

def is_finite(self):
r"""
Expand All @@ -2391,18 +2415,52 @@ def is_finite(self):
False
sage: cartesian_product([ZZ, Set(), ZZ]).is_finite()
True
TESTS:
This should still work even if some parent does not implement
``is_finite``::
sage: known_infinite_set = ZZ
sage: unknown_infinite_set = Set([1]) + ConditionSet(QQ, lambda x: x > 0)
sage: unknown_infinite_set.is_empty()
False
sage: unknown_infinite_set.is_finite()
Traceback (most recent call last):
...
AttributeError...
sage: cartesian_product([unknown_infinite_set, known_infinite_set]).is_finite()
False
sage: unknown_empty_set = ConditionSet(QQ, lambda x: False)
sage: cartesian_product([known_infinite_set, unknown_empty_set]).is_finite()
Traceback (most recent call last):
...
NotImplementedError...
sage: cartesian_product([unknown_infinite_set, Set([])]).is_finite()
True
"""
f = self.cartesian_factors()
try:
# Note: some parent might not implement "is_empty". So we
# carefully isolate this test.
test = any(c.is_empty() for c in f)
if self.is_empty():
return True
except (AttributeError, NotImplementedError):
pass
else:
if test:
return test
return all(c.is_finite() for c in f)
# it is unknown whether some set may be empty
if all(c.is_finite() for c in self.cartesian_factors()):
return True

Check warning on line 2450 in src/sage/categories/sets_cat.py

View check run for this annotation

Codecov / codecov/patch

src/sage/categories/sets_cat.py#L2450

Added line #L2450 was not covered by tests
raise NotImplementedError

# in this case, all sets are definitely nonempty
last_exception = None
for c in self.cartesian_factors():
try:
if not c.is_finite():
return False
except (AttributeError, NotImplementedError) as e:
last_exception = e
if last_exception is not None:
raise NotImplementedError from last_exception
return True

Check warning on line 2463 in src/sage/categories/sets_cat.py

View check run for this annotation

Codecov / codecov/patch

src/sage/categories/sets_cat.py#L2461-L2463

Added lines #L2461 - L2463 were not covered by tests

def cardinality(self):
r"""
Expand Down
10 changes: 8 additions & 2 deletions src/sage/sets/family.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -968,8 +968,14 @@ class LazyFamily(AbstractFamily):
category = InfiniteEnumeratedSets()
elif isinstance(set, (list, tuple, range)):
category = FiniteEnumeratedSets()
else:
category = EnumeratedSets()
else: # some sets such as QQ implements is_finite() but is not in InfiniteEnumeratedSets()
try:
if set.is_finite():
category = FiniteEnumeratedSets()
else:
category = InfiniteEnumeratedSets()
except (AttributeError, NotImplementedError):
category = EnumeratedSets()
Parent.__init__(self, category=category)
Expand Down
32 changes: 32 additions & 0 deletions src/sage/sets/set.py
Original file line number Diff line number Diff line change
Expand Up @@ -1520,6 +1520,38 @@ def _sympy_(self):
sympy_init()
return Union(self._X._sympy_(), self._Y._sympy_())

def __bool__(self):
"""
Return ``True`` if this set is not empty.
EXAMPLES::
sage: bool(Set(GF(3)).union(Set(GF(2))))
True
sage: bool(Set(GF(3)).intersection(Set(GF(2))))
False
TESTS:
This should still work in the case the first set is nonempty
and the second set has :meth:`is_empty` unimplemented::
sage: C = ConditionSet(QQ, lambda x: x > 0)
sage: C.is_empty()
Traceback (most recent call last):
...
AttributeError...
sage: C.is_finite()
Traceback (most recent call last):
...
AttributeError...
sage: bool(Set([1]) + C)
True
sage: (Set([1]) + C).is_empty()
False
"""
return bool(self._X) or bool(self._Y)


class Set_object_intersection(Set_object_binary):
"""
Expand Down

0 comments on commit c6fd715

Please sign in to comment.