from collections import deque
from functools import reduce, wraps
import inspect
import numpy as np
from ddg.math._functools import compose
[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]