"""This module contains the :term:`algebra of sets` and related functionality."""
# Copyright Algebraix Data Corporation 2015 - 2017
#
# This file is part of algebraixlib <http://github.com/AlgebraixData/algebraixlib>.
#
# algebraixlib is free software: you can redistribute it and/or modify it under the terms of version
# 3 of the GNU Lesser General Public License as published by the Free Software Foundation.
#
# algebraixlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along with algebraixlib.
# If not, see <http://www.gnu.org/licenses/>.
# --------------------------------------------------------------------------------------------------
import collections as _collections
import functools as _functools
import algebraixlib.mathobjects as _mo
import algebraixlib.structure as _structure
import algebraixlib.undef as _undef
from ..cache_status import CacheStatus
# --------------------------------------------------------------------------------------------------
[docs]class Algebra:
"""Provide the operations and relations that are members of the :term:`algebra of sets`.
This class contains only static member functions. Its main purpose is to provide a namespace for
and highlight the operations and relations that belong to the algebra of sets. All member
functions are also available at the enclosing module level.
"""
# ----------------------------------------------------------------------------------------------
# Binary algebra operations.
@staticmethod
[docs] def union(set1: 'P( M )', set2: 'P( M )', _checked=True) -> 'P( M )':
r"""Return the union of ``set1`` with ``set2``.
:return: The :term:`binary union` of ``set1`` and ``set2`` or `Undef()` if ``set1`` or
``set2`` are not :term:`set`\s (that is, instances of :class:`~.Set`).
"""
# pylint: disable=too-many-branches
if _checked:
if not is_member(set1):
return _undef.make_or_raise_undef2(set1)
if not is_member(set2):
return _undef.make_or_raise_undef2(set2)
else:
assert is_member_or_undef(set1)
assert is_member_or_undef(set2)
if set1 is _undef.Undef() or set2 is _undef.Undef():
return _undef.make_or_raise_undef(2)
values = set1.data.union(set2.data)
result = _mo.Set(values, direct_load=True)
if not result.is_empty:
# Relation flags:
if set1.cached_is_relation and set2.cached_is_relation:
result.cache_relation(CacheStatus.IS)
if set1.cached_is_absolute and set2.cached_is_absolute:
result.cache_absolute(CacheStatus.IS)
elif set1.cached_is_not_absolute or set2.cached_is_not_absolute:
result.cache_absolute(CacheStatus.IS_NOT)
if set1.cached_is_not_functional or set2.cached_is_not_functional:
result.cache_functional(CacheStatus.IS_NOT)
if set1.cached_is_not_right_functional or set2.cached_is_not_right_functional:
result.cache_right_functional(CacheStatus.IS_NOT)
elif set1.cached_is_not_relation or set2.cached_is_not_relation:
result.cache_relation(CacheStatus.IS_NOT)
# Clan flags:
if set1.cached_is_clan and set2.cached_is_clan:
result.cache_clan(CacheStatus.IS)
if set1.cached_is_absolute and set2.cached_is_absolute:
result.cache_absolute(CacheStatus.IS)
elif set1.cached_is_not_absolute or set2.cached_is_not_absolute:
result.cache_absolute(CacheStatus.IS_NOT)
if set1.cached_is_functional and set2.cached_is_functional:
result.cache_functional(CacheStatus.IS)
elif set1.cached_is_not_functional or set2.cached_is_not_functional:
result.cache_functional(CacheStatus.IS_NOT)
if set1.cached_is_right_functional and set2.cached_is_right_functional:
result.cache_right_functional(CacheStatus.IS)
elif set1.cached_is_not_right_functional or set2.cached_is_not_right_functional:
result.cache_right_functional(CacheStatus.IS_NOT)
if set1.cached_is_not_regular or set2.cached_is_not_regular:
result.cache_regular(CacheStatus.IS_NOT)
if set1.cached_is_not_right_regular or set2.cached_is_not_right_regular:
result.cache_right_regular(CacheStatus.IS_NOT)
elif set1.cached_is_not_clan or set2.cached_is_not_clan:
result.cache_clan(CacheStatus.IS_NOT)
# Neither are clan and neither are rel
if set1.cached_is_not_clan and set2.cached_is_not_clan\
and set1.cached_is_not_relation and set2.cached_is_not_relation:
if set1.cached_is_absolute and set2.cached_is_absolute:
result.cache_absolute(CacheStatus.IS)
elif set1.cached_is_not_absolute or set2.cached_is_not_absolute:
result.cache_absolute(CacheStatus.IS_NOT)
return result
@staticmethod
[docs] def intersect(set1: 'P( M )', set2: 'P( M )', _checked=True) -> 'P( M )':
r"""Return the intersection of ``set1`` with ``set2``.
:return: The :term:`binary intersection` of ``set1`` and ``set2`` or `Undef()` if ``set1``
or ``set2`` are not :term:`set`\s (that is, instances of :class:`~.Set`).
"""
# pylint: disable=too-many-branches
if _checked:
if not is_member(set1):
return _undef.make_or_raise_undef2(set1)
if not is_member(set2):
return _undef.make_or_raise_undef2(set2)
else:
assert is_member_or_undef(set1)
assert is_member_or_undef(set2)
if set1 is _undef.Undef() or set2 is _undef.Undef():
return _undef.make_or_raise_undef(2)
values = set1.data.intersection(set2.data)
result = _mo.Set(values, direct_load=True)
if not result.is_empty:
# Relation flags:
if set1.cached_is_relation or set2.cached_is_relation:
result.cache_relation(CacheStatus.IS)
if set1.cached_is_absolute or set2.cached_is_absolute:
result.cache_absolute(CacheStatus.IS)
if set1.cached_is_functional or set2.cached_is_functional:
result.cache_functional(CacheStatus.IS)
if set1.cached_is_right_functional or set2.cached_is_right_functional:
result.cache_right_functional(CacheStatus.IS)
# Clan flags:
if set1.cached_is_clan or set2.cached_is_clan:
result.cache_clan(CacheStatus.IS)
if set1.cached_is_absolute or set2.cached_is_absolute:
result.cache_absolute(CacheStatus.IS)
if set1.cached_is_functional or set2.cached_is_functional:
result.cache_functional(CacheStatus.IS)
if set1.cached_is_right_functional or set2.cached_is_right_functional:
result.cache_right_functional(CacheStatus.IS)
if set1.cached_is_regular or set2.cached_is_regular:
result.cache_regular(CacheStatus.IS)
if set1.cached_is_right_regular or set2.cached_is_right_regular:
result.cache_right_regular(CacheStatus.IS)
return result
@staticmethod
[docs] def minus(set1: 'P( M )', set2: 'P( M )', _checked=True) -> 'P( M )':
r"""Return the set difference of ``set1`` and ``set2``.
:return: The :term:`difference` of ``set1`` and ``set2`` or `Undef()` if ``set1`` or
``set2`` are not :term:`set`\s (that is, instances of :class:`~.Set`).
"""
# pylint: disable=too-many-branches
if _checked:
if not is_member(set1):
return _undef.make_or_raise_undef2(set1)
if not is_member(set2):
return _undef.make_or_raise_undef2(set2)
else:
assert is_member_or_undef(set1)
assert is_member_or_undef(set2)
if set1 is _undef.Undef() or set2 is _undef.Undef():
return _undef.make_or_raise_undef(2)
result = _mo.Set(set1.data.difference(set2.data), direct_load=True)
if not result.is_empty:
# Relation flags:
if set1.cached_is_relation:
result.cache_relation(CacheStatus.IS)
if set1.cached_is_absolute:
result.cache_absolute(CacheStatus.IS)
if set1.cached_is_functional:
result.cache_functional(CacheStatus.IS)
if set1.cached_is_right_functional:
result.cache_right_functional(CacheStatus.IS)
# Clan flags:
if set1.cached_is_clan:
result.cache_clan(CacheStatus.IS)
if set1.cached_is_absolute:
result.cache_absolute(CacheStatus.IS)
if set1.cached_is_functional:
result.cache_functional(CacheStatus.IS)
if set1.cached_is_right_functional:
result.cache_right_functional(CacheStatus.IS)
if set1.cached_is_reflexive:
result.cache_reflexive(CacheStatus.IS)
if set1.cached_is_symmetric:
result.cache_symmetric(CacheStatus.IS)
if set1.cached_is_transitive:
result.cache_transitive(CacheStatus.IS)
if set1.cached_is_regular:
result.cache_regular(CacheStatus.IS)
if set1.cached_is_right_regular:
result.cache_right_regular(CacheStatus.IS)
return result
@staticmethod
[docs] def substrict(set1: 'P( M )', set2: 'P( M )', _checked=True) -> 'P( M )':
r"""Return ``set1`` if it is a subset of ``set2``, otherwise return `Undef()`.
:return: Return the :term:`substriction` of ``set1`` and ``set1``; that is, return ``set1``
if it is a :term:`subset` of ``set2`` or `Undef()` if not. Also return `Undef()` if
``set1`` or ``set2`` are not :term:`set`\s (that is, instances of :class:`~.Set`).
"""
# pylint: disable=too-many-branches
if _checked:
if not is_member(set1):
return _undef.make_or_raise_undef2(set1)
if not is_member(set2):
return _undef.make_or_raise_undef2(set2)
else:
assert is_member_or_undef(set1)
assert is_member_or_undef(set2)
if set1 is _undef.Undef() or set2 is _undef.Undef():
return _undef.make_or_raise_undef(2)
if not is_subset_of(set1, set2, _checked=False):
return _undef.make_or_raise_undef(2)
if not set1.is_empty:
# Relation flags:
if set1.cached_is_relation:
if set2.cached_is_absolute:
set1.cache_absolute(CacheStatus.IS)
if set2.cached_is_functional:
set1.cache_functional(CacheStatus.IS)
if set2.cached_is_right_functional:
set1.cache_right_functional(CacheStatus.IS)
# Clan flags:
if set1.cached_is_clan:
if set2.cached_is_absolute:
set1.cache_absolute(CacheStatus.IS)
if set2.cached_is_functional:
set1.cache_functional(CacheStatus.IS)
if set2.cached_is_right_functional:
set1.cache_right_functional(CacheStatus.IS)
if set2.cached_is_regular:
set1.cache_regular(CacheStatus.IS)
if set2.cached_is_right_regular:
set1.cache_right_regular(CacheStatus.IS)
return set1
@staticmethod
[docs] def superstrict(set1: 'P( M )', set2: 'P( M )', _checked=True) -> 'P( M )':
r"""Return ``set1`` if it is a superset of ``set2``, otherwise return `Undef()`.
:return: Return the :term:`superstriction` of ``set1`` and ``set1``; that is, return
``set1`` if it is a :term:`superset` of ``set2`` or `Undef()` if not. Also return
`Undef()` if ``set1`` or ``set2`` are not :term:`set`\s (that is, instances of
:class:`~.Set`).
"""
# pylint: disable=too-many-branches
if _checked:
if not is_member(set1):
return _undef.make_or_raise_undef2(set1)
if not is_member(set2):
return _undef.make_or_raise_undef2(set2)
else:
assert is_member_or_undef(set1)
assert is_member_or_undef(set2)
if set1 is _undef.Undef() or set2 is _undef.Undef():
return _undef.make_or_raise_undef(2)
if not is_superset_of(set1, set2, _checked=False):
return _undef.make_or_raise_undef(2)
if not set1.is_empty:
# Relation flags:
if set1.cached_is_relation:
if set2.cached_is_not_absolute:
set1.cache_absolute(CacheStatus.IS_NOT)
if set2.cached_is_not_functional:
set1.cache_functional(CacheStatus.IS_NOT)
if set2.cached_is_not_right_functional:
set1.cache_right_functional(CacheStatus.IS_NOT)
# Clan flags:
if set1.cached_is_clan:
if set2.cached_is_not_absolute:
set1.cache_absolute(CacheStatus.IS_NOT)
if set2.cached_is_not_functional:
set1.cache_functional(CacheStatus.IS_NOT)
if set2.cached_is_not_right_functional:
set1.cache_right_functional(CacheStatus.IS_NOT)
if set2.cached_is_not_regular:
set1.cache_regular(CacheStatus.IS_NOT)
if set2.cached_is_not_right_regular:
set1.cache_right_regular(CacheStatus.IS_NOT)
return set1
# ----------------------------------------------------------------------------------------------
# Algebra relations.
@staticmethod
[docs] def is_subset_of(set1: 'P( M )', set2: 'P( M )', _checked=True) -> bool:
r"""Return whether ``set1`` is a subset of ``set2``.
:return: ``True`` if ``set1`` is a :term:`subset` of ``set2``, ``False`` if not. Return
`Undef()` if ``set1`` or ``set2`` are not :term:`set`\s (that is, instances of
:class:`~.Set`).
"""
if _checked:
if not is_member(set1):
return _undef.make_or_raise_undef2(set1)
if not is_member(set2):
return _undef.make_or_raise_undef2(set2)
else:
assert is_member_or_undef(set1)
assert is_member_or_undef(set2)
if set1 is _undef.Undef() or set2 is _undef.Undef():
return _undef.make_or_raise_undef(2)
return set1.data.issubset(set2.data)
@staticmethod
[docs] def is_superset_of(set1: 'P( M )', set2: 'P( M )', _checked=True) -> bool:
r"""Return whether ``set1`` is a superset of ``set2``.
:return: ``True`` if ``set1`` is a :term:`superset` of ``set2``, ``False`` if not. Return
`Undef()` if ``set1`` or ``set2`` are not :term:`set`\s (that is, instances of
:class:`~.Set`).
"""
if _checked:
if not is_member(set1):
return _undef.make_or_raise_undef2(set1)
if not is_member(set2):
return _undef.make_or_raise_undef2(set2)
else:
assert is_member_or_undef(set1)
assert is_member_or_undef(set2)
if set1 is _undef.Undef() or set2 is _undef.Undef():
return _undef.make_or_raise_undef(2)
return set1.data.issuperset(set2.data)
# For convenience, make the members of class Algebra (they are all static functions) available at
# the module level.
# pylint: disable=invalid-name
#: Convenience redirection to `Algebra.union`.
union = Algebra.union
#: Convenience redirection to `Algebra.intersect`.
intersect = Algebra.intersect
#: Convenience redirection to `Algebra.minus`.
minus = Algebra.minus
#: Convenience redirection to `Algebra.substrict`.
substrict = Algebra.substrict
#: Convenience redirection to `Algebra.superstrict`.
superstrict = Algebra.superstrict
#: Convenience redirection to `Algebra.is_subset_of`.
is_subset_of = Algebra.is_subset_of
#: Convenience redirection to `Algebra.is_superset_of`.
is_superset_of = Algebra.is_superset_of
# pylint: enable=invalid-name
# --------------------------------------------------------------------------------------------------
# Metadata functions.
[docs]def get_name() -> str:
"""Return the name and :term:`ground set` of this :term:`algebra` in string form."""
return 'Sets(M): {ground_set}'.format(ground_set=str(get_ground_set()))
[docs]def get_ground_set() -> _structure.Structure:
"""Return the :term:`ground set` of this :term:`algebra`."""
return _structure.PowerSet(_structure.GenesisSetM())
[docs]def get_absolute_ground_set() -> _structure.Structure:
"""Return the :term:`absolute ground set` of this :term:`algebra`."""
return _structure.PowerSet(_structure.GenesisSetA())
[docs]def is_member(obj: _mo.MathObject) -> bool:
"""Return whether ``obj`` is a member of the :term:`ground set` of this :term:`algebra`.
:return: ``True`` if ``obj`` is a :term:`set` (an instance of :class:`~.Set`),
``False`` if not.
"""
return obj.is_set
[docs]def is_member_or_undef(obj: _mo.MathObject) -> bool:
"""Return whether ``obj`` is either a member of the :term:`ground set` of this :term:`algebra`
or :class:`~.Undef`.
:return: ``True`` if ``obj`` is either a :term:`relation` or :class:`~.Undef`,
``False`` if not.
"""
return obj is _undef.Undef() or is_member(obj)
[docs]def is_absolute_member(obj: _mo.MathObject) -> bool:
"""Return whether ``obj`` is a member of the :term:`absolute ground set` of this algebra.
:return: ``True`` if ``obj`` is an :term:`absolute set`, ``False`` if not.
"""
if not obj.is_set:
# If known to not be a set, it's also not an absolute set. No further checking or caching.
return False
# From this point on, `obj` is known to be a set.
if obj.cached_absolute == CacheStatus.UNKNOWN:
import algebraixlib.algebras.clans as _clans
import algebraixlib.algebras.relations as _relations
# In order to find out whether this is an absolute set, we need to know whether `obj` is a
# relation or a clan (both sets). If it is one of these, it is not an absolute set -- but
# we also don't know whether it is an absolute relation or clan. So we return `False` but
# don't cache anything. (But we have now cached that it is a relation or a clan.)
if _relations.is_member(obj) or _clans.is_member(obj):
return False
is_absolute_set = all(elem.is_atom for elem in obj)
obj.cache_absolute(CacheStatus.from_bool(is_absolute_set))
# In order to determine whether this is an absolute set, we need to also examine whether this
# is a relation or a clan (both are sets). Absolute relations and absolute clans are not
# absolute sets.
return obj.cached_is_absolute and not obj.cached_is_relation and not obj.cached_is_clan
# --------------------------------------------------------------------------------------------------
# Related operations, not formally part of the algebra.
[docs]def multify(set_: 'P( M )', _checked=True) -> 'P( M x N )':
"""Return a :term:`multiset` based on ``set_`` where all multiples are set to 1."""
if _checked:
if not is_member(set_):
return _undef.make_or_raise_undef2(set_)
else:
assert is_member_or_undef(set_)
if set_ is _undef.Undef():
return _undef.make_or_raise_undef(2)
result = _mo.Multiset(set_.data, direct_load=True)
if not result.is_empty:
result.cache_multiclan(set_.cached_clan)
if set_.cached_is_relation:
# We don't yet have a concept of multirelations (multisets of couplets). This would be
# handled here.
pass
elif set_.cached_is_clan:
result.cache_absolute(set_.cached_absolute)
result.cache_functional(set_.cached_functional)
result.cache_right_functional(set_.cached_right_functional)
result.cache_reflexive(set_.cached_reflexive)
result.cache_symmetric(set_.cached_symmetric)
result.cache_transitive(set_.cached_transitive)
result.cache_regular(set_.cached_regular)
result.cache_right_regular(set_.cached_right_regular)
if set_.cached_is_not_relation and set_.cached_is_not_clan:
# set_ is known to be a plain set.
result.cache_absolute(set_.cached_absolute)
result.cache_functional(CacheStatus.N_A)
result.cache_right_functional(CacheStatus.N_A)
result.cache_reflexive(CacheStatus.N_A)
result.cache_symmetric(CacheStatus.N_A)
result.cache_transitive(CacheStatus.N_A)
result.cache_regular(CacheStatus.N_A)
result.cache_right_regular(CacheStatus.N_A)
return result
[docs]def big_union(set_: 'PP( M )', _checked=True) -> 'P( M )':
"""Return the union of all members of ``set_``.
:return: The :term:`union` of all members of ``set_`` or `Undef()` if ``set_`` or any of its
members are not instances of :class:`~.Set`.
Example code:
.. code::
from algebraixlib.mathobjects import Set
from algebraixlib.algebras.sets import big_union
big_union(Set(Set('a', 'b'), Set('b', 'c')))
# Output: Set(Atom('a'), Atom('b'), Atom('c'))
big_union(Set(Set('a', 'b'), 'a'))
# Output: <algebraixlib.undef.Undef at 0x4004978>
"""
if _checked:
if not is_member(set_):
return _undef.make_or_raise_undef2(set_)
for element in set_:
if not is_member(element):
return _undef.make_or_raise_undef(2)
else:
assert is_member_or_undef(set_)
if set_ is _undef.Undef():
return _undef.make_or_raise_undef(2)
return chain_binary_operation(set_, _functools.partial(union, _checked=False), is_member)
[docs]def big_intersect(set_: 'PP( M )', _checked=True) -> 'P( M )':
"""Return the intersection of all members of ``set_``.
:return: The :term:`intersection` of all members of ``set_`` or `Undef()` if ``set_`` or any of
its members are not instances of :class:`~.Set`.
Example code:
.. code::
from algebraixlib.mathobjects import Set
from algebraixlib.algebras.sets import big_intersect
big_intersect(Set(Set('a', 'b'), Set('b', 'c')))
# Output: Set(Atom('b'))
big_intersect(Set(Set('a', 'b'), 'a'))
# Output: <algebraixlib.undef.Undef at 0x4004978>
"""
if _checked:
if not is_member(set_):
return _undef.make_or_raise_undef2(set_)
for element in set_:
if not is_member(element):
return _undef.make_or_raise_undef(2)
else:
assert is_member_or_undef(set_)
if set_ is _undef.Undef():
return _undef.make_or_raise_undef(2)
return chain_binary_operation(set_, _functools.partial(intersect, _checked=False), is_member)
[docs]def single(set_: _mo.Set):
"""Return the single element of ``set_``.
:return: Return the single element of ``set_``, or `Undef()` if ``set_`` has not exactly one
element or is not a :term:`set` (that is, an instance of :class:`~.Set`).
"""
if not is_member(set_):
return _undef.make_or_raise_undef2(set_)
if set_.cardinality == 1:
return next(iter(set_))
return _undef.make_or_raise_undef(2)
[docs]def some(set_: _mo.Set):
"""Return 'some' element of ``set_``. Use with caution - may be non-deterministic.
:return: Some element of ``set_``, or `Undef()` if ``set_`` is empty or is not a :term:`set`
(that is, an instance of :class:`~.Set`).
.. note:: This function should only be used in contexts where the way the return value will be
utilized by the calling function is invariant of the particular element returned; the
element of ``set_`` that is returned is non-deterministic.
This function is only intended to be used in (mostly implementation) scenarios where it
does not matter which element of ``set_`` is retrieved, because the expressions that
consume that value will be invariant with respect to the exact element of ``set_`` that is
returned.
"""
if not is_member(set_):
return _undef.make_or_raise_undef2(set_)
if len(set_) > 0:
return next(iter(set_))
return _undef.make_or_raise_undef(2)
[docs]def power_set(set_: _mo.Set):
"""Return the :term:`power set` of ``set_``."""
if not is_member(set_):
return _undef.make_or_raise_undef2(set_)
from itertools import combinations
result = []
for subset_size in range(set_.cardinality + 1):
subset_combinations = combinations(set_, subset_size)
result.extend(_mo.Set(comb) for comb in subset_combinations)
result = _mo.Set(result)
if not result.is_empty:
if set_.cached_is_relation:
result.cache_clan(CacheStatus.IS)
result.cache_absolute(set_.cached_absolute)
result.cache_functional(set_.cached_functional)
result.cache_right_functional(set_.cached_right_functional)
result.cache_regular(CacheStatus.IS_NOT)
result.cache_right_regular(CacheStatus.IS_NOT)
return result
[docs]def power_up(set_: _mo.Set):
"""'Add a set of braces' around the elements of ``set_``.
:return: A :class:`~.Set` where every element is a ``Set`` that contains exactly one element
of ``set_`` and where there is exactly one element-``Set`` for every element of ``set_``.
"""
if not is_member(set_):
return _undef.make_or_raise_undef2(set_)
result = _mo.Set((_mo.Set(element) for element in set_), direct_load=True)
if not result.is_empty:
if set_.cached_is_relation:
result.cache_clan(CacheStatus.IS)
result.cache_absolute(set_.cached_absolute)
result.cache_functional(CacheStatus.IS)
result.cache_right_functional(CacheStatus.IS)
return result
[docs]def restrict(set_: 'P( M )', selector: _collections.Callable) -> 'P( M )':
"""Return a set with all the elements from ``set_`` for which the predicate ``selector`` returns
``True``.
:param set_: The source data. Must be a :term:`set`.
:param selector: A :class:`~collections.abc.Callable` that accepts as single argument a
:class:`~.MathObject` and returns a `bool` that indicates whether the element is
in the result set (``True``) or not (``False``).
"""
# pylint: disable=too-many-branches
if not is_member(set_):
return _undef.make_or_raise_undef2(set_)
result = _mo.Set((element for element in set_ if selector(element)), direct_load=True)
if not result.is_empty:
# Relation flags:
if set_.cached_is_relation:
result.cache_relation(CacheStatus.IS)
if set_.cached_is_absolute:
result.cache_absolute(CacheStatus.IS)
if set_.cached_is_functional:
result.cache_functional(CacheStatus.IS)
if set_.cached_is_right_functional:
result.cache_right_functional(CacheStatus.IS)
# Clan flags:
if set_.cached_is_clan:
result.cache_clan(CacheStatus.IS)
if set_.cached_is_absolute:
result.cache_absolute(CacheStatus.IS)
if set_.cached_is_functional:
result.cache_functional(CacheStatus.IS)
if set_.cached_is_right_functional:
result.cache_right_functional(CacheStatus.IS)
if set_.cached_is_reflexive:
result.cache_reflexive(CacheStatus.IS)
if set_.cached_is_symmetric:
result.cache_symmetric(CacheStatus.IS)
if set_.cached_is_transitive:
result.cache_transitive(CacheStatus.IS)
if set_.cached_is_regular:
result.cache_regular(CacheStatus.IS)
if set_.cached_is_right_regular:
result.cache_right_regular(CacheStatus.IS)
return result
[docs]def chain_binary_operation(set_, binary_op, is_algebra_member):
r"""Chain all elements of ``set_`` with the binary operation ``binary_op`` and return the
result.
:param set_: A :term:`set` of sets or :term:`multiset`\s.
:param binary_op: The operation through which the members of ``set_`` are chained. It must be
commutative and associative.
:param is_algebra_member: The ``is_member()`` function of the :term:`algebra` of which the
elements of ``set_`` must be members.
:return: A member of ``algebra`` that is the result of chaining all elements of ``set_`` with
the :term:`binary operation` ``binary_op``.
"""
if not is_member(set_):
return _undef.make_or_raise_undef2(set_)
if set_.is_empty:
return set_
set_itr = iter(set_)
element1 = next(set_itr)
if not is_algebra_member(element1):
return _undef.make_or_raise_undef()
result = element1
for element in set_itr:
if not is_algebra_member(element):
return _undef.make_or_raise_undef()
result = binary_op(result, element)
return result