Source code for dantro.mixins.numeric

"""This module implements mixin classes which provide numeric interfaces for
containers
"""

import logging
import math
import operator

import numpy as np

from ..abc import AbstractDataContainer

# Local variables
log = logging.getLogger(__name__)

# -----------------------------------------------------------------------------


[docs]class UnaryOperationsMixin: """This Mixin class implements the methods needed for unary operations. It leaves out those that expect that return values are of a certain type, e.g. ``__complex__``, ``__int__``, ... """
[docs] def __neg__(self): """Make negative Returns: A new object with negative elements """ return apply_func_to_copy(self, operator.neg)
[docs] def __pos__(self): """Make positive Returns: A new object with negative elements """ return apply_func_to_copy(self, operator.pos)
[docs] def __abs__(self): """Absolute value Returns: A new object with the absolute value of the elements """ return apply_func_to_copy(self, operator.abs)
[docs] def __invert__(self): """Inverse value Returns: A new object with the inverted values of the elements """ return apply_func_to_copy(self, operator.invert)
[docs] def __round__(self): """Rounds number to nearest integer Returns: A new object as rounded number to nearest integer """ return apply_func_to_copy(self, round)
[docs] def __ceil__(self): """Smallest integer Returns: A new object containing the smallest integer """ return apply_func_to_copy(self, math.ceil)
[docs] def __floor__(self): """Largest integer Returns: A new object containing the largest element """ return apply_func_to_copy(self, math.floor)
[docs] def __trunc__(self): """Truncated to the nearest integer toward 0 Returns: A new object containing the truncated element """ return apply_func_to_copy(self, math.trunc)
[docs]class NumbersMixin(UnaryOperationsMixin): """This mixin implements the methods needed for calculating with numbers."""
[docs] def __add__(self, other): """Add two objects Returns: A new object containing the summed data """ return apply_func_to_copy(self, operator.add, other)
[docs] def __sub__(self, other): """Subtract two objects Returns: A new object containing the subtracted data """ return apply_func_to_copy(self, operator.sub, other)
[docs] def __mul__(self, other): """Multiply two objects Returns: A object containing the multiplied data """ return apply_func_to_copy(self, operator.mul, other)
[docs] def __truediv__(self, other): """Divide two objects Returns: A new object containing the divided data """ return apply_func_to_copy(self, operator.truediv, other)
[docs] def __floordiv__(self, other): """Floor divide two objects Returns: A new object containing the floor divided data """ return apply_func_to_copy(self, operator.floordiv, other)
[docs] def __mod__(self, other): """Calculate the modulo of two objects Returns: A new object containing the summed data """ return apply_func_to_copy(self, operator.mod, other)
[docs] def __divmod__(self, other): """Calculate the floor division and modulo of two objects Returns: A new object containing the floor divided data and its modulo """ return apply_func_to_copy(self, divmod, other)
[docs] def __pow__(self, other): """Calculate the self data to the power of other data Returns: A new object containing the result """ return apply_func_to_copy(self, operator.pow, other)
# inplace operations
[docs] def __iadd__(self, other): """Add two objects Returns: Self with modified data """ return apply_func_inplace(self, operator.iadd, other)
[docs] def __isub__(self, other): """Subtract two objects Returns: Self with modified data """ return apply_func_inplace(self, operator.isub, other)
[docs] def __imul__(self, other): """Multiply two objects Returns: Self with modified data """ return apply_func_inplace(self, operator.imul, other)
[docs] def __itruediv__(self, other): """Divide two objects Returns: Self with modified data """ return apply_func_inplace(self, operator.itruediv, other)
[docs] def __ifloordiv__(self, other): """Floor divide two objects Returns: Self with modified data """ return apply_func_inplace(self, operator.ifloordiv, other)
[docs] def __imod__(self, other): """Calculate the modulo of two objects Returns: Self with modified data """ return apply_func_inplace(self, operator.imod, other)
[docs] def __ipow__(self, other): """Calculate the self data to the power of other data Returns: Self with modified data """ return apply_func_inplace(self, operator.ipow, other)
[docs]class ComparisonMixin: """This Mixin implements functions to compare objects"""
[docs] def __eq__(self, other): """Equality""" return self.data == get_data(other)
[docs] def __ne__(self, other): """Inequality""" return self.data != get_data(other)
[docs] def __lt__(self, other): """Less than""" return self.data < get_data(other)
[docs] def __le__(self, other): """Less than or equal""" return self.data <= get_data(other)
[docs] def __gt__(self, other): """Greater than""" return self.data > get_data(other)
[docs] def __ge__(self, other): """Greater than or equal""" return self.data >= get_data(other)
[docs] def __bool__(self): """Truth value""" return bool(self.data)
# ----------------------------------------------------------------------------- # Helpers --------------------------------------------------------------------- # -----------------------------------------------------------------------------
[docs]def get_data(obj): """Get the data of `obj` depending on whether it is part of dantro or not. Args: obj: The object to check Returns: Either the `.data` attribute of a dantro-based object or otherwise the object itself. """ if isinstance(obj, AbstractDataContainer): return obj.data # Not dantro-based, just return the object itself. return obj
[docs]def apply_func_to_copy(obj, func, other=None): """Apply a given function to a copy for all datatypes Returns: An object with the data on which the function was applied """ # Work on a copy new = obj.copy() # Change the data of the new object if other is None: new._data = func(new.data) else: if isinstance(other, AbstractDataContainer): new._data = func(new.data, other.data) else: new._data = func(new.data, other) return new
[docs]def apply_func_inplace(obj, func, other=None): """Apply a given function inplace for all datatypes Returns: An object with the data on which the function was applied """ # Change the data of the new object if other is None: func(obj._data) else: if isinstance(other, AbstractDataContainer): func(obj._data, other.data) else: func(obj._data, other) return obj