Source code for ddg.abc

from collections import deque
from functools import reduce, wraps
import inspect
import numpy as np
from ddg.math._functools import compose


[docs]class Transformable: """Makes a class transformable by keeping a stack of transformations. It is assumed that the transformations are functions. If a matrix is passed to push_transformation, it will be converted to a function. """ def __init__(self): self._trafos = deque()
[docs] def push_transformation(self, f): """Add a transformation to the trafo-stack. Parameters ---------- f : numpy.ndarray or function """ if isinstance(f, np.ndarray): self._trafos.appendleft(lambda x : np.dot(f, x)) else: self._trafos.appendleft(f)
[docs] def pop_transformation(self): """Pop transformation from the trafo-stack. Returns ------- function """ return self._trafos.popleft()
[docs] def transform(self, f): """Wrapper for `push_transformation`. See Also -------- push_transformation """ self.push_transformation(f)
@property def transformation(self): """Composition of all transformations on the stack. Returns ------- function """ return compose(*self._trafos)
[docs]class LinearTransformable(Transformable): """Makes a class transformable by keeping a stack of transformations. It is assumed that the transformations are matrices. Attributes ---------- trafo_dimension : int An empty transformation stack returns np.eye(_trafo_dimension). """ def __init__(self, trafo_dimension): self.trafo_dimension = trafo_dimension super().__init__()
[docs] def push_transformation(self, f): """Add a transformation to the trafo-stack. Parameters ---------- f : numpy.ndarray """ if not isinstance(f, np.ndarray): raise TypeError("Transformation should be a matrix.") self._trafos.appendleft(f)
@property def transformation(self): """Product of all matrices on the stack. Returns ------- numpy.ndarray """ return reduce(np.dot, self._trafos, np.eye(self.trafo_dimension))
[docs]class NonExact: """Adds tolerance attributes to a class. Parameters ---------- atol : float (default=None) Absolute tolerance rtol : float (default=None) Relative tolerance """ _original_atol_default = 1e-7 # used for resetting atol_default = _original_atol_default """Absolute tolerance default. Used for all purposes if `atol` is None. """ _original_rtol_default = 0.0 # used for resetting rtol_default = _original_rtol_default """Relative tolerance default. Used for all purposes if `rtol` is None. """ def __init__(self, atol=None, rtol=None): self._atol = atol self._rtol = rtol @property def atol(self): if self._atol is None: return self.atol_default else: return self._atol @atol.setter def atol(self, value): self._atol = value @property def rtol(self): if self._rtol is None: return self.rtol_default else: return self._rtol @rtol.setter def rtol(self, value): self._rtol = value
[docs] @classmethod def reset_tol_defaults(cls): cls.atol_default = cls._original_atol_default cls.rtol_default = cls._original_rtol_default
[docs] @classmethod def nonexact_function(cls, f): """Decorator to use global tolerance defaults in functions. If None is given to the wrapped function as the value for `atol` or `rtol`, it will be replaced by the global default. Parameters ---------- f : callable Function that takes `atol` and `rtol` arguments. Returns ------- callable """ @wraps(f) def wrapped(*args, **kwargs): # Using the signature object instead of just the kwargs dictionary # allows atol and rtol to still be passed as both positional and # keyword arguments. sgn = inspect.signature(f).bind(*args, **kwargs) sgn.apply_defaults() # Checking if the function takes atol/rtol parameters in the first # place allows functions to only take one of the two. if 'atol' in sgn.arguments and sgn.arguments['atol'] is None: sgn.arguments['atol'] = cls.atol_default if 'rtol' in sgn.arguments and sgn.arguments['rtol'] is None: sgn.arguments['rtol'] = cls.rtol_default return f(*sgn.args, **sgn.kwargs) return wrapped
[docs]def get_tol_defaults(): """Get current global tolerance defaults. Returns ------- dict {'atol': float, 'rtol': float} Notes ----- The defaults are stored in the class attributes ddg.abc.NonExact.atol_default and ddg.abc.NonExact.rtol_default. This function is just meant to be elevated to a higher level of the library to make them more accessible. """ return {'atol': NonExact.atol_default, 'rtol': NonExact.rtol_default}
[docs]def set_tol_defaults(atol=None, rtol=None): """Set global tolerance defaults. Parameters ---------- atol : float (default=None) rtol : float (default=None) Notes ----- The defaults are stored in the class attributes ddg.abc.NonExact.atol_default and ddg.abc.NonExact.rtol_default. This function is just meant to be elevated to a higher level of the library to make them more accessible. """ if atol is not None: NonExact.atol_default = atol if rtol is not None: NonExact.rtol_default = rtol
[docs]def reset_tol_defaults(): """Reset global tolerance defaults to their original values. Notes ----- The defaults are stored in the class attributes ddg.abc.NonExact.atol_default and ddg.abc.NonExact.rtol_default. This function is just meant to be elevated to a higher level of the library to make them more accessible. """ NonExact.reset_tol_defaults()
[docs]class Singleton(type): """Singleton metaclass """ _instances = {} def __call__(cls, *args, **kwargs): if not cls in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls]