Exemplo n.º 1
0
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 == _mo.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(_mo.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
Exemplo n.º 2
0
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 == _mo.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(_mo.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
Exemplo n.º 3
0
def pattern_match(graph: 'PP( AxA )', pattern: dict):
    r"""Return all relations in ``graph`` that contain all members of ``pattern``.

    :param graph: An absolute :term:`clan`.
    :param pattern: A dictionary where the keys are the :term:`left`\s and the values the
        :term:`right`\s that will be matched.
    """
    assert _clans.is_member(graph)
    match_predicate = _clans.from_dict(pattern)
    return _clans.superstrict(graph, match_predicate, _checked=False)
Exemplo n.º 4
0
def pattern_match(graph: 'PP( AxA )', pattern: dict):
    """Return all relations in ``graph`` that contain all members of ``pattern``.

    :param graph: An absolute clan.
    :param pattern: A dictionary where the keys are the lefts and the values the rights that
        will be matched.
    """
    assert(_clans.is_member(graph))
    match_predicate = _clans.from_dict(pattern)
    return _clans.superstrict(graph, match_predicate, _checked=False)
Exemplo n.º 5
0
def export_csv(absolute_clan_or_multiclan,
               file_or_path,
               ordered_lefts=None,
               sort_key=None):
    r"""Export an absolute clan or absolute multiclan as CSV file with header row.

    The :term:`left component`\s of the :term:`clan` or term:`multiclan` are interpreted as
    column names and are exported as header row. Every :term:`relation` in the input becomes a
    data row in the CSV file.

    :param absolute_clan_or_multiclan: An :term:`absolute clan` or term:`absolute multiclan`. If
        it is not :term:`regular`, ``ordered_lefts`` must be given.
    :param file_or_path: Either a file path (in this case the CSV data is written to a file at this
        location) or a file object (in this case the CSV data is written to its ``.write()``
        function).
    :param ordered_lefts: (Optional) A ``Sequence`` of :term:`left`\s that are exported in the
        given order. Default is the sequence that is the lexically sorted :term:`left set` of the
        (multi)clan. This parameter is required if ``absolute_clan_or_multiclan`` is not
        term:`regular`.
    :param sort_key: (Optional) A function that compares two row-:term:`relation`\s and provides an
        order (for use with :func:`sorted`). The output is not sorted if ``sort_key`` is missing.
    :return: ``True`` if the CSV export succeeded, ``False`` if not.
    """
    if not _clans.is_absolute_member(absolute_clan_or_multiclan) \
            and not _multiclans.is_absolute_member(absolute_clan_or_multiclan):
        return False
    regular_clan = _clans.is_member(absolute_clan_or_multiclan) \
            and _clans.is_regular(absolute_clan_or_multiclan)
    regular_mclan = _multiclans.is_member(absolute_clan_or_multiclan) \
            and _multiclans.is_regular(absolute_clan_or_multiclan)
    if ordered_lefts is None and not (regular_clan or regular_mclan):
        return False

    if ordered_lefts is None:
        # Since this clan is regular, get first relation to acquire left set.
        rel = next(iter(absolute_clan_or_multiclan))
        # left_set is sorted to guarantee consistent iterations
        ordered_lefts = sorted([left.value for left in rel.get_left_set()])

    # Generate dictionaries that associates left components with their right components for each
    # relation.
    clan_as_list_of_dicts = _convert_clan_to_list_of_dicts(
        ordered_lefts, (absolute_clan_or_multiclan if sort_key is None else
                        sorted(absolute_clan_or_multiclan, key=sort_key)))
    # Write the dictionaries.
    _csv_dict_writer(file_or_path, ordered_lefts, clan_as_list_of_dicts)
    return True
Exemplo n.º 6
0
def is_right_regular(mo: _mo.MathObject, _checked: bool = True) -> bool:
    r"""Return whether ``mo`` is :term:`right-regular` or `Undef()` if not applicable.

    Is implemented for :term:`clan`\s, :term:`multiclan`\s and :term:`set`\s of (sets of ...) clans.
    Is also defined (but not yet implemented) for any combination of sets or :term:`multiset`\s
    of clans.
    """
    # pylint: disable=too-many-return-statements
    if _checked:
        if not isinstance(mo, _mo.MathObject):
            return _undef.make_or_raise_undef()

    # Check cache status.
    if mo.cached_right_regular == _mo.CacheStatus.IS:
        return True
    if mo.cached_right_regular == _mo.CacheStatus.IS_NOT:
        return False
    if mo.cached_right_regular == _mo.CacheStatus.N_A:
        return _undef.make_or_raise_undef(2)

    # Check type (right-regular is only defined on Sets and Multisets) and algebra memberships.
    if not mo.is_set and not mo.is_multiset:
        mo.cache_right_regular(_mo.CacheStatus.N_A)
        return _undef.make_or_raise_undef(2)
    if _clans.is_member(mo):
        return _clans.is_right_regular(mo, _checked=False)
    if _multiclans.is_member(mo):
        return _multiclans.is_right_regular(mo, _checked=False)

    # Check higher (not yet defined) algebras.
    if mo.get_ground_set().get_powerset_level(_clans.get_ground_set()) > 0:
        mo_iter = iter(mo)
        elem1 = next(mo_iter)
        if not is_right_regular(elem1):
            mo.cache_right_regular(_mo.CacheStatus.IS_NOT)
            return False
        elem1_rights = elem1.get_rights()
        right_regular = all(
            is_right_regular(elem, _checked=False)
            and elem.get_rights() == elem1_rights for elem in mo_iter)
        mo.cache_right_regular(_mo.CacheStatus.from_bool(right_regular))
        return mo.cached_is_right_regular

    # Nothing applied: 'right-regular' is not defined.
    mo.cache_right_regular(_mo.CacheStatus.N_A)
    return _undef.make_or_raise_undef(2)
Exemplo n.º 7
0
def is_right_regular(mo: _mo.MathObject, _checked: bool=True) -> bool:
    r"""Return whether ``mo`` is :term:`right-regular` or `Undef()` if not applicable.

    Is implemented for :term:`clan`\s, :term:`multiclan`\s and :term:`set`\s of (sets of ...) clans.
    Is also defined (but not yet implemented) for any combination of sets or :term:`multiset`\s
    of clans.
    """
    # pylint: disable=too-many-return-statements
    if _checked:
        if not isinstance(mo, _mo.MathObject):
            return _undef.make_or_raise_undef()

    # Check cache status.
    if mo.cached_right_regular == _mo.CacheStatus.IS:
        return True
    if mo.cached_right_regular == _mo.CacheStatus.IS_NOT:
        return False
    if mo.cached_right_regular == _mo.CacheStatus.N_A:
        return _undef.make_or_raise_undef(2)

    # Check type (right-regular is only defined on Sets and Multisets) and algebra memberships.
    if not mo.is_set and not mo.is_multiset:
        mo.cache_right_regular(_mo.CacheStatus.N_A)
        return _undef.make_or_raise_undef(2)
    if _clans.is_member(mo):
        return _clans.is_right_regular(mo, _checked=False)
    if _multiclans.is_member(mo):
        return _multiclans.is_right_regular(mo, _checked=False)

    # Check higher (not yet defined) algebras.
    if mo.get_ground_set().get_powerset_level(_clans.get_ground_set()) > 0:
        mo_iter = iter(mo)
        elem1 = next(mo_iter)
        if not is_right_regular(elem1):
            mo.cache_right_regular(_mo.CacheStatus.IS_NOT)
            return False
        elem1_rights = elem1.get_rights()
        right_regular = all(
            is_right_regular(elem, _checked=False) and elem.get_rights() == elem1_rights
            for elem in mo_iter)
        mo.cache_right_regular(_mo.CacheStatus.from_bool(right_regular))
        return mo.cached_is_right_regular

    # Nothing applied: 'right-regular' is not defined.
    mo.cache_right_regular(_mo.CacheStatus.N_A)
    return _undef.make_or_raise_undef(2)
Exemplo n.º 8
0
    def __init__(self,
                 clan: 'PP(A x A)',
                 ordered_lefts=None,
                 leftset: 'P( A )' = None):
        """Constructor. Call in the derived class's constructor.

        :param clan: The tabular data to be exported.
        :param ordered_lefts: An optional (ordered) list of left atoms that determines the
            columns to be exported from ``clan`` and their order.
        :param leftset: An optional (unordered) set of left atoms that determines the the columns
            to be exported from ``clan``. If neither ordered_lefts nor lefts is given, all left
            components from ``clan`` are exported.
        """
        # Is set in .export()
        self._file = None

        if not _clans.is_member(clan):
            raise TypeError("'clan' must be a clan")
        self._clan = clan

        if ordered_lefts is not None:
            if leftset is not None:
                raise AssertionError(
                    "Only one of 'ordered_lefts' and 'lefts' may be given")
            if len(ordered_lefts) == 0:
                raise AssertionError(
                    "'ordered_lefts' must contain at least one left component")
            for left in ordered_lefts:
                if not left.is_atom:
                    raise TypeError(
                        "'ordered_lefts' must consist of Atom instances")
            self._ordered_lefts = ordered_lefts
        elif leftset is not None:
            self._ordered_lefts = []
            for left in leftset:
                if not left.is_atom:
                    raise TypeError("'lefts' must consist of Atom instances")
                self._ordered_lefts.append(left)
            if len(self._ordered_lefts) == 0:
                raise AssertionError(
                    "'lefts' must contain at least one left component")
        else:
            lefts = _clans.get_lefts(clan, _checked=False)
            self._ordered_lefts = [left for left in lefts]
Exemplo n.º 9
0
def is_reflexive(mo: _mo.MathObject, _checked: bool = True) -> bool:
    r"""Return whether ``mo`` is :term:`reflexive` or `Undef()` if not applicable.

    Is implemented for :term:`couplet`\s, :term:`relation`\s, :term:`clan`\s, :term:`multiclan`\s
    and  :term:`set`\s of (sets of ...) clans. Is also defined (but not yet implemented) for any
    combination of sets or :term:`multiset`\s of relations.
    """
    # pylint: disable=too-many-return-statements
    if _checked:
        if not isinstance(mo, _mo.MathObject):
            return _undef.make_or_raise_undef()

    # Check cache status.
    if mo.cached_reflexive == _mo.CacheStatus.IS:
        return True
    if mo.cached_reflexive == _mo.CacheStatus.IS_NOT:
        return False
    if mo.cached_reflexive == _mo.CacheStatus.N_A:
        return _undef.make_or_raise_undef(2)

    # Check types and algebra memberships.
    if _couplets.is_member(mo):
        return _couplets.is_reflexive(mo, _checked=False)
    if not mo.is_set and not mo.is_multiset:
        mo.cache_reflexive(_mo.CacheStatus.N_A)
        return _undef.make_or_raise_undef(2)
    if _relations.is_member(mo):
        return _relations.is_reflexive(mo, _checked=False)
    if _clans.is_member(mo):
        return _clans.is_reflexive(mo, _checked=False)
    if _multiclans.is_member(mo):
        return _multiclans.is_reflexive(mo, _checked=False)

    # Check higher (not yet defined) algebras.
    reflexive = _is_powerset_property(mo, _clans.get_ground_set(),
                                      is_reflexive)
    if reflexive is not _undef.Undef():
        mo.cache_reflexive(_mo.CacheStatus.from_bool(reflexive))
        return reflexive

    # Nothing applied: 'reflexive' is not defined.
    mo.cache_reflexive(_mo.CacheStatus.N_A)
    return _undef.make_or_raise_undef(2)
Exemplo n.º 10
0
def match_and_project(graph: 'PP( AxA )', pattern: dict=None, projection: dict=None):
    """Return all relations in ``graph`` that contain all members of ``pattern``. Rename their lefts
    according to the members of ``projection``.

    :param graph: An absolute clan.
    :param pattern: A dictionary where the keys are the lefts and the values the rights that
        will be matched.
    :param projection: A dictionary where the values are the new names and the keys the existing
        names of the lefts to be renamed.
    """
    assert(_clans.is_member(graph))
    if pattern is None:
        pattern = {}
    if projection is None:
        projection = {}

    matches = pattern_match(graph, pattern)
    compose_ctrl_set = _clans.transpose(_clans.from_dict(projection))
    return _clans.compose(matches, compose_ctrl_set, _checked=False)
Exemplo n.º 11
0
def export_csv(absolute_clan_or_multiclan, file_or_path, ordered_lefts=None, sort_key=None):
    r"""Export an absolute clan or absolute multiclan as CSV file with header row.

    The :term:`left component`\s of the :term:`clan` or term:`multiclan` are interpreted as
    column names and are exported as header row. Every :term:`relation` in the input becomes a
    data row in the CSV file.

    :param absolute_clan_or_multiclan: An :term:`absolute clan` or term:`absolute multiclan`. If
        it is not :term:`regular`, ``ordered_lefts`` must be given.
    :param file_or_path: Either a file path (in this case the CSV data is written to a file at this
        location) or a file object (in this case the CSV data is written to its ``.write()``
        function).
    :param ordered_lefts: (Optional) A ``Sequence`` of :term:`left`\s that are exported in the
        given order. Default is the sequence that is the lexically sorted :term:`left set` of the
        (multi)clan. This parameter is required if ``absolute_clan_or_multiclan`` is not
        term:`regular`.
    :param sort_key: (Optional) A function that compares two row-:term:`relation`\s and provides an
        order (for use with :func:`sorted`). The output is not sorted if ``sort_key`` is missing.
    :return: ``True`` if the CSV export succeeded, ``False`` if not.
    """
    if not _clans.is_absolute_member(absolute_clan_or_multiclan) \
            and not _multiclans.is_absolute_member(absolute_clan_or_multiclan):
        return False
    regular_clan = _clans.is_member(absolute_clan_or_multiclan) \
            and _clans.is_regular(absolute_clan_or_multiclan)
    regular_mclan = _multiclans.is_member(absolute_clan_or_multiclan) \
            and _multiclans.is_regular(absolute_clan_or_multiclan)
    if ordered_lefts is None and not (regular_clan or regular_mclan):
        return False

    if ordered_lefts is None:
        # Since this clan is regular, get first relation to acquire left set.
        rel = next(iter(absolute_clan_or_multiclan))
        # left_set is sorted to guarantee consistent iterations
        ordered_lefts = sorted([left.value for left in rel.get_left_set()])

    # Generate dictionaries that associates left components with their right components for each
    # relation.
    clan_as_list_of_dicts = _convert_clan_to_list_of_dicts(
        ordered_lefts, (absolute_clan_or_multiclan if sort_key is None else sorted(absolute_clan_or_multiclan, key=sort_key)))
    # Write the dictionaries.
    _csv_dict_writer(file_or_path, ordered_lefts, clan_as_list_of_dicts)
    return True
Exemplo n.º 12
0
def is_reflexive(mo: _mo.MathObject, _checked: bool=True) -> bool:
    r"""Return whether ``mo`` is :term:`reflexive` or `Undef()` if not applicable.

    Is implemented for :term:`couplet`\s, :term:`relation`\s, :term:`clan`\s, :term:`multiclan`\s
    and  :term:`set`\s of (sets of ...) clans. Is also defined (but not yet implemented) for any
    combination of sets or :term:`multiset`\s of relations.
    """
    # pylint: disable=too-many-return-statements
    if _checked:
        if not isinstance(mo, _mo.MathObject):
            return _undef.make_or_raise_undef()

    # Check cache status.
    if mo.cached_reflexive == _mo.CacheStatus.IS:
        return True
    if mo.cached_reflexive == _mo.CacheStatus.IS_NOT:
        return False
    if mo.cached_reflexive == _mo.CacheStatus.N_A:
        return _undef.make_or_raise_undef(2)

    # Check types and algebra memberships.
    if _couplets.is_member(mo):
        return _couplets.is_reflexive(mo, _checked=False)
    if not mo.is_set and not mo.is_multiset:
        mo.cache_reflexive(_mo.CacheStatus.N_A)
        return _undef.make_or_raise_undef(2)
    if _relations.is_member(mo):
        return _relations.is_reflexive(mo, _checked=False)
    if _clans.is_member(mo):
        return _clans.is_reflexive(mo, _checked=False)
    if _multiclans.is_member(mo):
        return _multiclans.is_reflexive(mo, _checked=False)

    # Check higher (not yet defined) algebras.
    reflexive = _is_powerset_property(mo, _clans.get_ground_set(), is_reflexive)
    if reflexive is not _undef.Undef():
        mo.cache_reflexive(_mo.CacheStatus.from_bool(reflexive))
        return reflexive

    # Nothing applied: 'reflexive' is not defined.
    mo.cache_reflexive(_mo.CacheStatus.N_A)
    return _undef.make_or_raise_undef(2)
Exemplo n.º 13
0
def match_and_project(graph: 'PP(A x A)',
                      pattern: dict = None,
                      projection: dict = None):
    r"""Return all relations in ``graph`` that contain all members of ``pattern``. Rename their
    lefts according to the members of ``projection``.

    :param graph: An absolute :term:`clan`.
    :param pattern: A dictionary where the keys are the :term:`left`\s and the values the
        :term:`right`\s that will be matched.
    :param projection: A dictionary where the values are the new names and the keys the existing
        names of the :term:`left`\s to be renamed.
    """
    assert _clans.is_member(graph)
    if pattern is None:
        pattern = {}
    if projection is None:
        projection = {}

    matches = pattern_match(graph, pattern)
    compose_ctrl_set = _clans.transpose(_clans.from_dict(projection))
    return _clans.compose(matches, compose_ctrl_set, _checked=False)
Exemplo n.º 14
0
    def __init__(self, clan: 'PP(A x A)', ordered_lefts=None, leftset: 'P( A )'=None):
        """Constructor. Call in the derived class's constructor.

        :param clan: The tabular data to be exported.
        :param ordered_lefts: An optional (ordered) list of left atoms that determines the
            columns to be exported from ``clan`` and their order.
        :param leftset: An optional (unordered) set of left atoms that determines the the columns
            to be exported from ``clan``. If neither ordered_lefts nor lefts is given, all left
            components from ``clan`` are exported.
        """
        # Is set in .export()
        self._file = None

        if not _clans.is_member(clan):
            raise TypeError("'clan' must be a clan")
        self._clan = clan

        if ordered_lefts is not None:
            if leftset is not None:
                raise AssertionError("Only one of 'ordered_lefts' and 'lefts' may be given")
            if len(ordered_lefts) == 0:
                raise AssertionError("'ordered_lefts' must contain at least one left component")
            for left in ordered_lefts:
                if not isinstance(left, _mo.Atom):
                    raise TypeError("'ordered_lefts' must consist of Atom instances")
            self._ordered_lefts = ordered_lefts
        elif leftset is not None:
            self._ordered_lefts = []
            for left in leftset:
                if not isinstance(left, _mo.Atom):
                    raise TypeError("'lefts' must consist of Atom instances")
                self._ordered_lefts.append(left)
            if len(self._ordered_lefts) == 0:
                raise AssertionError("'lefts' must contain at least one left component")
        else:
            lefts = _clans.get_lefts(clan, _checked=False)
            self._ordered_lefts = [left for left in lefts]
Exemplo n.º 15
0
def is_graph(obj: _mo.MathObject) -> bool:
    """Return ``True`` if ``obj`` is a `graph`, ``False`` if not."""
    return _clans.is_member(obj) and _check_graph(obj)
Exemplo n.º 16
0
def is_graph(obj: _mo.MathObject) -> bool:
    """Return ``True`` if ``obj`` is a `graph`."""
    return _clans.is_member(obj) and _check_graph(obj)