Source code for algebraical.algebraical

"""
Subclass of the
`built-in function type <https://docs.python.org/3/library/operator.html>`__
for representing algebraic operators (that are typically associated with
algebraic structures and algebraic circuits) as immutable, hashable, sortable,
and callable objects.

Instances of the class exported by this library can be used as gate operations
within circuits as they are implemented within the
`circuit <https://pypi.org/project/circuit>`__ library. This library is intended
to complement the `logical <https://pypi.org/project/logical>`__ library for
logical operations.
"""
from __future__ import annotations
from typing import Any, Tuple
import doctest
import operator

[docs]class algebraical(type(operator)): """ Class for representing algebraic operators. This class is derived from the type of the built-in functions found in the :obj:`operator` library. Thus, it is possible to invoke these operators on values of `numeric <https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex>`__ types and on objects that define the special `methods <https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types>`__ associated with these built-in operators. >>> algebraical.add_(1, 2) 3 The name and arity of an instance can be retrieved. >>> algebraical.mul_.name() 'mul' >>> algebraical.mul_.arity() 2 Instances can be compared according to their precedence. >>> pow_ > mul_ True >>> pow_ < add_ False >>> sorted([pow_, mul_, add_]) # From lowest to highest precedence. [add_, mul_, pow_] Instances are also hashable and can be used as members of :obj:`set` instances and as keys within :obj:`dict` instances. >>> from algebraical import * >>> {add_, add_, add_} {add_} >>> sorted({add_: 0, mul_: 1}.items()) [(add_, 0), (mul_, 1)] """ names: dict = None """Typical concise names for operators.""" arities: dict = None """Arities of operators.""" def __init__( self: algebraical, function: operator, # pylint: disable=redefined-outer-name name: str ): """ Create an instance given a built-in operator object. """ super().__init__(name) # Store the original function for use in :obj:`__call__`. self.function = function
[docs] def __call__( self: algebraical, *arguments: Tuple[Any, ...] ) -> Any: """ Apply the function represented by this instance to zero or more arguments. >>> algebraical.add_(1, 2) 3 """ return self.function(*arguments)
[docs] def name(self: algebraical) -> str: """ Return the canonical concise name for this operator. >>> algebraical.mul_.name() 'mul' """ return algebraical.names[self] # pylint: disable=unsubscriptable-object
[docs] def arity(self: algebraical) -> int: """ Return the arity of this operator. >>> algebraical.mul_.arity() 2 >>> algebraical.neg_.arity() 1 """ return algebraical.arities[self] # pylint: disable=unsubscriptable-object
[docs] def __repr__(self: algebraical) -> str: """ String representation of this instance. >>> algebraical.mul_ mul_ """ return algebraical.names[self] + '_' # pylint: disable=unsubscriptable-object
[docs] def __str__(self: algebraical) -> str: """ String representation of this instance. >>> str(algebraical.mul_) 'mul_' """ return repr(self)
def _precedence(self: algebraical): """ Return an integer that represents the precedence of an operator (with a higher integer representing a higher precedence). """ if self == algebraical.abs_: return 3 if self == algebraical.pow_: return 2 if self in ( algebraical.mul_, algebraical.matmul_, algebraical.truediv_, algebraical.floordiv_, algebraical.mod_ ): return 1 return 0
[docs] def __lt__(self: algebraical, other: algebraical) -> bool: """ Compare two operators according to their precedence, where an operator with lower precedence is *less than* an operator with higher precedence. >>> add_ < mul_ True >>> add_ < pow_ True >>> pow_ < mul_ False >>> add_ > abs_ False Operators that have the same precedence are not *less than* one another. >>> mul_ < mul_ False >>> sub_ > add_ False """ return self._precedence() < other._precedence()
[docs] def __le__(self: algebraical, other: algebraical) -> bool: """ Compare two operators according to their precedence, where an operator with lower precedence is *less than or equal to* an operator with higher precedence. >>> add_ <= mul_ True >>> add_ <= pow_ True >>> mul_ >= pow_ False >>> mul_ >= mul_ True """ return self._precedence() <= other._precedence()
pos_: algebraical = None """ Identity operator. >>> pos_(2) 2 """ neg_: algebraical = None """ Negation operator. >>> neg_(2) -2 """ abs_: algebraical = None """ Absolute value operator. >>> abs(-2) 2 """ add_: algebraical = None """ Addition operator. >>> add_(1, 2) 3 """ sub_: algebraical = None """ Subtraction operator. >>> sub_(3, 2) 1 """ mul_: algebraical = None """ Multiplication operator. >>> mul_(2, 3) 6 """ matmul_: algebraical = None """ Alternative multiplication operator. >>> class free(tuple): ... def __matmul(self, other): ... return free((self, other)) >>> isinstance(matmul_(free(), free()), free) True """ truediv_: algebraical = None """ Division operator. >>> truediv_(4, 2) 2 """ floordiv_: algebraical = None """ Integer division operator. >>> floordiv_(3, 2) 1 """ mod_: algebraical = None """ Modulus operator. >>> mod_(7, 4) 3 """ pow_: algebraical = None """ Exponentiation operator. >>> pow_(2, 3) 8 """
# All operators as named class constants. algebraical.pos_ = algebraical(operator.pos, 'pos') algebraical.neg_ = algebraical(operator.neg, 'neg') algebraical.abs_ = algebraical(operator.abs, 'abs') algebraical.add_ = algebraical(operator.add, 'add') algebraical.sub_ = algebraical(operator.add, 'sub') algebraical.mul_ = algebraical(operator.mul, 'mul') algebraical.matmul_ = algebraical(operator.matmul, 'matmul') algebraical.truediv_ = algebraical(operator.truediv, 'truediv') algebraical.floordiv_ = algebraical(operator.floordiv, 'floordiv') algebraical.mod_ = algebraical(operator.mod, 'mod') algebraical.pow_ = algebraical(operator.pow, 'pow') # All operators as top-level constants. pos_: algebraical = algebraical.pos_ neg_: algebraical = algebraical.neg_ abs_: algebraical = algebraical.abs_ add_: algebraical = algebraical.add_ sub_: algebraical = algebraical.sub_ mul_: algebraical = algebraical.mul_ matmul_: algebraical = algebraical.matmul_ truediv_: algebraical = algebraical.truediv_ floordiv_: algebraical = algebraical.floordiv_ mod_: algebraical = algebraical.mod_ pow_: algebraical = algebraical.pow_ # Operator names. algebraical.names: dict = { algebraical.pos_: 'pos', algebraical.neg_: 'neg', algebraical.abs_: 'abs', algebraical.add_: 'add', algebraical.sub_: 'sub', algebraical.mul_: 'mul', algebraical.matmul_: 'matmul', algebraical.truediv_: 'truediv', algebraical.floordiv_: 'floordiv', algebraical.mod_: 'mod', algebraical.pow_: 'pow' } # Operator arities. algebraical.arities: dict = { algebraical.pos_: 1, algebraical.neg_: 1, algebraical.abs_: 1, algebraical.add_: 2, algebraical.sub_: 2, algebraical.mul_: 2, algebraical.matmul_: 2, algebraical.truediv_: 2, algebraical.floordiv_: 2, algebraical.mod_: 2, algebraical.pow_: 2 } if __name__ == '__main__': doctest.testmod() # pragma: no cover