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

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 data types. 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