Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
Trac 18290: reviewer comments
Browse files Browse the repository at this point in the history
  • Loading branch information
videlec committed May 12, 2015
1 parent c5d6006 commit 6389ff2
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 157 deletions.
18 changes: 15 additions & 3 deletions src/sage/categories/additive_magmas.py
Original file line number Diff line number Diff line change
Expand Up @@ -766,12 +766,24 @@ def is_empty(self):
EXAMPLES::
sage: NN.is_empty()
sage: A = AdditiveAbelianGroup([3,3])
sage: A in AdditiveMagmas()
True
sage: A.is_empty()
False
TESTS::
sage: B = CommutativeAdditiveMonoids().example()
sage: B.is_empty()
False
sage: NN.is_empty.__module__
TESTS:
We check that the method `is_empty` is inherited from this
category in both examples above::
sage: A.is_empty.__module__
'sage.categories.additive_magmas'
sage: B.is_empty.__module__
'sage.categories.additive_magmas'
"""
return False
Expand Down
41 changes: 27 additions & 14 deletions src/sage/categories/enumerated_sets.py
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,7 @@ class CartesianProducts(CartesianProductsCategory):
class ParentMethods:
def __iter__(self):
r"""
Return an iterator for the elements of this cartesian product.
Return a lexicographic iterator for the elements of this cartesian product.
EXAMPLES::
Expand Down Expand Up @@ -754,20 +754,30 @@ def __iter__(self):
.. WARNING::
The elements are returned in lexicographic order,
which gives a valid enumeration only in the finite
case::
sage: it = iter(cartesian_product([ZZ,GF(5)]))
sage: it.next()
Traceback (most recent call last):
...
ValueError: the iteration order of cartesian product of
infinite sets is not well defined
which gives a valid enumeration only if the factors
excluding the first one are finite. So the following one is
fine::
sage: it = iter(cartesian_product([ZZ, GF(2)]))
sage: [it.next() 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: [it.next() 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)]
.. NOTE::
Here it would be faster to use :meth:`itertools.product` for sets
Here it would be faster to use :func:`itertools.product` for sets
of small size. But the latter expands all factor in memory!
So we can not reasonably use it in general.
Expand All @@ -776,8 +786,11 @@ def __iter__(self):
Recipe 19.9 in the Python Cookbook by Alex Martelli
and David Ascher.
"""
if not self.is_finite():
raise ValueError("the iteration order of cartesian product of infinite sets is not well defined")
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.")

# visualize an odometer, with "wheels" displaying "digits"...:
factors = list(self.cartesian_factors())
Expand Down
50 changes: 14 additions & 36 deletions src/sage/categories/finite_enumerated_sets.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,59 +484,37 @@ def extra_super_categories(self):
return [FiniteEnumeratedSets()]

class ParentMethods:
# Ambiguity resolution between methods inherited from
# Sets.CartesianProducts and from EnumeratedSets.Finite.
random_element = Sets.CartesianProducts.ParentMethods.random_element.im_func
r"""
Return a random element.
TESTS:
Ideally, these tests should be just after the declaration of the
associated attributes. But doing this way, Sage will not consider
them as a doctest.
We check that cartesian products of finite enumerated sets
inherit the `random` method from `Sets.CartesianProducts`
inherit various methods from `Sets.CartesianProducts`
and not from :class:`EnumeratedSets.Finite`::
sage: C = cartesian_product([Permutations(10), Permutations(10)])
sage: C = cartesian_product([Partitions(10), Permutations(20)])
sage: C in EnumeratedSets().Finite()
True
sage: C.random_element.__module__
'sage.categories.sets_cat'
"""
cardinality = Sets.CartesianProducts.ParentMethods.cardinality.im_func
r"""
Return the cardinality.
TESTS:
We check that cartesian products of finite enumerated sets
inherit the `cardinality` method from `Sets.CartesianProducts`
and not from :class:`EnumeratedSets.Finite`::
sage: C = cartesian_product([Partitions(10), Permutations(12)])
sage: C in EnumeratedSets().Finite()
True
sage: C.cardinality.__module__
'sage.categories.sets_cat'
"""

__iter__ = EnumeratedSets.CartesianProducts.ParentMethods.__iter__.im_func
r"""
Return an iterator through the element of this cartesian product
TESTS:
We check that cartesian products of finite enumerated sets
inherit the `__iter__` method from `Sets.CartesianProducts`
and not from :class:`EnumeratedSets.Finite`::
sage: C = cartesian_product([Partitions(10), Permutations(12)])
sage: C in EnumeratedSets().Finite()
True
sage: C.__iter__.__module__
'sage.categories.enumerated_sets'
"""

# Ambiguity resolution between methods inherited from
# Sets.CartesianProducts and from EnumeratedSets.Finite.
random_element = Sets.CartesianProducts.ParentMethods.random_element.im_func
cardinality = Sets.CartesianProducts.ParentMethods.cardinality.im_func
__iter__ = EnumeratedSets.CartesianProducts.ParentMethods.__iter__.im_func

def last(self):
r"""
Return the last element
Expand Down Expand Up @@ -645,7 +623,7 @@ def unrank(self, i):
card = c.cardinality()
elt.insert(0, c.unrank(i % card))
i //= card
if i > 0:
if i:
raise IndexError("index i (={}) is greater than the cardinality".format(i))
return self._cartesian_product_of_elements(elt)

