| from __future__ import annotations |
|
|
| from typing import Any, Callable, TYPE_CHECKING, overload |
| from functools import reduce |
| from collections import defaultdict |
| from collections.abc import Mapping, Iterable |
| import inspect |
|
|
| from sympy.core.kind import Kind, UndefinedKind, NumberKind |
| from sympy.core.basic import Basic |
| from sympy.core.containers import Tuple, TupleKind |
| from sympy.core.decorators import sympify_method_args, sympify_return |
| from sympy.core.evalf import EvalfMixin |
| from sympy.core.expr import Expr |
| from sympy.core.function import Lambda |
| from sympy.core.logic import (FuzzyBool, fuzzy_bool, fuzzy_or, fuzzy_and, |
| fuzzy_not) |
| from sympy.core.numbers import Float, Integer |
| from sympy.core.operations import LatticeOp |
| from sympy.core.parameters import global_parameters |
| from sympy.core.relational import Eq, Ne, is_lt |
| from sympy.core.singleton import Singleton, S |
| from sympy.core.sorting import ordered |
| from sympy.core.symbol import symbols, Symbol, Dummy, uniquely_named_symbol |
| from sympy.core.sympify import _sympify, sympify, _sympy_converter |
| from sympy.functions.elementary.exponential import exp, log |
| from sympy.functions.elementary.miscellaneous import Max, Min |
| from sympy.logic.boolalg import And, Or, Not, Xor, true, false |
| from sympy.utilities.decorator import deprecated |
| from sympy.utilities.exceptions import sympy_deprecation_warning |
| from sympy.utilities.iterables import (iproduct, sift, roundrobin, iterable, |
| subsets) |
| from sympy.utilities.misc import func_name, filldedent |
|
|
| from mpmath import mpi, mpf |
|
|
| from mpmath.libmp.libmpf import prec_to_dps |
|
|
|
|
| tfn = defaultdict(lambda: None, { |
| True: S.true, |
| S.true: S.true, |
| False: S.false, |
| S.false: S.false}) |
|
|
|
|
| @sympify_method_args |
| class Set(Basic, EvalfMixin): |
| """ |
| The base class for any kind of set. |
| |
| Explanation |
| =========== |
| |
| This is not meant to be used directly as a container of items. It does not |
| behave like the builtin ``set``; see :class:`FiniteSet` for that. |
| |
| Real intervals are represented by the :class:`Interval` class and unions of |
| sets by the :class:`Union` class. The empty set is represented by the |
| :class:`EmptySet` class and available as a singleton as ``S.EmptySet``. |
| """ |
|
|
| __slots__: tuple[()] = () |
|
|
| is_number = False |
| is_iterable = False |
| is_interval = False |
|
|
| is_FiniteSet = False |
| is_Interval = False |
| is_ProductSet = False |
| is_Union = False |
| is_Intersection: FuzzyBool = None |
| is_UniversalSet: FuzzyBool = None |
| is_Complement: FuzzyBool = None |
| is_ComplexRegion = False |
|
|
| is_empty: FuzzyBool = None |
| is_finite_set: FuzzyBool = None |
|
|
| @property |
| @deprecated( |
| """ |
| The is_EmptySet attribute of Set objects is deprecated. |
| Use 's is S.EmptySet" or 's.is_empty' instead. |
| """, |
| deprecated_since_version="1.5", |
| active_deprecations_target="deprecated-is-emptyset", |
| ) |
| def is_EmptySet(self): |
| return None |
|
|
| if TYPE_CHECKING: |
|
|
| def __new__(cls, *args: Basic | complex) -> Set: |
| ... |
|
|
| @overload |
| def subs(self, arg1: Mapping[Basic | complex, Set | complex], arg2: None=None) -> Set: ... |
| @overload |
| def subs(self, arg1: Iterable[tuple[Basic | complex, Set | complex]], arg2: None=None, **kwargs: Any) -> Set: ... |
| @overload |
| def subs(self, arg1: Set | complex, arg2: Set | complex) -> Set: ... |
| @overload |
| def subs(self, arg1: Mapping[Basic | complex, Basic | complex], arg2: None=None, **kwargs: Any) -> Basic: ... |
| @overload |
| def subs(self, arg1: Iterable[tuple[Basic | complex, Basic | complex]], arg2: None=None, **kwargs: Any) -> Basic: ... |
| @overload |
| def subs(self, arg1: Basic | complex, arg2: Basic | complex, **kwargs: Any) -> Basic: ... |
|
|
| def subs(self, arg1: Mapping[Basic | complex, Basic | complex] | Basic | complex, |
| arg2: Basic | complex | None = None, **kwargs: Any) -> Basic: |
| ... |
|
|
| def simplify(self, **kwargs) -> Set: |
| assert False |
|
|
| def evalf(self, n: int = 15, subs: dict[Basic, Basic | float] | None = None, |
| maxn: int = 100, chop: bool = False, strict: bool = False, |
| quad: str | None = None, verbose: bool = False) -> Set: |
| ... |
|
|
| n = evalf |
|
|
| @staticmethod |
| def _infimum_key(expr): |
| """ |
| Return infimum (if possible) else S.Infinity. |
| """ |
| try: |
| infimum = expr.inf |
| assert infimum.is_comparable |
| infimum = infimum.evalf() |
| except (NotImplementedError, |
| AttributeError, AssertionError, ValueError): |
| infimum = S.Infinity |
| return infimum |
|
|
| def union(self, other): |
| """ |
| Returns the union of ``self`` and ``other``. |
| |
| Examples |
| ======== |
| |
| As a shortcut it is possible to use the ``+`` operator: |
| |
| >>> from sympy import Interval, FiniteSet |
| >>> Interval(0, 1).union(Interval(2, 3)) |
| Union(Interval(0, 1), Interval(2, 3)) |
| >>> Interval(0, 1) + Interval(2, 3) |
| Union(Interval(0, 1), Interval(2, 3)) |
| >>> Interval(1, 2, True, True) + FiniteSet(2, 3) |
| Union({3}, Interval.Lopen(1, 2)) |
| |
| Similarly it is possible to use the ``-`` operator for set differences: |
| |
| >>> Interval(0, 2) - Interval(0, 1) |
| Interval.Lopen(1, 2) |
| >>> Interval(1, 3) - FiniteSet(2) |
| Union(Interval.Ropen(1, 2), Interval.Lopen(2, 3)) |
| |
| """ |
| return Union(self, other) |
|
|
| def intersect(self, other): |
| """ |
| Returns the intersection of 'self' and 'other'. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Interval |
| |
| >>> Interval(1, 3).intersect(Interval(1, 2)) |
| Interval(1, 2) |
| |
| >>> from sympy import imageset, Lambda, symbols, S |
| >>> n, m = symbols('n m') |
| >>> a = imageset(Lambda(n, 2*n), S.Integers) |
| >>> a.intersect(imageset(Lambda(m, 2*m + 1), S.Integers)) |
| EmptySet |
| |
| """ |
| return Intersection(self, other) |
|
|
| def intersection(self, other): |
| """ |
| Alias for :meth:`intersect()` |
| """ |
| return self.intersect(other) |
|
|
| def is_disjoint(self, other): |
| """ |
| Returns True if ``self`` and ``other`` are disjoint. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Interval |
| >>> Interval(0, 2).is_disjoint(Interval(1, 2)) |
| False |
| >>> Interval(0, 2).is_disjoint(Interval(3, 4)) |
| True |
| |
| References |
| ========== |
| |
| .. [1] https://en.wikipedia.org/wiki/Disjoint_sets |
| """ |
| return self.intersect(other) == S.EmptySet |
|
|
| def isdisjoint(self, other): |
| """ |
| Alias for :meth:`is_disjoint()` |
| """ |
| return self.is_disjoint(other) |
|
|
| def complement(self, universe): |
| r""" |
| The complement of 'self' w.r.t the given universe. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Interval, S |
| >>> Interval(0, 1).complement(S.Reals) |
| Union(Interval.open(-oo, 0), Interval.open(1, oo)) |
| |
| >>> Interval(0, 1).complement(S.UniversalSet) |
| Complement(UniversalSet, Interval(0, 1)) |
| |
| """ |
| return Complement(universe, self) |
|
|
| def _complement(self, other): |
| |
| if isinstance(self, ProductSet) and isinstance(other, ProductSet): |
| |
| if len(self.sets) != len(other.sets): |
| return other |
|
|
| |
| |
| overlaps = [] |
| pairs = list(zip(self.sets, other.sets)) |
| for n in range(len(pairs)): |
| sets = (o if i != n else o-s for i, (s, o) in enumerate(pairs)) |
| overlaps.append(ProductSet(*sets)) |
| return Union(*overlaps) |
|
|
| elif isinstance(other, Interval): |
| if isinstance(self, (Interval, FiniteSet)): |
| return Intersection(other, self.complement(S.Reals)) |
|
|
| elif isinstance(other, Union): |
| return Union(*(o - self for o in other.args)) |
|
|
| elif isinstance(other, Complement): |
| return Complement(other.args[0], Union(other.args[1], self), evaluate=False) |
|
|
| elif other is S.EmptySet: |
| return S.EmptySet |
|
|
| elif isinstance(other, FiniteSet): |
| sifted = sift(other, lambda x: fuzzy_bool(self.contains(x))) |
| |
| return Union(FiniteSet(*(sifted[False])), |
| Complement(FiniteSet(*(sifted[None])), self, evaluate=False) |
| if sifted[None] else S.EmptySet) |
|
|
| def symmetric_difference(self, other): |
| """ |
| Returns symmetric difference of ``self`` and ``other``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Interval, S |
| >>> Interval(1, 3).symmetric_difference(S.Reals) |
| Union(Interval.open(-oo, 1), Interval.open(3, oo)) |
| >>> Interval(1, 10).symmetric_difference(S.Reals) |
| Union(Interval.open(-oo, 1), Interval.open(10, oo)) |
| |
| >>> from sympy import S, EmptySet |
| >>> S.Reals.symmetric_difference(EmptySet) |
| Reals |
| |
| References |
| ========== |
| .. [1] https://en.wikipedia.org/wiki/Symmetric_difference |
| |
| """ |
| return SymmetricDifference(self, other) |
|
|
| def _symmetric_difference(self, other): |
| return Union(Complement(self, other), Complement(other, self)) |
|
|
| @property |
| def inf(self): |
| """ |
| The infimum of ``self``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Interval, Union |
| >>> Interval(0, 1).inf |
| 0 |
| >>> Union(Interval(0, 1), Interval(2, 3)).inf |
| 0 |
| |
| """ |
| return self._inf |
|
|
| @property |
| def _inf(self): |
| raise NotImplementedError("(%s)._inf" % self) |
|
|
| @property |
| def sup(self): |
| """ |
| The supremum of ``self``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Interval, Union |
| >>> Interval(0, 1).sup |
| 1 |
| >>> Union(Interval(0, 1), Interval(2, 3)).sup |
| 3 |
| |
| """ |
| return self._sup |
|
|
| @property |
| def _sup(self): |
| raise NotImplementedError("(%s)._sup" % self) |
|
|
| def contains(self, other): |
| """ |
| Returns a SymPy value indicating whether ``other`` is contained |
| in ``self``: ``true`` if it is, ``false`` if it is not, else |
| an unevaluated ``Contains`` expression (or, as in the case of |
| ConditionSet and a union of FiniteSet/Intervals, an expression |
| indicating the conditions for containment). |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Interval, S |
| >>> from sympy.abc import x |
| |
| >>> Interval(0, 1).contains(0.5) |
| True |
| |
| As a shortcut it is possible to use the ``in`` operator, but that |
| will raise an error unless an affirmative true or false is not |
| obtained. |
| |
| >>> Interval(0, 1).contains(x) |
| (0 <= x) & (x <= 1) |
| >>> x in Interval(0, 1) |
| Traceback (most recent call last): |
| ... |
| TypeError: did not evaluate to a bool: None |
| |
| The result of 'in' is a bool, not a SymPy value |
| |
| >>> 1 in Interval(0, 2) |
| True |
| >>> _ is S.true |
| False |
| """ |
| from .contains import Contains |
| other = sympify(other, strict=True) |
|
|
| c = self._contains(other) |
| if isinstance(c, Contains): |
| return c |
| if c is None: |
| return Contains(other, self, evaluate=False) |
| b = tfn[c] |
| if b is None: |
| return c |
| return b |
|
|
| def _contains(self, other): |
| """Test if ``other`` is an element of the set ``self``. |
| |
| This is an internal method that is expected to be overridden by |
| subclasses of ``Set`` and will be called by the public |
| :func:`Set.contains` method or the :class:`Contains` expression. |
| |
| Parameters |
| ========== |
| |
| other: Sympified :class:`Basic` instance |
| The object whose membership in ``self`` is to be tested. |
| |
| Returns |
| ======= |
| |
| Symbolic :class:`Boolean` or ``None``. |
| |
| A return value of ``None`` indicates that it is unknown whether |
| ``other`` is contained in ``self``. Returning ``None`` from here |
| ensures that ``self.contains(other)`` or ``Contains(self, other)`` will |
| return an unevaluated :class:`Contains` expression. |
| |
| If not ``None`` then the returned value is a :class:`Boolean` that is |
| logically equivalent to the statement that ``other`` is an element of |
| ``self``. Usually this would be either ``S.true`` or ``S.false`` but |
| not always. |
| """ |
| raise NotImplementedError(f"{type(self).__name__}._contains") |
|
|
| def is_subset(self, other): |
| """ |
| Returns True if ``self`` is a subset of ``other``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Interval |
| >>> Interval(0, 0.5).is_subset(Interval(0, 1)) |
| True |
| >>> Interval(0, 1).is_subset(Interval(0, 1, left_open=True)) |
| False |
| |
| """ |
| if not isinstance(other, Set): |
| raise ValueError("Unknown argument '%s'" % other) |
|
|
| |
| if self == other: |
| return True |
| is_empty = self.is_empty |
| if is_empty is True: |
| return True |
| elif fuzzy_not(is_empty) and other.is_empty: |
| return False |
| if self.is_finite_set is False and other.is_finite_set: |
| return False |
|
|
| |
| ret = self._eval_is_subset(other) |
| if ret is not None: |
| return ret |
| ret = other._eval_is_superset(self) |
| if ret is not None: |
| return ret |
|
|
| |
| from sympy.sets.handlers.issubset import is_subset_sets |
| ret = is_subset_sets(self, other) |
| if ret is not None: |
| return ret |
|
|
| |
| |
| |
| |
| if self.intersect(other) == self: |
| return True |
|
|
| def _eval_is_subset(self, other): |
| '''Returns a fuzzy bool for whether self is a subset of other.''' |
| return None |
|
|
| def _eval_is_superset(self, other): |
| '''Returns a fuzzy bool for whether self is a subset of other.''' |
| return None |
|
|
| |
| def issubset(self, other): |
| """ |
| Alias for :meth:`is_subset()` |
| """ |
| return self.is_subset(other) |
|
|
| def is_proper_subset(self, other): |
| """ |
| Returns True if ``self`` is a proper subset of ``other``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Interval |
| >>> Interval(0, 0.5).is_proper_subset(Interval(0, 1)) |
| True |
| >>> Interval(0, 1).is_proper_subset(Interval(0, 1)) |
| False |
| |
| """ |
| if isinstance(other, Set): |
| return self != other and self.is_subset(other) |
| else: |
| raise ValueError("Unknown argument '%s'" % other) |
|
|
| def is_superset(self, other): |
| """ |
| Returns True if ``self`` is a superset of ``other``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Interval |
| >>> Interval(0, 0.5).is_superset(Interval(0, 1)) |
| False |
| >>> Interval(0, 1).is_superset(Interval(0, 1, left_open=True)) |
| True |
| |
| """ |
| if isinstance(other, Set): |
| return other.is_subset(self) |
| else: |
| raise ValueError("Unknown argument '%s'" % other) |
|
|
| |
| def issuperset(self, other): |
| """ |
| Alias for :meth:`is_superset()` |
| """ |
| return self.is_superset(other) |
|
|
| def is_proper_superset(self, other): |
| """ |
| Returns True if ``self`` is a proper superset of ``other``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Interval |
| >>> Interval(0, 1).is_proper_superset(Interval(0, 0.5)) |
| True |
| >>> Interval(0, 1).is_proper_superset(Interval(0, 1)) |
| False |
| |
| """ |
| if isinstance(other, Set): |
| return self != other and self.is_superset(other) |
| else: |
| raise ValueError("Unknown argument '%s'" % other) |
|
|
| def _eval_powerset(self): |
| from .powerset import PowerSet |
| return PowerSet(self) |
|
|
| def powerset(self): |
| """ |
| Find the Power set of ``self``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import EmptySet, FiniteSet, Interval |
| |
| A power set of an empty set: |
| |
| >>> A = EmptySet |
| >>> A.powerset() |
| {EmptySet} |
| |
| A power set of a finite set: |
| |
| >>> A = FiniteSet(1, 2) |
| >>> a, b, c = FiniteSet(1), FiniteSet(2), FiniteSet(1, 2) |
| >>> A.powerset() == FiniteSet(a, b, c, EmptySet) |
| True |
| |
| A power set of an interval: |
| |
| >>> Interval(1, 2).powerset() |
| PowerSet(Interval(1, 2)) |
| |
| References |
| ========== |
| |
| .. [1] https://en.wikipedia.org/wiki/Power_set |
| |
| """ |
| return self._eval_powerset() |
|
|
| @property |
| def measure(self): |
| """ |
| The (Lebesgue) measure of ``self``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Interval, Union |
| >>> Interval(0, 1).measure |
| 1 |
| >>> Union(Interval(0, 1), Interval(2, 3)).measure |
| 2 |
| |
| """ |
| return self._measure |
|
|
| @property |
| def kind(self): |
| """ |
| The kind of a Set |
| |
| Explanation |
| =========== |
| |
| Any :class:`Set` will have kind :class:`SetKind` which is |
| parametrised by the kind of the elements of the set. For example |
| most sets are sets of numbers and will have kind |
| ``SetKind(NumberKind)``. If elements of sets are different in kind than |
| their kind will ``SetKind(UndefinedKind)``. See |
| :class:`sympy.core.kind.Kind` for an explanation of the kind system. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Interval, Matrix, FiniteSet, EmptySet, ProductSet, PowerSet |
| |
| >>> FiniteSet(Matrix([1, 2])).kind |
| SetKind(MatrixKind(NumberKind)) |
| |
| >>> Interval(1, 2).kind |
| SetKind(NumberKind) |
| |
| >>> EmptySet.kind |
| SetKind() |
| |
| A :class:`sympy.sets.powerset.PowerSet` is a set of sets: |
| |
| >>> PowerSet({1, 2, 3}).kind |
| SetKind(SetKind(NumberKind)) |
| |
| A :class:`ProductSet` represents the set of tuples of elements of |
| other sets. Its kind is :class:`sympy.core.containers.TupleKind` |
| parametrised by the kinds of the elements of those sets: |
| |
| >>> p = ProductSet(FiniteSet(1, 2), FiniteSet(3, 4)) |
| >>> list(p) |
| [(1, 3), (2, 3), (1, 4), (2, 4)] |
| >>> p.kind |
| SetKind(TupleKind(NumberKind, NumberKind)) |
| |
| When all elements of the set do not have same kind, the kind |
| will be returned as ``SetKind(UndefinedKind)``: |
| |
| >>> FiniteSet(0, Matrix([1, 2])).kind |
| SetKind(UndefinedKind) |
| |
| The kind of the elements of a set are given by the ``element_kind`` |
| attribute of ``SetKind``: |
| |
| >>> Interval(1, 2).kind.element_kind |
| NumberKind |
| |
| See Also |
| ======== |
| |
| NumberKind |
| sympy.core.kind.UndefinedKind |
| sympy.core.containers.TupleKind |
| MatrixKind |
| sympy.matrices.expressions.sets.MatrixSet |
| sympy.sets.conditionset.ConditionSet |
| Rationals |
| Naturals |
| Integers |
| sympy.sets.fancysets.ImageSet |
| sympy.sets.fancysets.Range |
| sympy.sets.fancysets.ComplexRegion |
| sympy.sets.powerset.PowerSet |
| sympy.sets.sets.ProductSet |
| sympy.sets.sets.Interval |
| sympy.sets.sets.Union |
| sympy.sets.sets.Intersection |
| sympy.sets.sets.Complement |
| sympy.sets.sets.EmptySet |
| sympy.sets.sets.UniversalSet |
| sympy.sets.sets.FiniteSet |
| sympy.sets.sets.SymmetricDifference |
| sympy.sets.sets.DisjointUnion |
| """ |
| return self._kind() |
|
|
| @property |
| def boundary(self): |
| """ |
| The boundary or frontier of a set. |
| |
| Explanation |
| =========== |
| |
| A point x is on the boundary of a set S if |
| |
| 1. x is in the closure of S. |
| I.e. Every neighborhood of x contains a point in S. |
| 2. x is not in the interior of S. |
| I.e. There does not exist an open set centered on x contained |
| entirely within S. |
| |
| There are the points on the outer rim of S. If S is open then these |
| points need not actually be contained within S. |
| |
| For example, the boundary of an interval is its start and end points. |
| This is true regardless of whether or not the interval is open. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Interval |
| >>> Interval(0, 1).boundary |
| {0, 1} |
| >>> Interval(0, 1, True, False).boundary |
| {0, 1} |
| """ |
| return self._boundary |
|
|
| @property |
| def is_open(self): |
| """ |
| Property method to check whether a set is open. |
| |
| Explanation |
| =========== |
| |
| A set is open if and only if it has an empty intersection with its |
| boundary. In particular, a subset A of the reals is open if and only |
| if each one of its points is contained in an open interval that is a |
| subset of A. |
| |
| Examples |
| ======== |
| >>> from sympy import S |
| >>> S.Reals.is_open |
| True |
| >>> S.Rationals.is_open |
| False |
| """ |
| return Intersection(self, self.boundary).is_empty |
|
|
| @property |
| def is_closed(self): |
| """ |
| A property method to check whether a set is closed. |
| |
| Explanation |
| =========== |
| |
| A set is closed if its complement is an open set. The closedness of a |
| subset of the reals is determined with respect to R and its standard |
| topology. |
| |
| Examples |
| ======== |
| >>> from sympy import Interval |
| >>> Interval(0, 1).is_closed |
| True |
| """ |
| return self.boundary.is_subset(self) |
|
|
| @property |
| def closure(self): |
| """ |
| Property method which returns the closure of a set. |
| The closure is defined as the union of the set itself and its |
| boundary. |
| |
| Examples |
| ======== |
| >>> from sympy import S, Interval |
| >>> S.Reals.closure |
| Reals |
| >>> Interval(0, 1).closure |
| Interval(0, 1) |
| """ |
| return self + self.boundary |
|
|
| @property |
| def interior(self): |
| """ |
| Property method which returns the interior of a set. |
| The interior of a set S consists all points of S that do not |
| belong to the boundary of S. |
| |
| Examples |
| ======== |
| >>> from sympy import Interval |
| >>> Interval(0, 1).interior |
| Interval.open(0, 1) |
| >>> Interval(0, 1).boundary.interior |
| EmptySet |
| """ |
| return self - self.boundary |
|
|
| @property |
| def _boundary(self): |
| raise NotImplementedError() |
|
|
| @property |
| def _measure(self): |
| raise NotImplementedError("(%s)._measure" % self) |
|
|
| def _kind(self): |
| return SetKind(UndefinedKind) |
|
|
| def _eval_evalf(self, prec): |
| dps = prec_to_dps(prec) |
| return self.func(*[arg.evalf(n=dps) for arg in self.args]) |
|
|
| @sympify_return([('other', 'Set')], NotImplemented) |
| def __add__(self, other): |
| return self.union(other) |
|
|
| @sympify_return([('other', 'Set')], NotImplemented) |
| def __or__(self, other): |
| return self.union(other) |
|
|
| @sympify_return([('other', 'Set')], NotImplemented) |
| def __and__(self, other): |
| return self.intersect(other) |
|
|
| @sympify_return([('other', 'Set')], NotImplemented) |
| def __mul__(self, other): |
| return ProductSet(self, other) |
|
|
| @sympify_return([('other', 'Set')], NotImplemented) |
| def __xor__(self, other): |
| return SymmetricDifference(self, other) |
|
|
| @sympify_return([('exp', Expr)], NotImplemented) |
| def __pow__(self, exp): |
| if not (exp.is_Integer and exp >= 0): |
| raise ValueError("%s: Exponent must be a positive Integer" % exp) |
| return ProductSet(*[self]*exp) |
|
|
| @sympify_return([('other', 'Set')], NotImplemented) |
| def __sub__(self, other): |
| return Complement(self, other) |
|
|
| def __contains__(self, other): |
| other = _sympify(other) |
| c = self._contains(other) |
| b = tfn[c] |
| if b is None: |
| |
| |
| raise TypeError('did not evaluate to a bool: %r' % c) |
| return b |
|
|
|
|
| class ProductSet(Set): |
| """ |
| Represents a Cartesian Product of Sets. |
| |
| Explanation |
| =========== |
| |
| Returns a Cartesian product given several sets as either an iterable |
| or individual arguments. |
| |
| Can use ``*`` operator on any sets for convenient shorthand. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Interval, FiniteSet, ProductSet |
| >>> I = Interval(0, 5); S = FiniteSet(1, 2, 3) |
| >>> ProductSet(I, S) |
| ProductSet(Interval(0, 5), {1, 2, 3}) |
| |
| >>> (2, 2) in ProductSet(I, S) |
| True |
| |
| >>> Interval(0, 1) * Interval(0, 1) # The unit square |
| ProductSet(Interval(0, 1), Interval(0, 1)) |
| |
| >>> coin = FiniteSet('H', 'T') |
| >>> set(coin**2) |
| {(H, H), (H, T), (T, H), (T, T)} |
| |
| The Cartesian product is not commutative or associative e.g.: |
| |
| >>> I*S == S*I |
| False |
| >>> (I*I)*I == I*(I*I) |
| False |
| |
| Notes |
| ===== |
| |
| - Passes most operations down to the argument sets |
| |
| References |
| ========== |
| |
| .. [1] https://en.wikipedia.org/wiki/Cartesian_product |
| """ |
| is_ProductSet = True |
|
|
| def __new__(cls, *sets, **assumptions): |
| if len(sets) == 1 and iterable(sets[0]) and not isinstance(sets[0], (Set, set)): |
| sympy_deprecation_warning( |
| """ |
| ProductSet(iterable) is deprecated. Use ProductSet(*iterable) instead. |
| """, |
| deprecated_since_version="1.5", |
| active_deprecations_target="deprecated-productset-iterable", |
| ) |
| sets = tuple(sets[0]) |
|
|
| sets = [sympify(s) for s in sets] |
|
|
| if not all(isinstance(s, Set) for s in sets): |
| raise TypeError("Arguments to ProductSet should be of type Set") |
|
|
| |
| if len(sets) == 0: |
| return FiniteSet(()) |
|
|
| if S.EmptySet in sets: |
| return S.EmptySet |
|
|
| return Basic.__new__(cls, *sets, **assumptions) |
|
|
| @property |
| def sets(self): |
| return self.args |
|
|
| def flatten(self): |
| def _flatten(sets): |
| for s in sets: |
| if s.is_ProductSet: |
| yield from _flatten(s.sets) |
| else: |
| yield s |
| return ProductSet(*_flatten(self.sets)) |
|
|
|
|
|
|
| def _contains(self, element): |
| """ |
| ``in`` operator for ProductSets. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Interval |
| >>> (2, 3) in Interval(0, 5) * Interval(0, 5) |
| True |
| |
| >>> (10, 10) in Interval(0, 5) * Interval(0, 5) |
| False |
| |
| Passes operation on to constituent sets |
| """ |
| if element.is_Symbol: |
| return None |
|
|
| if not isinstance(element, Tuple) or len(element) != len(self.sets): |
| return S.false |
|
|
| return And(*[s.contains(e) for s, e in zip(self.sets, element)]) |
|
|
| def as_relational(self, *symbols): |
| symbols = [_sympify(s) for s in symbols] |
| if len(symbols) != len(self.sets) or not all( |
| i.is_Symbol for i in symbols): |
| raise ValueError( |
| 'number of symbols must match the number of sets') |
| return And(*[s.as_relational(i) for s, i in zip(self.sets, symbols)]) |
|
|
| @property |
| def _boundary(self): |
| return Union(*(ProductSet(*(b + b.boundary if i != j else b.boundary |
| for j, b in enumerate(self.sets))) |
| for i, a in enumerate(self.sets))) |
|
|
| @property |
| def is_iterable(self): |
| """ |
| A property method which tests whether a set is iterable or not. |
| Returns True if set is iterable, otherwise returns False. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import FiniteSet, Interval |
| >>> I = Interval(0, 1) |
| >>> A = FiniteSet(1, 2, 3, 4, 5) |
| >>> I.is_iterable |
| False |
| >>> A.is_iterable |
| True |
| |
| """ |
| return all(set.is_iterable for set in self.sets) |
|
|
| def __iter__(self): |
| """ |
| A method which implements is_iterable property method. |
| If self.is_iterable returns True (both constituent sets are iterable), |
| then return the Cartesian Product. Otherwise, raise TypeError. |
| """ |
| return iproduct(*self.sets) |
|
|
| @property |
| def is_empty(self): |
| return fuzzy_or(s.is_empty for s in self.sets) |
|
|
| @property |
| def is_finite_set(self): |
| all_finite = fuzzy_and(s.is_finite_set for s in self.sets) |
| return fuzzy_or([self.is_empty, all_finite]) |
|
|
| @property |
| def _measure(self): |
| measure = 1 |
| for s in self.sets: |
| measure *= s.measure |
| return measure |
|
|
| def _kind(self): |
| return SetKind(TupleKind(*(i.kind.element_kind for i in self.args))) |
|
|
| def __len__(self): |
| return reduce(lambda a, b: a*b, (len(s) for s in self.args)) |
|
|
| def __bool__(self): |
| return all(self.sets) |
|
|
|
|
| class Interval(Set): |
| """ |
| Represents a real interval as a Set. |
| |
| Usage: |
| Returns an interval with end points ``start`` and ``end``. |
| |
| For ``left_open=True`` (default ``left_open`` is ``False``) the interval |
| will be open on the left. Similarly, for ``right_open=True`` the interval |
| will be open on the right. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Symbol, Interval |
| >>> Interval(0, 1) |
| Interval(0, 1) |
| >>> Interval.Ropen(0, 1) |
| Interval.Ropen(0, 1) |
| >>> Interval.Ropen(0, 1) |
| Interval.Ropen(0, 1) |
| >>> Interval.Lopen(0, 1) |
| Interval.Lopen(0, 1) |
| >>> Interval.open(0, 1) |
| Interval.open(0, 1) |
| |
| >>> a = Symbol('a', real=True) |
| >>> Interval(0, a) |
| Interval(0, a) |
| |
| Notes |
| ===== |
| - Only real end points are supported |
| - ``Interval(a, b)`` with $a > b$ will return the empty set |
| - Use the ``evalf()`` method to turn an Interval into an mpmath |
| ``mpi`` interval instance |
| |
| References |
| ========== |
| |
| .. [1] https://en.wikipedia.org/wiki/Interval_%28mathematics%29 |
| """ |
| is_Interval = True |
|
|
| def __new__(cls, start, end, left_open=False, right_open=False): |
|
|
| start = _sympify(start) |
| end = _sympify(end) |
| left_open = _sympify(left_open) |
| right_open = _sympify(right_open) |
|
|
| if not all(isinstance(a, (type(true), type(false))) |
| for a in [left_open, right_open]): |
| raise NotImplementedError( |
| "left_open and right_open can have only true/false values, " |
| "got %s and %s" % (left_open, right_open)) |
|
|
| |
| if fuzzy_not(fuzzy_and(i.is_extended_real for i in (start, end, end-start))): |
| raise ValueError("Non-real intervals are not supported") |
|
|
| |
| if is_lt(end, start): |
| return S.EmptySet |
| elif (end - start).is_negative: |
| return S.EmptySet |
|
|
| if end == start and (left_open or right_open): |
| return S.EmptySet |
| if end == start and not (left_open or right_open): |
| if start is S.Infinity or start is S.NegativeInfinity: |
| return S.EmptySet |
| return FiniteSet(end) |
|
|
| |
| if start is S.NegativeInfinity: |
| left_open = true |
| if end is S.Infinity: |
| right_open = true |
| if start == S.Infinity or end == S.NegativeInfinity: |
| return S.EmptySet |
|
|
| return Basic.__new__(cls, start, end, left_open, right_open) |
|
|
| @property |
| def start(self): |
| """ |
| The left end point of the interval. |
| |
| This property takes the same value as the ``inf`` property. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Interval |
| >>> Interval(0, 1).start |
| 0 |
| |
| """ |
| return self._args[0] |
|
|
| @property |
| def end(self): |
| """ |
| The right end point of the interval. |
| |
| This property takes the same value as the ``sup`` property. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Interval |
| >>> Interval(0, 1).end |
| 1 |
| |
| """ |
| return self._args[1] |
|
|
| @property |
| def left_open(self): |
| """ |
| True if interval is left-open. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Interval |
| >>> Interval(0, 1, left_open=True).left_open |
| True |
| >>> Interval(0, 1, left_open=False).left_open |
| False |
| |
| """ |
| return self._args[2] |
|
|
| @property |
| def right_open(self): |
| """ |
| True if interval is right-open. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Interval |
| >>> Interval(0, 1, right_open=True).right_open |
| True |
| >>> Interval(0, 1, right_open=False).right_open |
| False |
| |
| """ |
| return self._args[3] |
|
|
| @classmethod |
| def open(cls, a, b): |
| """Return an interval including neither boundary.""" |
| return cls(a, b, True, True) |
|
|
| @classmethod |
| def Lopen(cls, a, b): |
| """Return an interval not including the left boundary.""" |
| return cls(a, b, True, False) |
|
|
| @classmethod |
| def Ropen(cls, a, b): |
| """Return an interval not including the right boundary.""" |
| return cls(a, b, False, True) |
|
|
| @property |
| def _inf(self): |
| return self.start |
|
|
| @property |
| def _sup(self): |
| return self.end |
|
|
| @property |
| def left(self): |
| return self.start |
|
|
| @property |
| def right(self): |
| return self.end |
|
|
| @property |
| def is_empty(self): |
| if self.left_open or self.right_open: |
| cond = self.start >= self.end |
| else: |
| cond = self.start > self.end |
| return fuzzy_bool(cond) |
|
|
| @property |
| def is_finite_set(self): |
| return self.measure.is_zero |
|
|
| def _complement(self, other): |
| if other == S.Reals: |
| a = Interval(S.NegativeInfinity, self.start, |
| True, not self.left_open) |
| b = Interval(self.end, S.Infinity, not self.right_open, True) |
| return Union(a, b) |
|
|
| if isinstance(other, FiniteSet): |
| nums = [m for m in other.args if m.is_number] |
| if nums == []: |
| return None |
|
|
| return Set._complement(self, other) |
|
|
| @property |
| def _boundary(self): |
| finite_points = [p for p in (self.start, self.end) |
| if abs(p) != S.Infinity] |
| return FiniteSet(*finite_points) |
|
|
| def _contains(self, other): |
| if (not isinstance(other, Expr) or other is S.NaN |
| or other.is_real is False or other.has(S.ComplexInfinity)): |
| |
| |
| return false |
|
|
| if self.start is S.NegativeInfinity and self.end is S.Infinity: |
| if other.is_real is not None: |
| return tfn[other.is_real] |
|
|
| d = Dummy() |
| return self.as_relational(d).subs(d, other) |
|
|
| def as_relational(self, x): |
| """Rewrite an interval in terms of inequalities and logic operators.""" |
| x = sympify(x) |
| if self.right_open: |
| right = x < self.end |
| else: |
| right = x <= self.end |
| if self.left_open: |
| left = self.start < x |
| else: |
| left = self.start <= x |
| return And(left, right) |
|
|
| @property |
| def _measure(self): |
| return self.end - self.start |
|
|
| def _kind(self): |
| return SetKind(NumberKind) |
|
|
| def to_mpi(self, prec=53): |
| return mpi(mpf(self.start._eval_evalf(prec)), |
| mpf(self.end._eval_evalf(prec))) |
|
|
| def _eval_evalf(self, prec): |
| return Interval(self.left._evalf(prec), self.right._evalf(prec), |
| left_open=self.left_open, right_open=self.right_open) |
|
|
| def _is_comparable(self, other): |
| is_comparable = self.start.is_comparable |
| is_comparable &= self.end.is_comparable |
| is_comparable &= other.start.is_comparable |
| is_comparable &= other.end.is_comparable |
|
|
| return is_comparable |
|
|
| @property |
| def is_left_unbounded(self): |
| """Return ``True`` if the left endpoint is negative infinity. """ |
| return self.left is S.NegativeInfinity or self.left == Float("-inf") |
|
|
| @property |
| def is_right_unbounded(self): |
| """Return ``True`` if the right endpoint is positive infinity. """ |
| return self.right is S.Infinity or self.right == Float("+inf") |
|
|
| def _eval_Eq(self, other): |
| if not isinstance(other, Interval): |
| if isinstance(other, FiniteSet): |
| return false |
| elif isinstance(other, Set): |
| return None |
| return false |
|
|
|
|
| class Union(Set, LatticeOp): |
| """ |
| Represents a union of sets as a :class:`Set`. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Union, Interval |
| >>> Union(Interval(1, 2), Interval(3, 4)) |
| Union(Interval(1, 2), Interval(3, 4)) |
| |
| The Union constructor will always try to merge overlapping intervals, |
| if possible. For example: |
| |
| >>> Union(Interval(1, 2), Interval(2, 3)) |
| Interval(1, 3) |
| |
| See Also |
| ======== |
| |
| Intersection |
| |
| References |
| ========== |
| |
| .. [1] https://en.wikipedia.org/wiki/Union_%28set_theory%29 |
| """ |
| is_Union = True |
|
|
| @property |
| def identity(self): |
| return S.EmptySet |
|
|
| @property |
| def zero(self): |
| return S.UniversalSet |
|
|
| def __new__(cls, *args, **kwargs): |
| evaluate = kwargs.get('evaluate', global_parameters.evaluate) |
|
|
| |
| args = _sympify(args) |
|
|
| |
| if evaluate: |
| args = list(cls._new_args_filter(args)) |
| return simplify_union(args) |
|
|
| args = list(ordered(args, Set._infimum_key)) |
|
|
| obj = Basic.__new__(cls, *args) |
| obj._argset = frozenset(args) |
| return obj |
|
|
| @property |
| def args(self): |
| return self._args |
|
|
| def _complement(self, universe): |
| |
| return Intersection(s.complement(universe) for s in self.args) |
|
|
| @property |
| def _inf(self): |
| |
| |
| return Min(*[set.inf for set in self.args]) |
|
|
| @property |
| def _sup(self): |
| |
| |
| return Max(*[set.sup for set in self.args]) |
|
|
| @property |
| def is_empty(self): |
| return fuzzy_and(set.is_empty for set in self.args) |
|
|
| @property |
| def is_finite_set(self): |
| return fuzzy_and(set.is_finite_set for set in self.args) |
|
|
| @property |
| def _measure(self): |
| |
| |
| |
|
|
| |
| |
| |
| |
|
|
| |
| |
| sets = [(FiniteSet(s), s) for s in self.args] |
| measure = 0 |
| parity = 1 |
| while sets: |
| |
| measure += parity * sum(inter.measure for sos, inter in sets) |
|
|
| |
| |
| sets = ((sos + FiniteSet(newset), newset.intersect(intersection)) |
| for sos, intersection in sets for newset in self.args |
| if newset not in sos) |
|
|
| |
| sets = [(sos, inter) for sos, inter in sets if inter.measure != 0] |
|
|
| |
| sos_list = [] |
| sets_list = [] |
| for _set in sets: |
| if _set[0] in sos_list: |
| continue |
| else: |
| sos_list.append(_set[0]) |
| sets_list.append(_set) |
| sets = sets_list |
|
|
| |
| parity *= -1 |
| return measure |
|
|
| def _kind(self): |
| kinds = tuple(arg.kind for arg in self.args if arg is not S.EmptySet) |
| if not kinds: |
| return SetKind() |
| elif all(i == kinds[0] for i in kinds): |
| return kinds[0] |
| else: |
| return SetKind(UndefinedKind) |
|
|
| @property |
| def _boundary(self): |
| def boundary_of_set(i): |
| """ The boundary of set i minus interior of all other sets """ |
| b = self.args[i].boundary |
| for j, a in enumerate(self.args): |
| if j != i: |
| b = b - a.interior |
| return b |
| return Union(*map(boundary_of_set, range(len(self.args)))) |
|
|
| def _contains(self, other): |
| return Or(*[s.contains(other) for s in self.args]) |
|
|
| def is_subset(self, other): |
| return fuzzy_and(s.is_subset(other) for s in self.args) |
|
|
| def as_relational(self, symbol): |
| """Rewrite a Union in terms of equalities and logic operators. """ |
| if (len(self.args) == 2 and |
| all(isinstance(i, Interval) for i in self.args)): |
| |
| |
| |
| |
| |
| a, b = self.args |
| if a.sup == b.inf and a.right_open and b.left_open: |
| mincond = symbol > a.inf if a.left_open else symbol >= a.inf |
| maxcond = symbol < b.sup if b.right_open else symbol <= b.sup |
| necond = Ne(symbol, a.sup) |
| return And(necond, mincond, maxcond) |
| return Or(*[i.as_relational(symbol) for i in self.args]) |
|
|
| @property |
| def is_iterable(self): |
| return all(arg.is_iterable for arg in self.args) |
|
|
| def __iter__(self): |
| return roundrobin(*(iter(arg) for arg in self.args)) |
|
|
|
|
| class Intersection(Set, LatticeOp): |
| """ |
| Represents an intersection of sets as a :class:`Set`. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Intersection, Interval |
| >>> Intersection(Interval(1, 3), Interval(2, 4)) |
| Interval(2, 3) |
| |
| We often use the .intersect method |
| |
| >>> Interval(1,3).intersect(Interval(2,4)) |
| Interval(2, 3) |
| |
| See Also |
| ======== |
| |
| Union |
| |
| References |
| ========== |
| |
| .. [1] https://en.wikipedia.org/wiki/Intersection_%28set_theory%29 |
| """ |
| is_Intersection = True |
|
|
| @property |
| def identity(self): |
| return S.UniversalSet |
|
|
| @property |
| def zero(self): |
| return S.EmptySet |
|
|
| def __new__(cls, *args , evaluate=None): |
| if evaluate is None: |
| evaluate = global_parameters.evaluate |
|
|
| |
| args = list(ordered(set(_sympify(args)))) |
|
|
| |
| if evaluate: |
| args = list(cls._new_args_filter(args)) |
| return simplify_intersection(args) |
|
|
| args = list(ordered(args, Set._infimum_key)) |
|
|
| obj = Basic.__new__(cls, *args) |
| obj._argset = frozenset(args) |
| return obj |
|
|
| @property |
| def args(self): |
| return self._args |
|
|
| @property |
| def is_iterable(self): |
| return any(arg.is_iterable for arg in self.args) |
|
|
| @property |
| def is_finite_set(self): |
| if fuzzy_or(arg.is_finite_set for arg in self.args): |
| return True |
|
|
| def _kind(self): |
| kinds = tuple(arg.kind for arg in self.args if arg is not S.UniversalSet) |
| if not kinds: |
| return SetKind(UndefinedKind) |
| elif all(i == kinds[0] for i in kinds): |
| return kinds[0] |
| else: |
| return SetKind() |
|
|
| @property |
| def _inf(self): |
| raise NotImplementedError() |
|
|
| @property |
| def _sup(self): |
| raise NotImplementedError() |
|
|
| def _contains(self, other): |
| return And(*[set.contains(other) for set in self.args]) |
|
|
| def __iter__(self): |
| sets_sift = sift(self.args, lambda x: x.is_iterable) |
|
|
| completed = False |
| candidates = sets_sift[True] + sets_sift[None] |
|
|
| finite_candidates, others = [], [] |
| for candidate in candidates: |
| length = None |
| try: |
| length = len(candidate) |
| except TypeError: |
| others.append(candidate) |
|
|
| if length is not None: |
| finite_candidates.append(candidate) |
| finite_candidates.sort(key=len) |
|
|
| for s in finite_candidates + others: |
| other_sets = set(self.args) - {s} |
| other = Intersection(*other_sets, evaluate=False) |
| completed = True |
| for x in s: |
| try: |
| if x in other: |
| yield x |
| except TypeError: |
| completed = False |
| if completed: |
| return |
|
|
| if not completed: |
| if not candidates: |
| raise TypeError("None of the constituent sets are iterable") |
| raise TypeError( |
| "The computation had not completed because of the " |
| "undecidable set membership is found in every candidates.") |
|
|
| @staticmethod |
| def _handle_finite_sets(args): |
| '''Simplify intersection of one or more FiniteSets and other sets''' |
|
|
| |
| fs_args, others = sift(args, lambda x: x.is_FiniteSet, binary=True) |
|
|
| |
| if not fs_args: |
| return |
|
|
| |
| fs_sets = [set(fs) for fs in fs_args] |
| all_elements = reduce(lambda a, b: a | b, fs_sets, set()) |
|
|
| |
| |
| definite = set() |
| for e in all_elements: |
| inall = fuzzy_and(s.contains(e) for s in args) |
| if inall is True: |
| definite.add(e) |
| if inall is not None: |
| for s in fs_sets: |
| s.discard(e) |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| fs_elements = reduce(lambda a, b: a | b, fs_sets, set()) |
|
|
| |
| fs_symsets = [FiniteSet(*s) for s in fs_sets] |
|
|
| while fs_elements: |
| for e in fs_elements: |
| infs = fuzzy_and(s.contains(e) for s in fs_symsets) |
| if infs is True: |
| definite.add(e) |
| if infs is not None: |
| for n, s in enumerate(fs_sets): |
| |
| if e in s: |
| s.remove(e) |
| fs_symsets[n] = FiniteSet(*s) |
| fs_elements.remove(e) |
| break |
| |
| |
| else: |
| break |
|
|
| |
| |
| if not all(fs_sets): |
| fs_sets = [set()] |
|
|
| |
| |
| |
| |
| if definite: |
| fs_sets = [fs | definite for fs in fs_sets] |
|
|
| if fs_sets == [set()]: |
| return S.EmptySet |
|
|
| sets = [FiniteSet(*s) for s in fs_sets] |
|
|
| |
| |
| all_elements = reduce(lambda a, b: a | b, fs_sets, set()) |
| is_redundant = lambda o: all(fuzzy_bool(o.contains(e)) for e in all_elements) |
| others = [o for o in others if not is_redundant(o)] |
|
|
| if others: |
| rest = Intersection(*others) |
| |
| |
| |
| if rest is S.EmptySet: |
| return S.EmptySet |
| |
| if rest.is_Intersection: |
| sets.extend(rest.args) |
| else: |
| sets.append(rest) |
|
|
| if len(sets) == 1: |
| return sets[0] |
| else: |
| return Intersection(*sets, evaluate=False) |
|
|
| def as_relational(self, symbol): |
| """Rewrite an Intersection in terms of equalities and logic operators""" |
| return And(*[set.as_relational(symbol) for set in self.args]) |
|
|
|
|
| class Complement(Set): |
| r"""Represents the set difference or relative complement of a set with |
| another set. |
| |
| $$A - B = \{x \in A \mid x \notin B\}$$ |
| |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Complement, FiniteSet |
| >>> Complement(FiniteSet(0, 1, 2), FiniteSet(1)) |
| {0, 2} |
| |
| See Also |
| ========= |
| |
| Intersection, Union |
| |
| References |
| ========== |
| |
| .. [1] https://mathworld.wolfram.com/ComplementSet.html |
| """ |
|
|
| is_Complement = True |
|
|
| def __new__(cls, a, b, evaluate=True): |
| a, b = map(_sympify, (a, b)) |
| if evaluate: |
| return Complement.reduce(a, b) |
|
|
| return Basic.__new__(cls, a, b) |
|
|
| @staticmethod |
| def reduce(A, B): |
| """ |
| Simplify a :class:`Complement`. |
| |
| """ |
| if B == S.UniversalSet or A.is_subset(B): |
| return S.EmptySet |
|
|
| if isinstance(B, Union): |
| return Intersection(*(s.complement(A) for s in B.args)) |
|
|
| result = B._complement(A) |
| if result is not None: |
| return result |
| else: |
| return Complement(A, B, evaluate=False) |
|
|
| def _contains(self, other): |
| A = self.args[0] |
| B = self.args[1] |
| return And(A.contains(other), Not(B.contains(other))) |
|
|
| def as_relational(self, symbol): |
| """Rewrite a complement in terms of equalities and logic |
| operators""" |
| A, B = self.args |
|
|
| A_rel = A.as_relational(symbol) |
| B_rel = Not(B.as_relational(symbol)) |
|
|
| return And(A_rel, B_rel) |
|
|
| def _kind(self): |
| return self.args[0].kind |
|
|
| @property |
| def is_iterable(self): |
| if self.args[0].is_iterable: |
| return True |
|
|
| @property |
| def is_finite_set(self): |
| A, B = self.args |
| a_finite = A.is_finite_set |
| if a_finite is True: |
| return True |
| elif a_finite is False and B.is_finite_set: |
| return False |
|
|
| def __iter__(self): |
| A, B = self.args |
| for a in A: |
| if a not in B: |
| yield a |
| else: |
| continue |
|
|
|
|
| class EmptySet(Set, metaclass=Singleton): |
| """ |
| Represents the empty set. The empty set is available as a singleton |
| as ``S.EmptySet``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import S, Interval |
| >>> S.EmptySet |
| EmptySet |
| |
| >>> Interval(1, 2).intersect(S.EmptySet) |
| EmptySet |
| |
| See Also |
| ======== |
| |
| UniversalSet |
| |
| References |
| ========== |
| |
| .. [1] https://en.wikipedia.org/wiki/Empty_set |
| """ |
| is_empty = True |
| is_finite_set = True |
| is_FiniteSet = True |
|
|
| @property |
| @deprecated( |
| """ |
| The is_EmptySet attribute of Set objects is deprecated. |
| Use 's is S.EmptySet" or 's.is_empty' instead. |
| """, |
| deprecated_since_version="1.5", |
| active_deprecations_target="deprecated-is-emptyset", |
| ) |
| def is_EmptySet(self): |
| return True |
|
|
| @property |
| def _measure(self): |
| return 0 |
|
|
| def _contains(self, other): |
| return false |
|
|
| def as_relational(self, symbol): |
| return false |
|
|
| def __len__(self): |
| return 0 |
|
|
| def __iter__(self): |
| return iter([]) |
|
|
| def _eval_powerset(self): |
| return FiniteSet(self) |
|
|
| @property |
| def _boundary(self): |
| return self |
|
|
| def _complement(self, other): |
| return other |
|
|
| def _kind(self): |
| return SetKind() |
|
|
| def _symmetric_difference(self, other): |
| return other |
|
|
|
|
| class UniversalSet(Set, metaclass=Singleton): |
| """ |
| Represents the set of all things. |
| The universal set is available as a singleton as ``S.UniversalSet``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import S, Interval |
| >>> S.UniversalSet |
| UniversalSet |
| |
| >>> Interval(1, 2).intersect(S.UniversalSet) |
| Interval(1, 2) |
| |
| See Also |
| ======== |
| |
| EmptySet |
| |
| References |
| ========== |
| |
| .. [1] https://en.wikipedia.org/wiki/Universal_set |
| """ |
|
|
| is_UniversalSet = True |
| is_empty = False |
| is_finite_set = False |
|
|
| def _complement(self, other): |
| return S.EmptySet |
|
|
| def _symmetric_difference(self, other): |
| return other |
|
|
| @property |
| def _measure(self): |
| return S.Infinity |
|
|
| def _kind(self): |
| return SetKind(UndefinedKind) |
|
|
| def _contains(self, other): |
| return true |
|
|
| def as_relational(self, symbol): |
| return true |
|
|
| @property |
| def _boundary(self): |
| return S.EmptySet |
|
|
|
|
| class FiniteSet(Set): |
| """ |
| Represents a finite set of Sympy expressions. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import FiniteSet, Symbol, Interval, Naturals0 |
| >>> FiniteSet(1, 2, 3, 4) |
| {1, 2, 3, 4} |
| >>> 3 in FiniteSet(1, 2, 3, 4) |
| True |
| >>> FiniteSet(1, (1, 2), Symbol('x')) |
| {1, x, (1, 2)} |
| >>> FiniteSet(Interval(1, 2), Naturals0, {1, 2}) |
| FiniteSet({1, 2}, Interval(1, 2), Naturals0) |
| >>> members = [1, 2, 3, 4] |
| >>> f = FiniteSet(*members) |
| >>> f |
| {1, 2, 3, 4} |
| >>> f - FiniteSet(2) |
| {1, 3, 4} |
| >>> f + FiniteSet(2, 5) |
| {1, 2, 3, 4, 5} |
| |
| References |
| ========== |
| |
| .. [1] https://en.wikipedia.org/wiki/Finite_set |
| """ |
| is_FiniteSet = True |
| is_iterable = True |
| is_empty = False |
| is_finite_set = True |
|
|
| def __new__(cls, *args, **kwargs): |
| evaluate = kwargs.get('evaluate', global_parameters.evaluate) |
| if evaluate: |
| args = list(map(sympify, args)) |
|
|
| if len(args) == 0: |
| return S.EmptySet |
| else: |
| args = list(map(sympify, args)) |
|
|
| |
| dargs = {} |
| for i in reversed(list(ordered(args))): |
| if i.is_Symbol: |
| dargs[i] = i |
| else: |
| try: |
| dargs[i.as_dummy()] = i |
| except TypeError: |
| |
| dargs[i] = i |
| _args_set = set(dargs.values()) |
| args = list(ordered(_args_set, Set._infimum_key)) |
| obj = Basic.__new__(cls, *args) |
| obj._args_set = _args_set |
| return obj |
|
|
|
|
| def __iter__(self): |
| return iter(self.args) |
|
|
| def _complement(self, other): |
| if isinstance(other, Interval): |
| |
| |
| |
| nums, syms = [], [] |
| for m in self.args: |
| if m.is_number and m.is_real: |
| nums.append(m) |
| elif m.is_real == False: |
| pass |
| else: |
| syms.append(m) |
| if other == S.Reals and nums != []: |
| nums.sort() |
| intervals = [] |
| intervals += [Interval(S.NegativeInfinity, nums[0], True, True)] |
| for a, b in zip(nums[:-1], nums[1:]): |
| intervals.append(Interval(a, b, True, True)) |
| intervals.append(Interval(nums[-1], S.Infinity, True, True)) |
| if syms != []: |
| return Complement(Union(*intervals, evaluate=False), |
| FiniteSet(*syms), evaluate=False) |
| else: |
| return Union(*intervals, evaluate=False) |
| elif nums == []: |
| if syms: |
| return Complement(other, FiniteSet(*syms), evaluate=False) |
| else: |
| return other |
|
|
| elif isinstance(other, FiniteSet): |
| unk = [] |
| for i in self: |
| c = sympify(other.contains(i)) |
| if c is not S.true and c is not S.false: |
| unk.append(i) |
| unk = FiniteSet(*unk) |
| if unk == self: |
| return |
| not_true = [] |
| for i in other: |
| c = sympify(self.contains(i)) |
| if c is not S.true: |
| not_true.append(i) |
| return Complement(FiniteSet(*not_true), unk) |
|
|
| return Set._complement(self, other) |
|
|
| def _contains(self, other): |
| """ |
| Tests whether an element, other, is in the set. |
| |
| Explanation |
| =========== |
| |
| The actual test is for mathematical equality (as opposed to |
| syntactical equality). In the worst case all elements of the |
| set must be checked. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import FiniteSet |
| >>> 1 in FiniteSet(1, 2) |
| True |
| >>> 5 in FiniteSet(1, 2) |
| False |
| |
| """ |
| if other in self._args_set: |
| return S.true |
| else: |
| |
| |
| return Or(*[Eq(e, other, evaluate=True) for e in self.args]) |
|
|
| def _eval_is_subset(self, other): |
| return fuzzy_and(other._contains(e) for e in self.args) |
|
|
| @property |
| def _boundary(self): |
| return self |
|
|
| @property |
| def _inf(self): |
| return Min(*self) |
|
|
| @property |
| def _sup(self): |
| return Max(*self) |
|
|
| @property |
| def measure(self): |
| return 0 |
|
|
| def _kind(self): |
| if not self.args: |
| return SetKind() |
| elif all(i.kind == self.args[0].kind for i in self.args): |
| return SetKind(self.args[0].kind) |
| else: |
| return SetKind(UndefinedKind) |
|
|
| def __len__(self): |
| return len(self.args) |
|
|
| def as_relational(self, symbol): |
| """Rewrite a FiniteSet in terms of equalities and logic operators. """ |
| return Or(*[Eq(symbol, elem) for elem in self]) |
|
|
| def compare(self, other): |
| return (hash(self) - hash(other)) |
|
|
| def _eval_evalf(self, prec): |
| dps = prec_to_dps(prec) |
| return FiniteSet(*[elem.evalf(n=dps) for elem in self]) |
|
|
| def _eval_simplify(self, **kwargs): |
| from sympy.simplify import simplify |
| return FiniteSet(*[simplify(elem, **kwargs) for elem in self]) |
|
|
| @property |
| def _sorted_args(self): |
| return self.args |
|
|
| def _eval_powerset(self): |
| return self.func(*[self.func(*s) for s in subsets(self.args)]) |
|
|
| def _eval_rewrite_as_PowerSet(self, *args, **kwargs): |
| """Rewriting method for a finite set to a power set.""" |
| from .powerset import PowerSet |
|
|
| is2pow = lambda n: bool(n and not n & (n - 1)) |
| if not is2pow(len(self)): |
| return None |
|
|
| fs_test = lambda arg: isinstance(arg, Set) and arg.is_FiniteSet |
| if not all(fs_test(arg) for arg in args): |
| return None |
|
|
| biggest = max(args, key=len) |
| for arg in subsets(biggest.args): |
| arg_set = FiniteSet(*arg) |
| if arg_set not in args: |
| return None |
| return PowerSet(biggest) |
|
|
| def __ge__(self, other): |
| if not isinstance(other, Set): |
| raise TypeError("Invalid comparison of set with %s" % func_name(other)) |
| return other.is_subset(self) |
|
|
| def __gt__(self, other): |
| if not isinstance(other, Set): |
| raise TypeError("Invalid comparison of set with %s" % func_name(other)) |
| return self.is_proper_superset(other) |
|
|
| def __le__(self, other): |
| if not isinstance(other, Set): |
| raise TypeError("Invalid comparison of set with %s" % func_name(other)) |
| return self.is_subset(other) |
|
|
| def __lt__(self, other): |
| if not isinstance(other, Set): |
| raise TypeError("Invalid comparison of set with %s" % func_name(other)) |
| return self.is_proper_subset(other) |
|
|
| def __eq__(self, other): |
| if isinstance(other, (set, frozenset)): |
| return self._args_set == other |
| return super().__eq__(other) |
|
|
| __hash__ : Callable[[Basic], Any] = Basic.__hash__ |
|
|
| _sympy_converter[set] = lambda x: FiniteSet(*x) |
| _sympy_converter[frozenset] = lambda x: FiniteSet(*x) |
|
|
|
|
| class SymmetricDifference(Set): |
| """Represents the set of elements which are in either of the |
| sets and not in their intersection. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import SymmetricDifference, FiniteSet |
| >>> SymmetricDifference(FiniteSet(1, 2, 3), FiniteSet(3, 4, 5)) |
| {1, 2, 4, 5} |
| |
| See Also |
| ======== |
| |
| Complement, Union |
| |
| References |
| ========== |
| |
| .. [1] https://en.wikipedia.org/wiki/Symmetric_difference |
| """ |
|
|
| is_SymmetricDifference = True |
|
|
| def __new__(cls, a, b, evaluate=True): |
| if evaluate: |
| return SymmetricDifference.reduce(a, b) |
|
|
| return Basic.__new__(cls, a, b) |
|
|
| @staticmethod |
| def reduce(A, B): |
| result = B._symmetric_difference(A) |
| if result is not None: |
| return result |
| else: |
| return SymmetricDifference(A, B, evaluate=False) |
|
|
| def as_relational(self, symbol): |
| """Rewrite a symmetric_difference in terms of equalities and |
| logic operators""" |
| A, B = self.args |
|
|
| A_rel = A.as_relational(symbol) |
| B_rel = B.as_relational(symbol) |
|
|
| return Xor(A_rel, B_rel) |
|
|
| @property |
| def is_iterable(self): |
| if all(arg.is_iterable for arg in self.args): |
| return True |
|
|
| def __iter__(self): |
|
|
| args = self.args |
| union = roundrobin(*(iter(arg) for arg in args)) |
|
|
| for item in union: |
| count = 0 |
| for s in args: |
| if item in s: |
| count += 1 |
|
|
| if count % 2 == 1: |
| yield item |
|
|
|
|
|
|
| class DisjointUnion(Set): |
| """ Represents the disjoint union (also known as the external disjoint union) |
| of a finite number of sets. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import DisjointUnion, FiniteSet, Interval, Union, Symbol |
| >>> A = FiniteSet(1, 2, 3) |
| >>> B = Interval(0, 5) |
| >>> DisjointUnion(A, B) |
| DisjointUnion({1, 2, 3}, Interval(0, 5)) |
| >>> DisjointUnion(A, B).rewrite(Union) |
| Union(ProductSet({1, 2, 3}, {0}), ProductSet(Interval(0, 5), {1})) |
| >>> C = FiniteSet(Symbol('x'), Symbol('y'), Symbol('z')) |
| >>> DisjointUnion(C, C) |
| DisjointUnion({x, y, z}, {x, y, z}) |
| >>> DisjointUnion(C, C).rewrite(Union) |
| ProductSet({x, y, z}, {0, 1}) |
| |
| References |
| ========== |
| |
| https://en.wikipedia.org/wiki/Disjoint_union |
| """ |
|
|
| def __new__(cls, *sets): |
| dj_collection = [] |
| for set_i in sets: |
| if isinstance(set_i, Set): |
| dj_collection.append(set_i) |
| else: |
| raise TypeError("Invalid input: '%s', input args \ |
| to DisjointUnion must be Sets" % set_i) |
| obj = Basic.__new__(cls, *dj_collection) |
| return obj |
|
|
| @property |
| def sets(self): |
| return self.args |
|
|
| @property |
| def is_empty(self): |
| return fuzzy_and(s.is_empty for s in self.sets) |
|
|
| @property |
| def is_finite_set(self): |
| all_finite = fuzzy_and(s.is_finite_set for s in self.sets) |
| return fuzzy_or([self.is_empty, all_finite]) |
|
|
| @property |
| def is_iterable(self): |
| if self.is_empty: |
| return False |
| iter_flag = True |
| for set_i in self.sets: |
| if not set_i.is_empty: |
| iter_flag = iter_flag and set_i.is_iterable |
| return iter_flag |
|
|
| def _eval_rewrite_as_Union(self, *sets, **kwargs): |
| """ |
| Rewrites the disjoint union as the union of (``set`` x {``i``}) |
| where ``set`` is the element in ``sets`` at index = ``i`` |
| """ |
|
|
| dj_union = S.EmptySet |
| index = 0 |
| for set_i in sets: |
| if isinstance(set_i, Set): |
| cross = ProductSet(set_i, FiniteSet(index)) |
| dj_union = Union(dj_union, cross) |
| index = index + 1 |
| return dj_union |
|
|
| def _contains(self, element): |
| """ |
| ``in`` operator for DisjointUnion |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Interval, DisjointUnion |
| >>> D = DisjointUnion(Interval(0, 1), Interval(0, 2)) |
| >>> (0.5, 0) in D |
| True |
| >>> (0.5, 1) in D |
| True |
| >>> (1.5, 0) in D |
| False |
| >>> (1.5, 1) in D |
| True |
| |
| Passes operation on to constituent sets |
| """ |
| if not isinstance(element, Tuple) or len(element) != 2: |
| return S.false |
|
|
| if not element[1].is_Integer: |
| return S.false |
|
|
| if element[1] >= len(self.sets) or element[1] < 0: |
| return S.false |
|
|
| return self.sets[element[1]]._contains(element[0]) |
|
|
| def _kind(self): |
| if not self.args: |
| return SetKind() |
| elif all(i.kind == self.args[0].kind for i in self.args): |
| return self.args[0].kind |
| else: |
| return SetKind(UndefinedKind) |
|
|
| def __iter__(self): |
| if self.is_iterable: |
|
|
| iters = [] |
| for i, s in enumerate(self.sets): |
| iters.append(iproduct(s, {Integer(i)})) |
|
|
| return iter(roundrobin(*iters)) |
| else: |
| raise ValueError("'%s' is not iterable." % self) |
|
|
| def __len__(self): |
| """ |
| Returns the length of the disjoint union, i.e., the number of elements in the set. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import FiniteSet, DisjointUnion, EmptySet |
| >>> D1 = DisjointUnion(FiniteSet(1, 2, 3, 4), EmptySet, FiniteSet(3, 4, 5)) |
| >>> len(D1) |
| 7 |
| >>> D2 = DisjointUnion(FiniteSet(3, 5, 7), EmptySet, FiniteSet(3, 5, 7)) |
| >>> len(D2) |
| 6 |
| >>> D3 = DisjointUnion(EmptySet, EmptySet) |
| >>> len(D3) |
| 0 |
| |
| Adds up the lengths of the constituent sets. |
| """ |
|
|
| if self.is_finite_set: |
| size = 0 |
| for set in self.sets: |
| size += len(set) |
| return size |
| else: |
| raise ValueError("'%s' is not a finite set." % self) |
|
|
|
|
| def imageset(*args): |
| r""" |
| Return an image of the set under transformation ``f``. |
| |
| Explanation |
| =========== |
| |
| If this function cannot compute the image, it returns an |
| unevaluated ImageSet object. |
| |
| .. math:: |
| \{ f(x) \mid x \in \mathrm{self} \} |
| |
| Examples |
| ======== |
| |
| >>> from sympy import S, Interval, imageset, sin, Lambda |
| >>> from sympy.abc import x |
| |
| >>> imageset(x, 2*x, Interval(0, 2)) |
| Interval(0, 4) |
| |
| >>> imageset(lambda x: 2*x, Interval(0, 2)) |
| Interval(0, 4) |
| |
| >>> imageset(Lambda(x, sin(x)), Interval(-2, 1)) |
| ImageSet(Lambda(x, sin(x)), Interval(-2, 1)) |
| |
| >>> imageset(sin, Interval(-2, 1)) |
| ImageSet(Lambda(x, sin(x)), Interval(-2, 1)) |
| >>> imageset(lambda y: x + y, Interval(-2, 1)) |
| ImageSet(Lambda(y, x + y), Interval(-2, 1)) |
| |
| Expressions applied to the set of Integers are simplified |
| to show as few negatives as possible and linear expressions |
| are converted to a canonical form. If this is not desirable |
| then the unevaluated ImageSet should be used. |
| |
| >>> imageset(x, -2*x + 5, S.Integers) |
| ImageSet(Lambda(x, 2*x + 1), Integers) |
| |
| See Also |
| ======== |
| |
| sympy.sets.fancysets.ImageSet |
| |
| """ |
| from .fancysets import ImageSet |
| from .setexpr import set_function |
|
|
| if len(args) < 2: |
| raise ValueError('imageset expects at least 2 args, got: %s' % len(args)) |
|
|
| if isinstance(args[0], (Symbol, tuple)) and len(args) > 2: |
| f = Lambda(args[0], args[1]) |
| set_list = args[2:] |
| else: |
| f = args[0] |
| set_list = args[1:] |
|
|
| if isinstance(f, Lambda): |
| pass |
| elif callable(f): |
| nargs = getattr(f, 'nargs', {}) |
| if nargs: |
| if len(nargs) != 1: |
| raise NotImplementedError(filldedent(''' |
| This function can take more than 1 arg |
| but the potentially complicated set input |
| has not been analyzed at this point to |
| know its dimensions. TODO |
| ''')) |
| N = nargs.args[0] |
| if N == 1: |
| s = 'x' |
| else: |
| s = [Symbol('x%i' % i) for i in range(1, N + 1)] |
| else: |
| s = inspect.signature(f).parameters |
|
|
| dexpr = _sympify(f(*[Dummy() for i in s])) |
| var = tuple(uniquely_named_symbol( |
| Symbol(i), dexpr) for i in s) |
| f = Lambda(var, f(*var)) |
| else: |
| raise TypeError(filldedent(''' |
| expecting lambda, Lambda, or FunctionClass, |
| not \'%s\'.''' % func_name(f))) |
|
|
| if any(not isinstance(s, Set) for s in set_list): |
| name = [func_name(s) for s in set_list] |
| raise ValueError( |
| 'arguments after mapping should be sets, not %s' % name) |
|
|
| if len(set_list) == 1: |
| set = set_list[0] |
| try: |
| |
| r = set_function(f, set) |
| if r is None: |
| raise TypeError |
| if not r: |
| return r |
| except TypeError: |
| r = ImageSet(f, set) |
| if isinstance(r, ImageSet): |
| f, set = r.args |
|
|
| if f.variables[0] == f.expr: |
| return set |
|
|
| if isinstance(set, ImageSet): |
| |
| |
| |
| |
| if len(set.lamda.variables) == 1 and len(f.variables) == 1: |
| x = set.lamda.variables[0] |
| y = f.variables[0] |
| return imageset( |
| Lambda(x, f.expr.subs(y, set.lamda.expr)), *set.base_sets) |
|
|
| if r is not None: |
| return r |
|
|
| return ImageSet(f, *set_list) |
|
|
|
|
| def is_function_invertible_in_set(func, setv): |
| """ |
| Checks whether function ``func`` is invertible when the domain is |
| restricted to set ``setv``. |
| """ |
| |
| if func in (exp, log): |
| return True |
| u = Dummy("u") |
| fdiff = func(u).diff(u) |
| |
| |
| if (fdiff > 0) == True or (fdiff < 0) == True: |
| return True |
| |
| return None |
|
|
|
|
| def simplify_union(args): |
| """ |
| Simplify a :class:`Union` using known rules. |
| |
| Explanation |
| =========== |
| |
| We first start with global rules like 'Merge all FiniteSets' |
| |
| Then we iterate through all pairs and ask the constituent sets if they |
| can simplify themselves with any other constituent. This process depends |
| on ``union_sets(a, b)`` functions. |
| """ |
| from sympy.sets.handlers.union import union_sets |
|
|
| |
| if not args: |
| return S.EmptySet |
|
|
| for arg in args: |
| if not isinstance(arg, Set): |
| raise TypeError("Input args to Union must be Sets") |
|
|
| |
| finite_sets = [x for x in args if x.is_FiniteSet] |
| if len(finite_sets) > 1: |
| a = (x for set in finite_sets for x in set) |
| finite_set = FiniteSet(*a) |
| args = [finite_set] + [x for x in args if not x.is_FiniteSet] |
|
|
| |
| |
| args = set(args) |
| new_args = True |
| while new_args: |
| for s in args: |
| new_args = False |
| for t in args - {s}: |
| new_set = union_sets(s, t) |
| |
| |
| if new_set is not None: |
| if not isinstance(new_set, set): |
| new_set = {new_set} |
| new_args = (args - {s, t}).union(new_set) |
| break |
| if new_args: |
| args = new_args |
| break |
|
|
| if len(args) == 1: |
| return args.pop() |
| else: |
| return Union(*args, evaluate=False) |
|
|
|
|
| def simplify_intersection(args): |
| """ |
| Simplify an intersection using known rules. |
| |
| Explanation |
| =========== |
| |
| We first start with global rules like |
| 'if any empty sets return empty set' and 'distribute any unions' |
| |
| Then we iterate through all pairs and ask the constituent sets if they |
| can simplify themselves with any other constituent |
| """ |
|
|
| |
| if not args: |
| return S.UniversalSet |
|
|
| for arg in args: |
| if not isinstance(arg, Set): |
| raise TypeError("Input args to Union must be Sets") |
|
|
| |
| if S.EmptySet in args: |
| return S.EmptySet |
|
|
| |
| rv = Intersection._handle_finite_sets(args) |
|
|
| if rv is not None: |
| return rv |
|
|
| |
| for s in args: |
| if s.is_Union: |
| other_sets = set(args) - {s} |
| if len(other_sets) > 0: |
| other = Intersection(*other_sets) |
| return Union(*(Intersection(arg, other) for arg in s.args)) |
| else: |
| return Union(*s.args) |
|
|
| for s in args: |
| if s.is_Complement: |
| args.remove(s) |
| other_sets = args + [s.args[0]] |
| return Complement(Intersection(*other_sets), s.args[1]) |
|
|
| from sympy.sets.handlers.intersection import intersection_sets |
|
|
| |
| |
|
|
| |
| |
| args = set(args) |
| new_args = True |
| while new_args: |
| for s in args: |
| new_args = False |
| for t in args - {s}: |
| new_set = intersection_sets(s, t) |
| |
| |
|
|
| if new_set is not None: |
| new_args = (args - {s, t}).union({new_set}) |
| break |
| if new_args: |
| args = new_args |
| break |
|
|
| if len(args) == 1: |
| return args.pop() |
| else: |
| return Intersection(*args, evaluate=False) |
|
|
|
|
| def _handle_finite_sets(op, x, y, commutative): |
| |
| fs_args, other = sift([x, y], lambda x: isinstance(x, FiniteSet), binary=True) |
| if len(fs_args) == 2: |
| return FiniteSet(*[op(i, j) for i in fs_args[0] for j in fs_args[1]]) |
| elif len(fs_args) == 1: |
| sets = [_apply_operation(op, other[0], i, commutative) for i in fs_args[0]] |
| return Union(*sets) |
| else: |
| return None |
|
|
|
|
| def _apply_operation(op, x, y, commutative): |
| from .fancysets import ImageSet |
| d = Dummy('d') |
|
|
| out = _handle_finite_sets(op, x, y, commutative) |
| if out is None: |
| out = op(x, y) |
|
|
| if out is None and commutative: |
| out = op(y, x) |
| if out is None: |
| _x, _y = symbols("x y") |
| if isinstance(x, Set) and not isinstance(y, Set): |
| out = ImageSet(Lambda(d, op(d, y)), x).doit() |
| elif not isinstance(x, Set) and isinstance(y, Set): |
| out = ImageSet(Lambda(d, op(x, d)), y).doit() |
| else: |
| out = ImageSet(Lambda((_x, _y), op(_x, _y)), x, y) |
| return out |
|
|
|
|
| def set_add(x, y): |
| from sympy.sets.handlers.add import _set_add |
| return _apply_operation(_set_add, x, y, commutative=True) |
|
|
|
|
| def set_sub(x, y): |
| from sympy.sets.handlers.add import _set_sub |
| return _apply_operation(_set_sub, x, y, commutative=False) |
|
|
|
|
| def set_mul(x, y): |
| from sympy.sets.handlers.mul import _set_mul |
| return _apply_operation(_set_mul, x, y, commutative=True) |
|
|
|
|
| def set_div(x, y): |
| from sympy.sets.handlers.mul import _set_div |
| return _apply_operation(_set_div, x, y, commutative=False) |
|
|
|
|
| def set_pow(x, y): |
| from sympy.sets.handlers.power import _set_pow |
| return _apply_operation(_set_pow, x, y, commutative=False) |
|
|
|
|
| def set_function(f, x): |
| from sympy.sets.handlers.functions import _set_function |
| return _set_function(f, x) |
|
|
|
|
| class SetKind(Kind): |
| """ |
| SetKind is kind for all Sets |
| |
| Every instance of Set will have kind ``SetKind`` parametrised by the kind |
| of the elements of the ``Set``. The kind of the elements might be |
| ``NumberKind``, or ``TupleKind`` or something else. When not all elements |
| have the same kind then the kind of the elements will be given as |
| ``UndefinedKind``. |
| |
| Parameters |
| ========== |
| |
| element_kind: Kind (optional) |
| The kind of the elements of the set. In a well defined set all elements |
| will have the same kind. Otherwise the kind should |
| :class:`sympy.core.kind.UndefinedKind`. The ``element_kind`` argument is optional but |
| should only be omitted in the case of ``EmptySet`` whose kind is simply |
| ``SetKind()`` |
| |
| Examples |
| ======== |
| |
| >>> from sympy import Interval |
| >>> Interval(1, 2).kind |
| SetKind(NumberKind) |
| >>> Interval(1,2).kind.element_kind |
| NumberKind |
| |
| See Also |
| ======== |
| |
| sympy.core.kind.NumberKind |
| sympy.matrices.kind.MatrixKind |
| sympy.core.containers.TupleKind |
| """ |
| def __new__(cls, element_kind=None): |
| obj = super().__new__(cls, element_kind) |
| obj.element_kind = element_kind |
| return obj |
|
|
| def __repr__(self): |
| if not self.element_kind: |
| return "SetKind()" |
| else: |
| return "SetKind(%s)" % self.element_kind |
|
|