Source code for pint.testsuite.test_umath

from pint import DimensionalityError
from pint.compat import np
from pint.testsuite import QuantityTestCase, helpers

# Following http://docs.scipy.org/doc/numpy/reference/ufuncs.html

if np:
    pi = np.pi


[docs]@helpers.requires_numpy() class TestUFuncs(QuantityTestCase): FORCE_NDARRAY = True @property def qless(self): return np.asarray([1.0, 2.0, 3.0, 4.0]) * self.ureg.dimensionless @property def qs(self): return 8 * self.ureg.J @property def q1(self): return np.asarray([1.0, 2.0, 3.0, 4.0]) * self.ureg.J @property def q2(self): return 2 * self.q1 @property def qm(self): return np.asarray([1.0, 2.0, 3.0, 4.0]) * self.ureg.m @property def qi(self): return np.asarray([1 + 1j, 2 + 2j, 3 + 3j, 4 + 4j]) * self.ureg.m def _test1( self, func, ok_with, raise_with=(), output_units="same", results=None, rtol=1e-6 ): """Test function that takes a single argument and returns Quantity. Parameters ---------- func : function callable. ok_with : iterables of values that work fine. raise_with : iterables of values that raise exceptions. (Default value = ()) output_units : units to be used when building results. 'same': ok_with[n].units (default). is float: ok_with[n].units ** output_units. None: no output units, the result should be an ndarray. Other value will be parsed as unit. results : iterable of results. If None, the result will be obtained by applying func to each ok_with value (Default value = None) rtol : relative tolerance. (Default value = 1e-6) Returns ------- """ if results is None: results = [None] * len(ok_with) for x1, res in zip(ok_with, results): err_msg = "At {} with {}".format(func.__name__, x1) if output_units == "same": ou = x1.units elif isinstance(output_units, (int, float)): ou = x1.units ** output_units else: ou = output_units qm = func(x1) if res is None: res = func(x1.magnitude) if ou is not None: res = self.Q_(res, ou) self.assertQuantityAlmostEqual(qm, res, rtol=rtol, msg=err_msg) for x1 in raise_with: with self.assertRaises( DimensionalityError, msg=f"At {func.__name__} with {x1}" ): func(x1) def _testn(self, func, ok_with, raise_with=(), results=None): """Test function that takes a single argument and returns and ndarray (not a Quantity) Parameters ---------- func : function callable. ok_with : iterables of values that work fine. raise_with : iterables of values that raise exceptions. (Default value = ()) results : iterable of results. If None, the result will be obtained by applying func to each ok_with value (Default value = None) Returns ------- """ self._test1(func, ok_with, raise_with, output_units=None, results=results) def _test1_2o( self, func, ok_with, raise_with=(), output_units=("same", "same"), results=None, rtol=1e-6, ): """Test functions that takes a single argument and return two Quantities. Parameters ---------- func : function callable. ok_with : iterables of values that work fine. raise_with : iterables of values that raise exceptions. (Default value = ()) output_units : tuple of units to be used when building the result tuple. 'same': ok_with[n].units (default). is float: ok_with[n].units ** output_units. None: no output units, the result should be an ndarray. Other value will be parsed as unit. results : iterable of results. If None, the result will be obtained by applying func to each ok_with value (Default value = None) rtol : relative tolerance. (Default value = 1e-6) "same") : Returns ------- """ if results is None: results = [None] * len(ok_with) for x1, res in zip(ok_with, results): err_msg = "At {} with {}".format(func.__name__, x1) qms = func(x1) if res is None: res = func(x1.magnitude) for ndx, (qm, re, ou) in enumerate(zip(qms, res, output_units)): if ou == "same": ou = x1.units elif isinstance(ou, (int, float)): ou = x1.units ** ou if ou is not None: re = self.Q_(re, ou) self.assertQuantityAlmostEqual(qm, re, rtol=rtol, msg=err_msg) for x1 in raise_with: with self.assertRaises(ValueError, msg=f"At {func.__name__} with {x1}"): func(x1) def _test2( self, func, x1, ok_with, raise_with=(), output_units="same", rtol=1e-6, convert2=True, ): """Test function that takes two arguments and return a Quantity. Parameters ---------- func : function callable. x1 : first argument of func. ok_with : iterables of values that work fine. raise_with : iterables of values that raise exceptions. (Default value = ()) output_units : units to be used when building results. 'same': x1.units (default). 'prod': x1.units * ok_with[n].units 'div': x1.units / ok_with[n].units 'second': x1.units * ok_with[n] None: no output units, the result should be an ndarray. Other value will be parsed as unit. rtol : relative tolerance. (Default value = 1e-6) convert2 : if the ok_with[n] should be converted to x1.units. (Default value = True) Returns ------- """ for x2 in ok_with: err_msg = "At {} with {} and {}".format(func.__name__, x1, x2) if output_units == "same": ou = x1.units elif output_units == "prod": ou = x1.units * x2.units elif output_units == "div": ou = x1.units / x2.units elif output_units == "second": ou = x1.units ** x2 else: ou = output_units qm = func(x1, x2) if convert2 and hasattr(x2, "magnitude"): m2 = x2.to(getattr(x1, "units", "")).magnitude else: m2 = getattr(x2, "magnitude", x2) res = func(x1.magnitude, m2) if ou is not None: res = self.Q_(res, ou) self.assertQuantityAlmostEqual(qm, res, rtol=rtol, msg=err_msg) for x2 in raise_with: with self.assertRaises( DimensionalityError, msg=f"At {func.__name__} with {x1} and {x2}" ): func(x1, x2) def _testn2(self, func, x1, ok_with, raise_with=()): """Test function that takes two arguments and return a ndarray. Parameters ---------- func : function callable. x1 : first argument of func. ok_with : iterables of values that work fine. raise_with : iterables of values that raise exceptions. (Default value = ()) Returns ------- """ self._test2(func, x1, ok_with, raise_with, output_units=None)
[docs]@helpers.requires_numpy() class TestMathUfuncs(TestUFuncs): """Universal functions (ufunc) > Math operations http://docs.scipy.org/doc/numpy/reference/ufuncs.html#math-operations add(x1, x2[, out]) Add arguments element-wise. subtract(x1, x2[, out]) Subtract arguments, element-wise. multiply(x1, x2[, out]) Multiply arguments element-wise. divide(x1, x2[, out]) Divide arguments element-wise. logaddexp(x1, x2[, out]) Logarithm of the sum of exponentiations of the inputs. logaddexp2(x1, x2[, out]) Logarithm of the sum of exponentiations of the inputs in base-2. true_divide(x1, x2[, out]) Returns a true division of the inputs, element-wise. floor_divide(x1, x2[, out]) Return the largest integer smaller or equal to the division of the inputs. negative(x[, out]) Returns an array with the negative of each element of the original array. power(x1, x2[, out]) First array elements raised to powers from second array, element-wise. NOT IMPLEMENTED remainder(x1, x2[, out]) Return element-wise remainder of division. mod(x1, x2[, out]) Return element-wise remainder of division. fmod(x1, x2[, out]) Return the element-wise remainder of division. absolute(x[, out]) Calculate the absolute value element-wise. rint(x[, out]) Round elements of the array to the nearest integer. sign(x[, out]) Returns an element-wise indication of the sign of a number. conj(x[, out]) Return the complex conjugate, element-wise. exp(x[, out]) Calculate the exponential of all elements in the input array. exp2(x[, out]) Calculate 2**p for all p in the input array. log(x[, out]) Natural logarithm, element-wise. log2(x[, out]) Base-2 logarithm of x. log10(x[, out]) Return the base 10 logarithm of the input array, element-wise. expm1(x[, out]) Calculate exp(x) - 1 for all elements in the array. log1p(x[, out]) Return the natural logarithm of one plus the input array, element-wise. sqrt(x[, out]) Return the positive square-root of an array, element-wise. square(x[, out]) Return the element-wise square of the input. reciprocal(x[, out]) Return the reciprocal of the argument, element-wise. ones_like(x[, out]) Returns an array of ones with the same shape and type as a given array. Parameters ---------- Returns ------- """ def test_add(self): self._test2(np.add, self.q1, (self.q2, self.qs), (self.qm,)) def test_subtract(self): self._test2(np.subtract, self.q1, (self.q2, self.qs), (self.qm,)) def test_multiply(self): self._test2(np.multiply, self.q1, (self.q2, self.qs), (), "prod") def test_divide(self): self._test2( np.divide, self.q1, (self.q2, self.qs, self.qless), (), "div", convert2=False, ) def test_logaddexp(self): self._test2(np.logaddexp, self.qless, (self.qless,), (self.q1,), "") def test_logaddexp2(self): self._test2(np.logaddexp2, self.qless, (self.qless,), (self.q1,), "div") def test_true_divide(self): self._test2( np.true_divide, self.q1, (self.q2, self.qs, self.qless), (), "div", convert2=False, ) def test_floor_divide(self): self._test2( np.floor_divide, self.q1, (self.q2, self.qs, self.qless), (), "div", convert2=False, ) def test_negative(self): self._test1(np.negative, (self.qless, self.q1), ()) def test_remainder(self): self._test2( np.remainder, self.q1, (self.q2, self.qs, self.qless), (), "same", convert2=False, ) def test_mod(self): self._test2( np.mod, self.q1, (self.q2, self.qs, self.qless), (), "same", convert2=False ) def test_fmod(self): self._test2( np.fmod, self.q1, (self.q2, self.qs, self.qless), (), "same", convert2=False ) def test_absolute(self): self._test1(np.absolute, (self.q2, self.qs, self.qless, self.qi), (), "same") def test_rint(self): self._test1(np.rint, (self.q2, self.qs, self.qless, self.qi), (), "same") def test_conj(self): self._test1(np.conj, (self.q2, self.qs, self.qless, self.qi), (), "same") def test_exp(self): self._test1(np.exp, (self.qless,), (self.q1,), "") def test_exp2(self): self._test1(np.exp2, (self.qless,), (self.q1,), "") def test_log(self): self._test1(np.log, (self.qless,), (self.q1,), "") def test_log2(self): self._test1(np.log2, (self.qless,), (self.q1,), "") def test_log10(self): self._test1(np.log10, (self.qless,), (self.q1,), "") def test_expm1(self): self._test1(np.expm1, (self.qless,), (self.q1,), "") def test_sqrt(self): self._test1(np.sqrt, (self.q2, self.qs, self.qless, self.qi), (), 0.5) def test_square(self): self._test1(np.square, (self.q2, self.qs, self.qless, self.qi), (), 2) def test_reciprocal(self): self._test1(np.reciprocal, (self.q2, self.qs, self.qless, self.qi), (), -1)
[docs]@helpers.requires_numpy() class TestTrigUfuncs(TestUFuncs): """Universal functions (ufunc) > Trigonometric functions http://docs.scipy.org/doc/numpy/reference/ufuncs.html#trigonometric-functions sin(x[, out]) Trigonometric sine, element-wise. cos(x[, out]) Cosine elementwise. tan(x[, out]) Compute tangent element-wise. arcsin(x[, out]) Inverse sine, element-wise. arccos(x[, out]) Trigonometric inverse cosine, element-wise. arctan(x[, out]) Trigonometric inverse tangent, element-wise. arctan2(x1, x2[, out]) Element-wise arc tangent of x1/x2 choosing the quadrant correctly. hypot(x1, x2[, out]) Given the “legs” of a right triangle, return its hypotenuse. sinh(x[, out]) Hyperbolic sine, element-wise. cosh(x[, out]) Hyperbolic cosine, element-wise. tanh(x[, out]) Compute hyperbolic tangent element-wise. arcsinh(x[, out]) Inverse hyperbolic sine elementwise. arccosh(x[, out]) Inverse hyperbolic cosine, elementwise. arctanh(x[, out]) Inverse hyperbolic tangent elementwise. deg2rad(x[, out]) Convert angles from degrees to radians. rad2deg(x[, out]) Convert angles from radians to degrees. Parameters ---------- Returns ------- """ def test_sin(self): self._test1( np.sin, ( np.arange(0, pi / 2, pi / 4) * self.ureg.dimensionless, np.arange(0, pi / 2, pi / 4) * self.ureg.radian, np.arange(0, pi / 2, pi / 4) * self.ureg.mm / self.ureg.m, ), (1 * self.ureg.m,), "", results=(None, None, np.sin(np.arange(0, pi / 2, pi / 4) * 0.001)), ) self._test1( np.sin, (np.rad2deg(np.arange(0, pi / 2, pi / 4)) * self.ureg.degrees,), results=(np.sin(np.arange(0, pi / 2, pi / 4)),), ) def test_cos(self): self._test1( np.cos, ( np.arange(0, pi / 2, pi / 4) * self.ureg.dimensionless, np.arange(0, pi / 2, pi / 4) * self.ureg.radian, np.arange(0, pi / 2, pi / 4) * self.ureg.mm / self.ureg.m, ), (1 * self.ureg.m,), "", results=(None, None, np.cos(np.arange(0, pi / 2, pi / 4) * 0.001)), ) self._test1( np.cos, (np.rad2deg(np.arange(0, pi / 2, pi / 4)) * self.ureg.degrees,), results=(np.cos(np.arange(0, pi / 2, pi / 4)),), ) def test_tan(self): self._test1( np.tan, ( np.arange(0, pi / 2, pi / 4) * self.ureg.dimensionless, np.arange(0, pi / 2, pi / 4) * self.ureg.radian, np.arange(0, pi / 2, pi / 4) * self.ureg.mm / self.ureg.m, ), (1 * self.ureg.m,), "", results=(None, None, np.tan(np.arange(0, pi / 2, pi / 4) * 0.001)), ) self._test1( np.tan, (np.rad2deg(np.arange(0, pi / 2, pi / 4)) * self.ureg.degrees,), results=(np.tan(np.arange(0, pi / 2, pi / 4)),), ) def test_arcsin(self): self._test1( np.arcsin, ( np.arange(0, 0.9, 0.1) * self.ureg.dimensionless, np.arange(0, 0.9, 0.1) * self.ureg.m / self.ureg.m, ), (1 * self.ureg.m,), "radian", ) def test_arccos(self): self._test1( np.arccos, ( np.arange(0, 0.9, 0.1) * self.ureg.dimensionless, np.arange(0, 0.9, 0.1) * self.ureg.m / self.ureg.m, ), (1 * self.ureg.m,), "radian", ) def test_arctan(self): self._test1( np.arctan, ( np.arange(0, 0.9, 0.1) * self.ureg.dimensionless, np.arange(0, 0.9, 0.1) * self.ureg.m / self.ureg.m, ), (1 * self.ureg.m,), "radian", ) def test_arctan2(self): m = self.ureg.m j = self.ureg.J km = self.ureg.km self._test2( np.arctan2, np.arange(0, 0.9, 0.1) * m, ( np.arange(0, 0.9, 0.1) * m, np.arange(0.9, 0.0, -0.1) * m, np.arange(0, 0.9, 0.1) * km, np.arange(0.9, 0.0, -0.1) * km, ), raise_with=np.arange(0, 0.9, 0.1) * j, output_units="radian", ) def test_hypot(self): self.assertTrue( np.hypot(3.0 * self.ureg.m, 4.0 * self.ureg.m) == 5.0 * self.ureg.m ) self.assertTrue( np.hypot(3.0 * self.ureg.m, 400.0 * self.ureg.cm) == 5.0 * self.ureg.m ) with self.assertRaises(DimensionalityError): np.hypot(1.0 * self.ureg.m, 2.0 * self.ureg.J) def test_sinh(self): self._test1( np.sinh, ( np.arange(0, pi / 2, pi / 4) * self.ureg.dimensionless, np.arange(0, pi / 2, pi / 4) * self.ureg.radian, np.arange(0, pi / 2, pi / 4) * self.ureg.mm / self.ureg.m, ), (1 * self.ureg.m,), "", results=(None, None, np.sinh(np.arange(0, pi / 2, pi / 4) * 0.001)), ) self._test1( np.sinh, (np.rad2deg(np.arange(0, pi / 2, pi / 4)) * self.ureg.degrees,), results=(np.sinh(np.arange(0, pi / 2, pi / 4)),), ) def test_cosh(self): self._test1( np.cosh, ( np.arange(0, pi / 2, pi / 4) * self.ureg.dimensionless, np.arange(0, pi / 2, pi / 4) * self.ureg.radian, np.arange(0, pi / 2, pi / 4) * self.ureg.mm / self.ureg.m, ), (1 * self.ureg.m,), "", results=(None, None, np.cosh(np.arange(0, pi / 2, pi / 4) * 0.001)), ) self._test1( np.cosh, (np.rad2deg(np.arange(0, pi / 2, pi / 4)) * self.ureg.degrees,), results=(np.cosh(np.arange(0, pi / 2, pi / 4)),), ) def test_tanh(self): self._test1( np.tanh, ( np.arange(0, pi / 2, pi / 4) * self.ureg.dimensionless, np.arange(0, pi / 2, pi / 4) * self.ureg.radian, np.arange(0, pi / 2, pi / 4) * self.ureg.mm / self.ureg.m, ), (1 * self.ureg.m,), "", results=(None, None, np.tanh(np.arange(0, pi / 2, pi / 4) * 0.001)), ) self._test1( np.tanh, (np.rad2deg(np.arange(0, pi / 2, pi / 4)) * self.ureg.degrees,), results=(np.tanh(np.arange(0, pi / 2, pi / 4)),), ) def test_arcsinh(self): self._test1( np.arcsinh, ( np.arange(0, 0.9, 0.1) * self.ureg.dimensionless, np.arange(0, 0.9, 0.1) * self.ureg.m / self.ureg.m, ), (1 * self.ureg.m,), "radian", ) def test_arccosh(self): self._test1( np.arccosh, ( np.arange(1.0, 1.9, 0.1) * self.ureg.dimensionless, np.arange(1.0, 1.9, 0.1) * self.ureg.m / self.ureg.m, ), (1 * self.ureg.m,), "radian", ) def test_arctanh(self): self._test1( np.arctanh, ( np.arange(0, 0.9, 0.1) * self.ureg.dimensionless, np.arange(0, 0.9, 0.1) * self.ureg.m / self.ureg.m, ), (0.1 * self.ureg.m,), "radian", ) def test_deg2rad(self): self._test1( np.deg2rad, (np.arange(0, pi / 2, pi / 4) * self.ureg.degrees,), (1 * self.ureg.m,), "radians", ) def test_rad2deg(self): self._test1( np.rad2deg, ( np.arange(0, pi / 2, pi / 4) * self.ureg.dimensionless, np.arange(0, pi / 2, pi / 4) * self.ureg.radian, np.arange(0, pi / 2, pi / 4) * self.ureg.mm / self.ureg.m, ), (1 * self.ureg.m,), "degree", results=( None, None, np.rad2deg(np.arange(0, pi / 2, pi / 4) * 0.001) * self.ureg.degree, ), )
[docs]class TestComparisonUfuncs(TestUFuncs): """Universal functions (ufunc) > Comparison functions http://docs.scipy.org/doc/numpy/reference/ufuncs.html#comparison-functions greater(x1, x2[, out]) Return the truth value of (x1 > x2) element-wise. greater_equal(x1, x2[, out]) Return the truth value of (x1 >= x2) element-wise. less(x1, x2[, out]) Return the truth value of (x1 < x2) element-wise. less_equal(x1, x2[, out]) Return the truth value of (x1 =< x2) element-wise. not_equal(x1, x2[, out]) Return (x1 != x2) element-wise. equal(x1, x2[, out]) Return (x1 == x2) element-wise. Parameters ---------- Returns ------- """ def test_greater(self): self._testn2(np.greater, self.q1, (self.q2,), (self.qm,)) def test_greater_equal(self): self._testn2(np.greater_equal, self.q1, (self.q2,), (self.qm,)) def test_less(self): self._testn2(np.less, self.q1, (self.q2,), (self.qm,)) def test_less_equal(self): self._testn2(np.less_equal, self.q1, (self.q2,), (self.qm,)) def test_not_equal(self): self._testn2(np.not_equal, self.q1, (self.q2,), (self.qm,)) def test_equal(self): self._testn2(np.equal, self.q1, (self.q2,), (self.qm,))
[docs]class TestFloatingUfuncs(TestUFuncs): """Universal functions (ufunc) > Floating functions http://docs.scipy.org/doc/numpy/reference/ufuncs.html#floating-functions isreal(x) Returns a bool array, where True if input element is real. iscomplex(x) Returns a bool array, where True if input element is complex. isfinite(x[, out]) Test element-wise for finite-ness (not infinity or not Not a Number). isinf(x[, out]) Test element-wise for positive or negative infinity. isnan(x[, out]) Test element-wise for Not a Number (NaN), return result as a bool array. signbit(x[, out]) Returns element-wise True where signbit is set (less than zero). copysign(x1, x2[, out]) Change the sign of x1 to that of x2, element-wise. nextafter(x1, x2[, out]) Return the next representable floating-point value after x1 in the direction of x2 element-wise. modf(x[, out1, out2]) Return the fractional and integral parts of an array, element-wise. ldexp(x1, x2[, out]) Compute y = x1 * 2**x2. frexp(x[, out1, out2]) Split the number, x, into a normalized fraction (y1) and exponent (y2) fmod(x1, x2[, out]) Return the element-wise remainder of division. floor(x[, out]) Return the floor of the input, element-wise. ceil(x[, out]) Return the ceiling of the input, element-wise. trunc(x[, out]) Return the truncated value of the input, element-wise. Parameters ---------- Returns ------- """ def test_isreal(self): self._testn(np.isreal, (self.q1, self.qm, self.qless)) def test_iscomplex(self): self._testn(np.iscomplex, (self.q1, self.qm, self.qless)) def test_isfinite(self): self._testn(np.isfinite, (self.q1, self.qm, self.qless)) def test_isinf(self): self._testn(np.isinf, (self.q1, self.qm, self.qless)) def test_isnan(self): self._testn(np.isnan, (self.q1, self.qm, self.qless)) def test_signbit(self): self._testn(np.signbit, (self.q1, self.qm, self.qless)) def test_copysign(self): self._test2(np.copysign, self.q1, (self.q2, self.qs), (self.qm,)) def test_nextafter(self): self._test2(np.nextafter, self.q1, (self.q2, self.qs), (self.qm,)) def test_modf(self): self._test1_2o(np.modf, (self.q2, self.qs)) def test_ldexp(self): x1, x2 = np.frexp(self.q2) self._test2(np.ldexp, x1, (x2,)) def test_frexp(self): self._test1_2o(np.frexp, (self.q2, self.qs), output_units=("same", None)) def test_fmod(self): # See TestMathUfuncs.test_fmod pass def test_floor(self): self._test1(np.floor, (self.q1, self.qm, self.qless)) def test_ceil(self): self._test1(np.ceil, (self.q1, self.qm, self.qless)) def test_trunc(self): self._test1(np.trunc, (self.q1, self.qm, self.qless))