Abstract base classes

NonExact

The main purpose of the class NonExact is to add tolerance attributes atol and rtol to a class, which can then be used internally in numerical calculations. A simple example:

>>> from ddg.abc import NonExact

>>> class MyNonExactClass(NonExact):
...     pass
...
>>> A = MyNonExactClass(atol=1e-8, rtol=0.)
>>> A.atol
1e-08
>>> A.rtol
0.0

There are global default values for atol and rtol, which are used everywhere if atol or rtol are set to None (the default). These defaults are stored as class attributes of NonExact.

>>> NonExact.atol_default
1e-07
>>> NonExact.rtol_default
0.0
>>> A = MyNonExactClass()
>>> A.atol
1e-07
>>> NonExact.atol_default = 1e-13
>>> A.atol
1e-13
>>> A.atol = 1e-5
>>> A.atol
1e-05
>>> A.atol = None
>>> A.atol
1e-13

Since these class attributes are not in a particularly accessible place, they can also be read and changed via the top-level functions ddg.get_tol_defaults() and ddg.set_tol_defaults() and reset to their original values with ddg.reset_tol_defaults():

>>> import ddg

>>> ddg.get_tol_defaults()
{'atol': 1e-13, 'rtol': 0.0}
>>> ddg.set_tol_defaults(rtol=1e-3)
>>> ddg.get_tol_defaults()
{'atol': 1e-13, 'rtol': 0.001}

For simple utility functions which are not members of a class, we can use the decorator ddg.abc.NonExact.nonexact_function(), which is realized as a class method. If a function decorated with this method receives None as atol or rtol, it will be replaced by the global default. The function can still set its own defaults.

>>> import numpy as np

>>> @NonExact.nonexact_function
... def is_almost_zero(a, atol=None, rtol=0.0):
...     return np.isclose(a, 0, atol=atol, rtol=rtol)
...
>>> is_almost_zero(1e-14)
True
>>> is_almost_zero(1e-12)
False
>>> is_almost_zero(1e-14, atol=1e-15)
False
>>> ddg.set_tol_defaults(atol=1e-15)
>>> is_almost_zero(1e-14)
False