Expand Down
3 changes: 1 addition & 2 deletions src/sage/categories/finite_permutation_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ class FinitePermutationGroups(CategoryWithAxiom):
sage: C.super_categories()
[Category of permutation groups,
Category of finite groups,
Category of finite finitely generated semigroups,
Category of unital finitely generated magmas]
Category of finite finitely generated semigroups]
sage: C.example()
Dihedral group of order 6 as a permutation group
Expand Down
30 changes: 0 additions & 30 deletions src/sage/categories/finitely_generated_magmas.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,33 +53,3 @@ def magma_generators(self):
sage: S.magma_generators()
Family ('a', 'b', 'c', 'd')
"""

class Unital(CategoryWithAxiom):
class ParentMethods:
def is_empty(self):
r"""
Return whether ``self`` is empty.
Since this set is a unital magma it is not empty and this method
always return ``False``.
EXAMPLES::
sage: SymmetricGroup(2).is_empty()
False
TESTS::
sage: SymmetricGroup(2).is_empty.__module__
'sage.categories.finitely_generated_magmas'
.. TODO::
This method belong to ``Magmas.Unital.ParentMethods``. But due to
another implementation of ``is_empty`` in ``EnumeratedSets``
and the fact that ``Enumerated`` is not an axiom we get that
for an enumerated unital magmas the ``is_empty`` which is
used is the one from enumerated sets. But it should be this
one!
"""
return False
26 changes: 26 additions & 0 deletions src/sage/categories/magmas.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,32 @@ def _test_one(self, **options):
tester.assertEqual(type(one.__hash__()), int)
tester.assertEqual(one.__hash__(), one.__hash__())

def is_empty(self):
r"""
Return whether ``self`` is empty.
Since this set is a unital magma it is not empty and this method
always return ``False``.
EXAMPLES::
sage: S = SymmetricGroup(2)
sage: S.is_empty()
False
sage: M = Monoids().example()
sage: M.is_empty()
False
TESTS::
sage: S.is_empty.__module__
'sage.categories.magmas'
sage: M.is_empty.__module__
'sage.categories.magmas'
"""
return False

class SubcategoryMethods:

@cached_method
Expand Down
98 changes: 26 additions & 72 deletions src/sage/categories/sets_cat.py
Original file line number Diff line number Diff line change
Expand Up @@ -1352,70 +1352,16 @@ def _test_some_elements(self, **options):
tester.assertTrue(x in self, LazyFormat(
"the object %s in self.some_elements() is not in self")%(x,))

def cardinality(self):
"""
The cardinality of ``self``.
``self.cardinality()`` should return the cardinality of the set
``self`` as a sage :class:`Integer` or as ``infinity``.
This if the default implementation from the category
``Sets()``; it raises a ``NotImplementedError`` since one
does not know whether the set is finite or not.
EXAMPLES::
sage: class broken(UniqueRepresentation, Parent):
....: def __init__(self):
....: Parent.__init__(self, category = Sets())
....: def _repr_(self):
....: return 'broken parent'
sage: broken().cardinality()
Traceback (most recent call last):
...
NotImplementedError: unknown cardinality for 'broken parent'
"""
raise NotImplementedError("unknown cardinality for '{}'".format(self))
#Note: the four methods 'cardinality', 'is_finite_, 'is_empty' and
# 'random_element' might or might not be implemented in the parent
# objects. Most of the time a default implementation will be provided by
# a subcategory of Sets. We do not declare them as optional abstract
# methods to not pollute the namespace.

def is_finite(self):
r"""
Return whether this set is finite.
EXAMPLES::
sage: Set([1,2,3]).is_finite()
True
"""
from sage.rings.infinity import Infinity
return self.cardinality() != Infinity

def is_empty(self):
r"""
Return whether this set is empty.
EXAMPLES::
sage: Set([]).is_empty()
True
"""
return self.cardinality().is_zero()

def random_element(self):
r"""
TESTS::
sage: class Broken(UniqueRepresentation, Parent):
....: def __init__(self):
....: Parent.__init__(self, category=Sets())
....: def _repr_(self):
....: return "broken parent"
sage: Broken().random_element()
Traceback (most recent call last):
...
NotImplementedError: random_element not implemented
for 'broken parent'
"""
raise NotImplementedError("random_element not implemented for '{}'".format(self))
# def cardinality(self)
# def is_finite(self)
# def is_empty(self)
# def random_element(self):

# Functorial constructions

Expand Down Expand Up @@ -2082,15 +2028,23 @@ def cardinality(self):
True
"""
f = self.cartesian_factors()
if any(c.is_empty() for c in f):
from sage.rings.integer_ring import ZZ
return ZZ.zero()
elif any(not c.is_finite() for c in f):
from sage.rings.infinity import Infinity
return Infinity

try:
# Note: some parent might not implement "is_empty". So we
# carefully isolate this test.
is_empty = any(c.is_empty() for c in f)
except Exception:
pass
else:
from sage.misc.misc_c import prod
return prod(c.cardinality() for c in f)
if is_empty:
from sage.rings.integer_ring import ZZ
return ZZ.zero()
elif any(c in Sets().Infinite() for c in f):
from sage.rings.infinity import Infinity
return Infinity

from sage.misc.misc_c import prod
return prod(c.cardinality() for c in f)

def random_element(self, *args):
r"""
Expand Down Expand Up @@ -2120,7 +2074,7 @@ def random_element(self, *args):
True
"""
return self._cartesian_product_of_elements(
tuple(c.random_element(*args) for c in self.cartesian_factors()))
c.random_element(*args) for c in self.cartesian_factors())

@abstract_method
def _sets_keys(self):
Expand Down

0 comments on commit 6389ff2

Please sign in to comment.