def object_hook_f(obj): """``obj`` is a representation of a straightforward translation of JSON into Python. For a known special construct (must be a ``dict``), convert it into its correct object representation and return it. Otherwise return ``obj`` as-is. (May be used as ``object_hook`` function for the various JSON decoder APIs.) """ if len(obj) == 2 and '__cls__' in obj and '__val__' in obj: if obj['__cls__'] == 'builtin.bytes': return bytes(elem for elem in obj['__val__']) if obj['__cls__'] == 'builtin.complex': return complex(obj['__val__']) if obj['__cls__'] == 'builtin.frozenset': return frozenset(decode(elem) for elem in obj['__val__']) if obj['__cls__'] == 'builtin.list': return list(decode(elem) for elem in obj['__val__']) if obj['__cls__'] == 'builtin.range': return range(decode(obj['__val__'][0]), decode(obj['__val__'][1]), decode(obj['__val__'][2])) if obj['__cls__'] == 'builtin.tuple': return tuple(decode(elem) for elem in obj['__val__']) if obj['__cls__'] == 'Atom': return _mo.Atom(decode(obj['__val__']), direct_load=True) if obj['__cls__'] == 'Couplet': return _mo.Couplet(left=decode(obj['__val__'][0]), right=decode(obj['__val__'][1]), direct_load=True) if obj['__cls__'] == 'Set': return _mo.Set((decode(elem) for elem in obj['__val__']), direct_load=True) if obj['__cls__'] == 'Multiset': return _mo.Multiset( {decode(val): mult for val, mult in obj['__val__']}, direct_load=True) return obj
def order_slice(mclan: 'P(P(M x M) x N)', less_than_f, offset: '( A )', limit: '( A )', _checked: bool = True) -> 'P(P(M x M) x N)': r"""Return a multiclan that contains the relations with indices ``offset <= index < offset + limit``, after having been ordered according to ``order``. :param mclan: The source data. Must be a :term:`multiclan`. :param less_than_f: A function that accepts two :term:`relation`\s as arguments and returns ``True`` if the first one is less than the second one. :param offset: An :term:`atom` with an integer value that indicates the index of the first relation (after sorting the multiclan) in the result. Set to ``Atom(0)`` if you want to start with the first relation of the sorted multiclan. :param limit: An atom with an integer value that indicates how many relations should be in the resulting multiclan. When ``limit`` is ``float('inf')``, all relations are returned. """ tuple_list_generator = order_slice_to_listgen(mclan, less_than_f, offset, limit, _checked) if tuple_list_generator is _undef.Undef(): return _undef.Undef() mclan = _mo.Multiset({rel: mult for (rel, mult) in tuple_list_generator}, direct_load=True) return mclan
def make_labeled_partition(set_or_mset, class_invariant_func): r"""Return a 'labeled' partition of ``set_or_mset``, partitioned according to ``class_invariant_func``. :param set_or_mset: The :term:`set` or :term:`multiset` that is to be partitioned. :param class_invariant_func: A function from elements of ``set_or_mset`` to `MathObject`\s. It defines an :term:`equivalence relation` on ``set_or_mset`` such that .. math:: x, y \in set\_or\_mset : x \equiv y \iff class\_invariant\_func(x) = class\_invariant\_func(y) :return: A :term:`function` with structure :math:`P(range(class\_invariant\_func) \times P(set\_or\_mset.ground\_set))` that maps the range of ``class_invariant_func`` when applied to ``set_or_mset`` to sets of elements of ``set_or_mset`` that belong to the given equivalence class. """ if set_or_mset.is_set: partition_dict = _create_partition_dict_from_set(set_or_mset, class_invariant_func) return _mo.Set((_mo.Couplet(label, _mo.Set(components, direct_load=True) .cache_clan(set_or_mset.cached_clan), direct_load=True) for label, components in partition_dict.items()), direct_load=True) elif set_or_mset.is_multiset: partition_dict = _create_partition_dict_from_multiset(set_or_mset, class_invariant_func) return _mo.Set((_mo.Couplet(label, _mo.Multiset(counter, direct_load=True) .cache_multiclan(set_or_mset.cached_multiclan), direct_load=True) for label, counter in partition_dict.items()), direct_load=True) else: raise AssertionError('First argument must be Set or Multiset')
def partition(set_or_mset, class_invariant_func): r"""Return ``set_or_mset`` partitioned according to ``class_invariant_func``. :param set_or_mset: The :term:`set` or :term:`multiset` that is to be partitioned. :param class_invariant_func: A function from elements of ``set_or_mset`` to `MathObject`\s. It defines an :term:`equivalence relation` on ``set_or_mset`` such that .. math:: x, y \in set\_or\_mset : x \equiv y \iff class\_invariant\_func(x) = class\_invariant\_func(y) :return: A set with structure :math:`P(set\_or\_mset.ground\_set)` that defines a partition on ``set_or_mset``, imposed by the equivalence relation defined by the function ``class_invariant_func``. """ if set_or_mset.is_set: partition_dict = _create_partition_dict_from_set(set_or_mset, class_invariant_func) # Create the resulting Set of Sets from the partition components. return _mo.Set((_mo.Set(components, direct_load=True) .cache_clan(set_or_mset.cached_clan) for components in partition_dict.values()), direct_load=True) elif set_or_mset.is_multiset: partition_dict = _create_partition_dict_from_multiset(set_or_mset, class_invariant_func) # Create the resulting Set of Multisets from the partition components. return _mo.Set((_mo.Multiset(counter, direct_load=True) .cache_multiclan(set_or_mset.cached_multiclan) for counter in partition_dict.values()), direct_load=True) else: raise AssertionError('First argument must be Set or Multiset')
def intersect(multiset1: 'P( M x N )', multiset2: 'P( M x N )', _checked=True) -> 'P( M x N )': """Return the multiset intersection of ``multiset1`` with ``multiset2``. :return: The :term:`multiset intersection` of ``multiset1`` and ``multiset2`` or `Undef()` if ``multiset1`` or ``multiset2`` are not instances of :class:`~.Multiset`. """ if _checked: if not is_member(multiset1): return _undef.make_or_raise_undef2(multiset1) if not is_member(multiset2): return _undef.make_or_raise_undef2(multiset2) else: assert is_member_or_undef(multiset1) assert is_member_or_undef(multiset2) if multiset1 is _undef.Undef() or multiset2 is _undef.Undef(): return _undef.make_or_raise_undef(2) values = multiset1.data & multiset2.data result = _mo.Multiset(values) if not result.is_empty: # Multiclan flags: if multiset1.cached_is_multiclan or multiset2.cached_is_multiclan: result.cache_multiclan(CacheStatus.IS) if multiset1.cached_is_absolute or multiset2.cached_is_absolute: result.cache_absolute(CacheStatus.IS) if multiset1.cached_is_functional or multiset2.cached_is_functional: result.cache_functional(CacheStatus.IS) if multiset1.cached_is_right_functional or multiset2.cached_is_right_functional: result.cache_right_functional(CacheStatus.IS) if multiset1.cached_is_regular or multiset2.cached_is_regular: result.cache_regular(CacheStatus.IS) if multiset1.cached_is_right_regular or multiset2.cached_is_right_regular: result.cache_right_regular(CacheStatus.IS) return result
def from_dict(dict1: dict) -> 'P(P(M x M) x N)': r"""Return a :term:`multiclan` with a single :term:`relation` where the :term:`couplet`\s are the elements of ``dict1``.""" rel = _relations.from_dict(dict1) mclan = _mo.Multiset(rel, direct_load=True) mclan.cache_multiclan(_mo.CacheStatus.IS) mclan.cache_functional(_mo.CacheStatus.IS) mclan.cache_regular(_mo.CacheStatus.IS) return mclan
def diag(*args, _checked=True) -> 'P(P(M x M) x N)': """Return a multiclan diagonal of the arguments. :param args: Pass in the elements from which the :term:`clan diagonal` is formed. (If you want to pass in an iterable, you need to prefix it with an asterisk ``*``.) """ rels = _relations.diag(*args, _checked=_checked) if rels is _undef.Undef(): return _undef.make_or_raise_undef(2) clan = _mo.Multiset(rels, direct_load=True) clan.cache_multiclan(CacheStatus.IS) clan.cache_functional(CacheStatus.IS).cache_right_functional( CacheStatus.IS) clan.cache_reflexive(CacheStatus.IS).cache_symmetric(CacheStatus.IS) clan.cache_regular(CacheStatus.IS).cache_right_regular(CacheStatus.IS) return clan
def union(multiset1: 'P( M x N )', multiset2: 'P( M x N )', _checked=True) -> 'P( M x N )': """Return the multiset union of ``multiset1`` with ``multiset2``. :return: The :term:`multiset union` of ``multiset1`` and ``multiset2`` or `Undef()` if ``multiset1`` or ``multiset2`` are not instances of :class:`~.Multiset`. """ # pylint: disable=too-many-branches if _checked: if not is_member(multiset1): return _undef.make_or_raise_undef2(multiset1) if not is_member(multiset2): return _undef.make_or_raise_undef2(multiset2) else: assert is_member_or_undef(multiset1) assert is_member_or_undef(multiset2) if multiset1 is _undef.Undef() or multiset2 is _undef.Undef(): return _undef.make_or_raise_undef(2) values = multiset1.data | multiset2.data result = _mo.Multiset(values, direct_load=True) if not result.is_empty: # Multiclan flags: if multiset1.cached_is_multiclan and multiset2.cached_is_multiclan: result.cache_multiclan(CacheStatus.IS) if multiset1.cached_is_absolute and multiset2.cached_is_absolute: result.cache_absolute(CacheStatus.IS) elif multiset1.cached_is_not_absolute or multiset2.cached_is_not_absolute: result.cache_absolute(CacheStatus.IS_NOT) if multiset1.cached_is_functional and multiset2.cached_is_functional: result.cache_functional(CacheStatus.IS) elif multiset1.cached_is_not_functional or multiset2.cached_is_not_functional: result.cache_functional(CacheStatus.IS_NOT) if multiset1.cached_is_right_functional and multiset2.cached_is_right_functional: result.cache_right_functional(CacheStatus.IS) elif multiset1.cached_is_not_right_functional \ or multiset2.cached_is_not_right_functional: result.cache_right_functional(CacheStatus.IS_NOT) if multiset1.cached_is_not_regular or multiset1.cached_is_not_regular: result.cache_regular(CacheStatus.IS_NOT) if multiset1.cached_is_not_right_regular or multiset1.cached_is_not_right_regular: result.cache_right_regular(CacheStatus.IS_NOT) elif multiset1.cached_is_not_multiclan or multiset2.cached_is_not_multiclan: result.cache_multiclan(CacheStatus.IS_NOT) return result
def unary_multi_extend(set_or_mset, op, _checked=True) -> 'P( M x N )': r"""Return the :term:`unary extension` of ``op`` from one :term:`algebra` to another algebra. For this extension, the elements of the extended algebra must be :term:`multiset`\s of the elements of the original algebra. :param set_or_mset: A :term:`set` or a :term:`multiset` with elements on which ``op`` operates. :param op: A :term:`unary operation` that operates on the elements of ``set_or_mset``. :return: A set that consists of the defined results of ``op`` when executed on the elements of ``set_or_mset``, or `Undef()` if ``set_or_mset`` is neither a set nor a multiset. """ if _checked: if not _multisets.is_member(set_or_mset) and not _sets.is_member( set_or_mset): return _undef.make_or_raise_undef2(set_or_mset) else: assert _multisets.is_member(set_or_mset) or _sets.is_member(set_or_mset) \ or set_or_mset is _undef.Undef() if set_or_mset is _undef.Undef(): return _undef.make_or_raise_undef(2) def _get_values_set(set_): result_counter = _collections.Counter() for elem in set_: result = op(elem) if result is not _undef.Undef(): result_counter[result] += 1 return result_counter def _get_values_multiset(mset): result_counter = _collections.Counter() for elem, multiplicity in mset.data.items(): result = op(elem) if result is not _undef.Undef(): result_counter[result] += multiplicity return result_counter get_values = _get_values_multiset if _multisets.is_member( set_or_mset) else _get_values_set return _mo.Multiset(get_values(set_or_mset))
def binary_multi_extend(multiset1: 'P( M x N )', multiset2: 'P( M x N )', op, _checked=True) -> 'P( M x N )': r"""Return the :term:`binary extension` of ``op`` from one :term:`algebra` to another algebra. For this extension, the elements of the extended algebra must be :term:`multiset`\s of the elements of the original algebra. :param multiset1: A :term:`multiset` with elements on which ``op`` operates. :param multiset2: A multiset with elements on which ``op`` operates. :param op: A :term:`binary operation` that operates on the elements of ``multiset1`` and ``multiset2``. :return: A multiset that consists of the defined results of ``op`` when executed on all combinations of the elements of ``multiset1`` and ``multiset2``, or `Undef()` if either set is not a :class:`~.Multiset`. """ if _checked: if not _multisets.is_member(multiset1): return _undef.make_or_raise_undef2(multiset1) if not _multisets.is_member(multiset2): return _undef.make_or_raise_undef2(multiset2) else: assert _multisets.is_member_or_undef(multiset1) assert _multisets.is_member_or_undef(multiset2) if multiset1 is _undef.Undef() or multiset2 is _undef.Undef(): return _undef.make_or_raise_undef(2) def _get_values(_set1, _set2): return_count = _collections.Counter() for elem1, multi1 in _set1.data.items(): for elem2, multi2 in _set2.data.items(): result = op(elem1, elem2) if result is not _undef.Undef(): return_count[result] += multi1 * multi2 return return_count return _mo.Multiset(_get_values(multiset1, multiset2), direct_load=True)
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(_mo.CacheStatus.N_A) result.cache_right_functional(_mo.CacheStatus.N_A) result.cache_reflexive(_mo.CacheStatus.N_A) result.cache_symmetric(_mo.CacheStatus.N_A) result.cache_transitive(_mo.CacheStatus.N_A) result.cache_regular(_mo.CacheStatus.N_A) result.cache_right_regular(_mo.CacheStatus.N_A) return result
def import_csv(csv_file_or_filepath, types: {} = None, skip_rows: int = 0, index_column: str = None, has_dup_rows: bool = False, columns: [] = None) -> 'PP( A x M )': r"""Import the file ``csv_file_or_filepath`` as CSV data and return a clan or multiclan. :param csv_file_or_filepath: The file path or file object (for example ``StringIO`` buffer) to import. :param types: (Optional) A dictionary of type conversions. The keys are the column names; the values are functors (or types) that receive the string from the CSV cell and return the value to be imported. Example: ``{'foo': int, 'bar': float}``. By default all values are interpreted as `string`\s. :param skip_rows: (Optional) A number of lines to skip (default 0). Some CSV files have a preamble that can be skipped with this option. :param index_column: (Optional) A name for an index column. (No index column is created if this argument is not specified.) The index starts with 0. (This option is not compatible with the ``has_dup_rows`` option.) :param has_dup_rows: (Optional) If ``True``, allow duplicate rows and return a multiclan instead of a clan. By default, the value is ``False`` and a clan is returned. (This option is not compatible with the option ``index_column``.) :param columns: (Optional) A list of column names. If present, this list is used as the sequence of columns (and all lines in the data are loaded). If missing, the first line of the data must be a header that contains the column names (and this header line is not loaded as data). :return: A :term:`clan` (if ``has_dup_rows is ``False`` or not provided) or a :term:`multiclan` (if ``has_dup_rows`` is ``True``). """ if types is None: types = {} def _filter_row(row): """Remove missing and blank elements from the CSV row.""" for key, val in row.items(): if val is None or val == '': continue yield key, val _util.get_left_cached.left_cache = {} import_csv.regular = True # Set to false if any row is missing one or more values assert ((index_column is not None) & (has_dup_rows is False)) or (index_column is None) def _import_csv(csv_file): for _ in range(0, skip_rows): next(csv_file) reader = _csv.DictReader(csv_file, fieldnames=columns) _index = 0 for row in reader: filtered_row = {key: val for key, val in _filter_row(row)} if import_csv.regular and len(row) != len(filtered_row): import_csv.regular = False for key, val in types.items(): if key in filtered_row: filtered_row[key] = val(filtered_row[key]) if index_column is not None: filtered_row[index_column] = _index _index += 1 yield _mo.Set( (_mo.Couplet(left=_util.get_left_cached(left), right=_mo.Atom(right), direct_load=True) for left, right in filtered_row.items()), direct_load=True)\ .cache_relation(_mo.CacheStatus.IS).cache_functional(_mo.CacheStatus.IS) if hasattr(csv_file_or_filepath, "readlines"): # Support StringIO. if has_dup_rows: return _mo.Multiset(_import_csv(csv_file_or_filepath), direct_load=True).cache_multiclan( _mo.CacheStatus.IS).cache_functional( _mo.CacheStatus.IS).cache_regular( _mo.CacheStatus.from_bool( import_csv.regular)) else: return _mo.Set(_import_csv(csv_file_or_filepath), direct_load=True)\ .cache_clan(_mo.CacheStatus.IS).cache_functional(_mo.CacheStatus.IS)\ .cache_regular(_mo.CacheStatus.from_bool(import_csv.regular)) else: with open(csv_file_or_filepath, encoding='utf-8', errors='ignore') as file: if has_dup_rows: return _mo.Multiset( _import_csv(file), direct_load=True).cache_multiclan( _mo.CacheStatus.IS).cache_functional( _mo.CacheStatus.IS).cache_regular( _mo.CacheStatus.from_bool(import_csv.regular)) else: return _mo.Set(_import_csv(file), direct_load=True)\ .cache_clan(_mo.CacheStatus.IS).cache_functional(_mo.CacheStatus.IS)\ .cache_regular(_mo.CacheStatus.from_bool(import_csv.regular))
# # 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 algebraixlib.algebras.multiclans as _multiclans import algebraixlib.algebras.multisets as _multisets import algebraixlib.mathobjects as _mo from algebraixlib.util.latexprinter import math_object_to_latex as _math_object_to_latex # -------------------------------------------------------------------------------------------------- ms_1 = _mo.Multiset({'a': 3, 'b': 3}) ms_2 = _mo.Multiset({'b': 2, 'c': 1}) # Multiset Union operation Example simple_union = _multisets.union(ms_1, ms_2) print(str(ms_1) + ' UNION ' + str(ms_2)) print('=> EVALUATES TO ' + str(simple_union)) ms_3 = _mo.Multiset({'a': 3, 'b': 3, 'c': 1}) if ms_3 == simple_union: print( "multiset's union takes the max of all the multiples merges the arguments into one " "result multiset.\n") # Multiset Intersect Operation Example