cadquery-freecad-module/Libs/pint/testsuite/test_umath.py

677 lines
26 KiB
Python

# -*- coding: utf-8 -*-
from __future__ import division, unicode_literals, print_function, absolute_import
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
@helpers.requires_numpy()
class TestUFuncs(QuantityTestCase):
FORCE_NDARRAY = True
@property
def qless(self):
return np.asarray([1., 2., 3., 4.]) * self.ureg.dimensionless
@property
def qs(self):
return 8 * self.ureg.J
@property
def q1(self):
return np.asarray([1., 2., 3., 4.]) * self.ureg.J
@property
def q2(self):
return 2 * self.q1
@property
def qm(self):
return np.asarray([1., 2., 3., 4.]) * self.ureg.m
@property
def qi(self):
return np.asarray([1 + 1j, 2 + 2j, 3 + 3j, 4 + 4j]) * self.ureg.m
def assertEqual(self, first, second, msg=None):
np.testing.assert_equal(first, second, msg)
def assertRaisesMsg(self, msg, ExcType, func, *args, **kwargs):
try:
func(*args, **kwargs)
self.assertFalse(True, msg='Exception {0} not raised {1}'.format(ExcType, msg))
except ExcType as e:
pass
except Exception as e:
self.assertFalse(True, msg='{0} not raised but {1}\n{2}'.format(ExcType, e, msg))
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.
:param func: function callable.
:param ok_with: iterables of values that work fine.
:param raise_with: iterables of values that raise exceptions.
:param 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.
:param results: iterable of results.
If None, the result will be obtained by applying
func to each ok_with value
:param rtol: relative tolerance.
"""
if results is None:
results = [None, ] * len(ok_with)
for x1, res in zip(ok_with, results):
err_msg = 'At {0} with {1}'.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:
self.assertRaisesMsg('At {0} with {1}'.format(func.__name__, x1),
ValueError, 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)
:param func: function callable.
:param ok_with: iterables of values that work fine.
:param raise_with: iterables of values that raise exceptions.
:param results: iterable of results.
If None, the result will be obtained by applying
func to each ok_with value
"""
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.
:param func: function callable.
:param ok_with: iterables of values that work fine.
:param raise_with: iterables of values that raise exceptions.
:param 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.
:param results: iterable of results.
If None, the result will be obtained by applying
func to each ok_with value
:param rtol: relative tolerance.
"""
if results is None:
results = [None, ] * len(ok_with)
for x1, res in zip(ok_with, results):
err_msg = 'At {0} with {1}'.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:
self.assertRaisesMsg('At {0} with {1}'.format(func.__name__, x1),
ValueError, 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.
:param func: function callable.
:param x1: first argument of func.
:param ok_with: iterables of values that work fine.
:param raise_with: iterables of values that raise exceptions.
:param 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.
:param rtol: relative tolerance.
:param convert2: if the ok_with[n] should be converted to x1.units.
"""
for x2 in ok_with:
err_msg = 'At {0} with {1} and {2}'.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:
self.assertRaisesMsg('At {0} with {1} and {2}'.format(func.__name__, x1, x2),
ValueError, func, x1, x2)
def _testn2(self, func, x1, ok_with, raise_with=()):
"""Test function that takes two arguments and return a ndarray.
:param func: function callable.
:param x1: first argument of func.
:param ok_with: iterables of values that work fine.
:param raise_with: iterables of values that raise exceptions.
"""
self._test2(func, x1, ok_with, raise_with, output_units=None)
@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.
"""
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)
@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.
"""
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
), (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,
), (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
), (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, .9, .1) * self.ureg.dimensionless,
np.arange(0, .9, .1) * self.ureg.m / self.ureg.m
), (self.ureg.m, ), 'radian')
def test_arccos(self):
x = np.arange(0, .9, .1) * self.ureg.m
self._test1(np.arccos, (np.arange(0, .9, .1) * self.ureg.dimensionless,
np.arange(0, .9, .1) * self.ureg.m / self.ureg.m
), (self.ureg.m, ), 'radian')
def test_arctan(self):
self._test1(np.arctan, (np.arange(0, .9, .1) * self.ureg.dimensionless,
np.arange(0, .9, .1) * self.ureg.m / self.ureg.m
), (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, .9, .1) * m,
(np.arange(0, .9, .1) * m, np.arange(.9, 0., -.1) * m,
np.arange(0, .9, .1) * km, np.arange(.9, 0., -.1) * km,
),
raise_with=np.arange(0, .9, .1) * j,
output_units='radian')
def test_hypot(self):
self.assertTrue(np.hypot(3. * self.ureg.m, 4. * self.ureg.m) == 5. * self.ureg.m)
self.assertTrue(np.hypot(3. * self.ureg.m, 400. * self.ureg.cm) == 5. * self.ureg.m)
self.assertRaises(ValueError, np.hypot, 1. * self.ureg.m, 2. * 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
), (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
), (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
), (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, .9, .1) * self.ureg.dimensionless,
np.arange(0, .9, .1) * self.ureg.m / self.ureg.m
), (self.ureg.m, ), 'radian')
def test_arccosh(self):
self._test1(np.arccosh, (np.arange(1., 1.9, .1) * self.ureg.dimensionless,
np.arange(1., 1.9, .1) * self.ureg.m / self.ureg.m
), (self.ureg.m, ), 'radian')
def test_arctanh(self):
self._test1(np.arctanh, (np.arange(0, .9, .1) * self.ureg.dimensionless,
np.arange(0, .9, .1) * self.ureg.m / self.ureg.m
), (.1 * self.ureg.m, ), 'radian')
def test_deg2rad(self):
self._test1(np.deg2rad, (np.arange(0, pi/2, pi/4) * self.ureg.degrees,
), (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,
),
(self.ureg.m, ), 'degree',
results=(None,
None,
np.rad2deg(np.arange(0, pi/2, pi/4)*0.001) * self.ureg.degree,
))
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.
"""
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, ))
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.
"""
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.isreal,
(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))