diff --git a/src/sage/categories/enumerated_sets.py b/src/sage/categories/enumerated_sets.py index 354417b7b37..3874af5a60d 100644 --- a/src/sage/categories/enumerated_sets.py +++ b/src/sage/categories/enumerated_sets.py @@ -934,29 +934,6 @@ def _some_elements_from_iterator(self): some_elements = _some_elements_from_iterator - def random_element(self): - """ - Return a random element in ``self``. - - Unless otherwise stated, and for finite enumerated sets, - the probability is uniform. - - This is a generic implementation from the category - ``EnumeratedSets()``. It raises a :exc:`NotImplementedError` - since one does not know whether the set is finite. - - EXAMPLES:: - - sage: class broken(UniqueRepresentation, Parent): - ....: def __init__(self): - ....: Parent.__init__(self, category = EnumeratedSets()) - sage: broken().random_element() - Traceback (most recent call last): - ... - NotImplementedError: unknown cardinality - """ - raise NotImplementedError("unknown cardinality") - def map(self, f, name=None, *, is_injective=True): r""" Return the image `\{f(x) | x \in \text{self}\}` of this diff --git a/src/sage/categories/finite_enumerated_sets.py b/src/sage/categories/finite_enumerated_sets.py index cff04249ddc..4abdc8ffd3d 100644 --- a/src/sage/categories/finite_enumerated_sets.py +++ b/src/sage/categories/finite_enumerated_sets.py @@ -543,7 +543,9 @@ def _test_random(self, random_seed=123332938836894739865, **options): E = float(N) / float(n) chi_2 = sum(float(o) ** 2 for o in Counter(elements).values()) / E - float(N) - tester.assertLessEqual(chi_2, critical, f"assuming random_element of {self} follows a uniform distribution, this outcome would only occur with probability {1-T.cum_distribution_function(chi_2)}") + tester.assertLessEqual( + chi_2, critical, f"assuming random_element of {self} follows a uniform distribution, " + f"this outcome would only occur with probability {1-T.cum_distribution_function(chi_2)} (using seed {random_seed})") def _test_rank(self, **options): r""" diff --git a/src/sage/categories/infinite_enumerated_sets.py b/src/sage/categories/infinite_enumerated_sets.py index e27008a30ec..1449bfb390f 100644 --- a/src/sage/categories/infinite_enumerated_sets.py +++ b/src/sage/categories/infinite_enumerated_sets.py @@ -45,22 +45,6 @@ class InfiniteEnumeratedSets(CategoryWithAxiom): class ParentMethods: - def random_element(self): - """ - Raise an error because ``self`` is an infinite enumerated set. - - EXAMPLES:: - - sage: NN = InfiniteEnumeratedSets().example() - sage: NN.random_element() - Traceback (most recent call last): - ... - NotImplementedError: infinite set - - TODO: should this be an optional abstract_method instead? - """ - raise NotImplementedError("infinite set") - def tuple(self): """ Raise an error because ``self`` is an infinite enumerated set. diff --git a/src/sage/sets/condition_set.py b/src/sage/sets/condition_set.py index a0d618ebdde..62ee6f7b95e 100644 --- a/src/sage/sets/condition_set.py +++ b/src/sage/sets/condition_set.py @@ -392,6 +392,20 @@ def _an_element_(self): return element raise NotImplementedError + def random_element(self): + """ + Return a random element in this set. Note that this might run forever. + + EXAMPLES:: + + sage: ConditionSet(Set(ZZ), lambda x: x == 0).random_element() + 0 + """ + while True: + x = self._universe.random_element() + if x in self: + return x + def ambient(self): r""" Return the universe of ``self``. diff --git a/src/sage/sets/non_negative_integers.py b/src/sage/sets/non_negative_integers.py index ab7960df3d1..9a0255a7fa4 100644 --- a/src/sage/sets/non_negative_integers.py +++ b/src/sage/sets/non_negative_integers.py @@ -12,6 +12,7 @@ from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.structure.unique_representation import UniqueRepresentation from sage.rings.integer import Integer +from sage.rings.integer_ring import ZZ class NonNegativeIntegers(UniqueRepresentation, Parent): @@ -195,6 +196,15 @@ def an_element(self): """ return self.from_integer(Integer(42)) + def random_element(self): + """ + EXAMPLES:: + + sage: NonNegativeIntegers().an_element() + 42 + """ + return self.from_integer(ZZ.random_element().abs()) + def some_elements(self): """ EXAMPLES:: diff --git a/src/sage/sets/set.py b/src/sage/sets/set.py index 510d799469a..f3f530bcfd2 100644 --- a/src/sage/sets/set.py +++ b/src/sage/sets/set.py @@ -294,10 +294,23 @@ def difference(self, X): sage: X = Set(GF(9,'b')).difference(Set(GF(27,'b'))); X # needs sage.rings.finite_rings {0, 1, 2, b, b + 1, b + 2, 2*b, 2*b + 1, 2*b + 2} + + TESTS: + + Check that some trivial simplifications are performed:: + + sage: Set(QQ) - Set([]) + Set of elements of Rational Field + sage: Set(ZZ) - (Set(ZZ) - Set([0, 1/2, 1])) + Set-theoretic intersection of Set of elements of Integer Ring and {0, 1, 1/2} """ if isinstance(X, (Set_generic, Set_base)): if self is X: return Set([]) + if X.is_empty(): + return self + if isinstance(X, Set_object_difference) and X._X == self: + return self.intersection(X._Y) return Set_object_difference(self, X) raise TypeError("X (=%s) must be a Set" % X) @@ -602,6 +615,19 @@ def _an_element_(self): pass return self._an_element_from_iterator() + def random_element(self): + """ + Return a random element of ``self``. + + EXAMPLES:: + + sage: Set(ZZ).random_element() # random + 4 + """ + if self.__object is self: + raise NotImplementedError # some subclasses uses __object weirdly... + return self.__object.random_element() + def __contains__(self, x): """ Return ``True`` if `x` is in ``self``. @@ -1503,6 +1529,18 @@ def cardinality(self): """ return self._X.cardinality() + self._Y.cardinality() + def random_element(self): + """ + Return a random element in this set. + Note that even when the set is finite, the distribution may not be uniform. + + EXAMPLES:: + + sage: x = (Set(ZZ) | Set(RR)).random_element(); x # random + 1 + """ + return choice([self._X, self._Y]).random_element() + @cached_method def _sympy_(self): """ @@ -1582,7 +1620,8 @@ def __init__(self, X, Y, category=None): 25 sage: X.category() Category of finite enumerated sets - sage: TestSuite(X).run() + sage: TestSuite(X).run(skip=["_test_random"]) + sage: for seed in range(1, 100): X._test_random(seed) sage: X = Set(Primes(), category=Sets()).intersection(Set(IntegerRange(200))) sage: X.cardinality() @@ -1687,6 +1726,31 @@ def __iter__(self): if x in Y: yield x + def random_element(self): + """ + Return a random element in this set. Note that this might run forever. + + EXAMPLES:: + + sage: (Set([1, 2]) & Set([2, 3])).random_element() + 2 + sage: (Set(NN) & ConditionSet(Set(ZZ), lambda x: x <= 0)).random_element() + 0 + """ + try: + y_is_finite = self._Y.is_finite() + except (AttributeError, NotImplementedError): + y_is_finite = None + if y_is_finite: # if Y.random_element() is uniform, this is also uniform + while True: + y = self._Y.random_element() + if y in self._X: + return y + while True: + x = self._X.random_element() + if x in self._Y: + return x + def __contains__(self, x): """ Return ``True`` if ``self`` contains ``x``. @@ -1914,6 +1978,23 @@ def _sympy_(self): sympy_init() return Complement(self._X._sympy_(), self._Y._sympy_()) + def random_element(self): + """ + Return a random element in this set. Note that this might run forever. + + EXAMPLES:: + + sage: x = (Set(ZZ) - Set([0])).random_element(); x # random + 1 + sage: assert x != 0, x + sage: x = (Set(ZZ) - (Set(ZZ) - Set([1]))).random_element(); x # random + """ + Y = self._Y + while True: + x = self._X.random_element() + if x not in Y: + return x + class Set_object_symmetric_difference(Set_object_binary): """