1201 lines
37 KiB
Python
1201 lines
37 KiB
Python
|
from itertools import product, combinations_with_replacement
|
||
|
|
||
|
import numpy as np
|
||
|
|
||
|
from numba import jit, njit, typeof
|
||
|
from numba.np.numpy_support import numpy_version
|
||
|
from numba.tests.support import TestCase, MemoryLeakMixin, tag
|
||
|
import unittest
|
||
|
|
||
|
|
||
|
def array_all(arr):
|
||
|
return arr.all()
|
||
|
|
||
|
def array_all_global(arr):
|
||
|
return np.all(arr)
|
||
|
|
||
|
def array_any(arr):
|
||
|
return arr.any()
|
||
|
|
||
|
def array_any_global(arr):
|
||
|
return np.any(arr)
|
||
|
|
||
|
def array_cumprod(arr):
|
||
|
return arr.cumprod()
|
||
|
|
||
|
def array_cumprod_global(arr):
|
||
|
return np.cumprod(arr)
|
||
|
|
||
|
def array_nancumprod(arr):
|
||
|
return np.nancumprod(arr)
|
||
|
|
||
|
def array_cumsum(arr):
|
||
|
return arr.cumsum()
|
||
|
|
||
|
def array_cumsum_global(arr):
|
||
|
return np.cumsum(arr)
|
||
|
|
||
|
def array_nancumsum(arr):
|
||
|
return np.nancumsum(arr)
|
||
|
|
||
|
def array_sum(arr):
|
||
|
return arr.sum()
|
||
|
|
||
|
def array_sum_global(arr):
|
||
|
return np.sum(arr)
|
||
|
|
||
|
def array_prod(arr):
|
||
|
return arr.prod()
|
||
|
|
||
|
def array_prod_global(arr):
|
||
|
return np.prod(arr)
|
||
|
|
||
|
def array_mean(arr):
|
||
|
return arr.mean()
|
||
|
|
||
|
def array_mean_global(arr):
|
||
|
return np.mean(arr)
|
||
|
|
||
|
def array_var(arr):
|
||
|
return arr.var()
|
||
|
|
||
|
def array_var_global(arr):
|
||
|
return np.var(arr)
|
||
|
|
||
|
def array_std(arr):
|
||
|
return arr.std()
|
||
|
|
||
|
def array_std_global(arr):
|
||
|
return np.std(arr)
|
||
|
|
||
|
def array_min(arr):
|
||
|
return arr.min()
|
||
|
|
||
|
def array_min_global(arr):
|
||
|
return np.min(arr)
|
||
|
|
||
|
def array_amin(arr):
|
||
|
return np.amin(arr)
|
||
|
|
||
|
def array_max(arr):
|
||
|
return arr.max()
|
||
|
|
||
|
def array_max_global(arr):
|
||
|
return np.max(arr)
|
||
|
|
||
|
def array_amax(arr):
|
||
|
return np.amax(arr)
|
||
|
|
||
|
def array_argmin(arr):
|
||
|
return arr.argmin()
|
||
|
|
||
|
def array_argmin_global(arr):
|
||
|
return np.argmin(arr)
|
||
|
|
||
|
def array_argmax(arr):
|
||
|
return arr.argmax()
|
||
|
|
||
|
def array_argmax_global(arr):
|
||
|
return np.argmax(arr)
|
||
|
|
||
|
def array_median_global(arr):
|
||
|
return np.median(arr)
|
||
|
|
||
|
def array_nanmin(arr):
|
||
|
return np.nanmin(arr)
|
||
|
|
||
|
def array_nanmax(arr):
|
||
|
return np.nanmax(arr)
|
||
|
|
||
|
def array_nanmean(arr):
|
||
|
return np.nanmean(arr)
|
||
|
|
||
|
def array_nansum(arr):
|
||
|
return np.nansum(arr)
|
||
|
|
||
|
def array_nanprod(arr):
|
||
|
return np.nanprod(arr)
|
||
|
|
||
|
def array_nanstd(arr):
|
||
|
return np.nanstd(arr)
|
||
|
|
||
|
def array_nanvar(arr):
|
||
|
return np.nanvar(arr)
|
||
|
|
||
|
def array_nanmedian_global(arr):
|
||
|
return np.nanmedian(arr)
|
||
|
|
||
|
def array_percentile_global(arr, q):
|
||
|
return np.percentile(arr, q)
|
||
|
|
||
|
def array_nanpercentile_global(arr, q):
|
||
|
return np.nanpercentile(arr, q)
|
||
|
|
||
|
def array_ptp_global(a):
|
||
|
return np.ptp(a)
|
||
|
|
||
|
def array_ptp(a):
|
||
|
return a.ptp()
|
||
|
|
||
|
def array_quantile_global(arr, q):
|
||
|
return np.quantile(arr, q)
|
||
|
|
||
|
def array_nanquantile_global(arr, q):
|
||
|
return np.nanquantile(arr, q)
|
||
|
|
||
|
def base_test_arrays(dtype):
|
||
|
if dtype == np.bool_:
|
||
|
def factory(n):
|
||
|
assert n % 2 == 0
|
||
|
return np.bool_([0, 1] * (n // 2))
|
||
|
else:
|
||
|
def factory(n):
|
||
|
return np.arange(n, dtype=dtype) + 1
|
||
|
|
||
|
a1 = factory(10)
|
||
|
a2 = factory(10).reshape(2, 5)
|
||
|
# The prod() of this array fits in a 32-bit int
|
||
|
a3 = (factory(12))[::-1].reshape((2, 3, 2), order='A')
|
||
|
assert not (a3.flags.c_contiguous or a3.flags.f_contiguous)
|
||
|
|
||
|
return [a1, a2, a3]
|
||
|
|
||
|
def full_test_arrays(dtype):
|
||
|
array_list = base_test_arrays(dtype)
|
||
|
|
||
|
# Add floats with some mantissa
|
||
|
if dtype == np.float32:
|
||
|
array_list += [a / 10 for a in array_list]
|
||
|
|
||
|
# add imaginary part
|
||
|
if dtype == np.complex64:
|
||
|
acc = []
|
||
|
for a in array_list:
|
||
|
tmp = a / 10 + 1j * a / 11
|
||
|
tmp[::2] = np.conj(tmp[::2])
|
||
|
acc.append(tmp)
|
||
|
array_list.extend(acc)
|
||
|
|
||
|
for a in array_list:
|
||
|
assert a.dtype == np.dtype(dtype)
|
||
|
return array_list
|
||
|
|
||
|
def run_comparative(compare_func, test_array):
|
||
|
cfunc = njit(compare_func)
|
||
|
numpy_result = compare_func(test_array)
|
||
|
numba_result = cfunc(test_array)
|
||
|
|
||
|
return numpy_result, numba_result
|
||
|
|
||
|
|
||
|
class TestArrayReductions(MemoryLeakMixin, TestCase):
|
||
|
"""
|
||
|
Test array reduction methods and functions such as .sum(), .max(), etc.
|
||
|
"""
|
||
|
|
||
|
def setUp(self):
|
||
|
super(TestArrayReductions, self).setUp()
|
||
|
np.random.seed(42)
|
||
|
|
||
|
def check_reduction_basic(self, pyfunc, **kwargs):
|
||
|
# Basic reduction checks on 1-d float64 arrays
|
||
|
cfunc = jit(nopython=True)(pyfunc)
|
||
|
def check(arr):
|
||
|
self.assertPreciseEqual(pyfunc(arr), cfunc(arr), **kwargs)
|
||
|
|
||
|
arr = np.float64([1.0, 2.0, 0.0, -0.0, 1.0, -1.5])
|
||
|
check(arr)
|
||
|
arr = np.float64([-0.0, -1.5])
|
||
|
check(arr)
|
||
|
arr = np.float64([-1.5, 2.5, 'inf'])
|
||
|
check(arr)
|
||
|
arr = np.float64([-1.5, 2.5, '-inf'])
|
||
|
check(arr)
|
||
|
arr = np.float64([-1.5, 2.5, 'inf', '-inf'])
|
||
|
check(arr)
|
||
|
arr = np.float64(['nan', -1.5, 2.5, 'nan', 3.0])
|
||
|
check(arr)
|
||
|
arr = np.float64(['nan', -1.5, 2.5, 'nan', 'inf', '-inf', 3.0])
|
||
|
check(arr)
|
||
|
arr = np.float64([5.0, 'nan', -1.5, 'nan'])
|
||
|
check(arr)
|
||
|
# Only NaNs
|
||
|
arr = np.float64(['nan', 'nan'])
|
||
|
check(arr)
|
||
|
|
||
|
def test_all_basic(self, pyfunc=array_all):
|
||
|
cfunc = jit(nopython=True)(pyfunc)
|
||
|
def check(arr):
|
||
|
self.assertPreciseEqual(pyfunc(arr), cfunc(arr))
|
||
|
|
||
|
arr = np.float64([1.0, 0.0, float('inf'), float('nan')])
|
||
|
check(arr)
|
||
|
arr[1] = -0.0
|
||
|
check(arr)
|
||
|
arr[1] = 1.5
|
||
|
check(arr)
|
||
|
arr = arr.reshape((2, 2))
|
||
|
check(arr)
|
||
|
check(arr[::-1])
|
||
|
|
||
|
def test_any_basic(self, pyfunc=array_any):
|
||
|
cfunc = jit(nopython=True)(pyfunc)
|
||
|
def check(arr):
|
||
|
self.assertPreciseEqual(pyfunc(arr), cfunc(arr))
|
||
|
|
||
|
arr = np.float64([0.0, -0.0, 0.0, 0.0])
|
||
|
check(arr)
|
||
|
arr[2] = float('nan')
|
||
|
check(arr)
|
||
|
arr[2] = float('inf')
|
||
|
check(arr)
|
||
|
arr[2] = 1.5
|
||
|
check(arr)
|
||
|
arr = arr.reshape((2, 2))
|
||
|
check(arr)
|
||
|
check(arr[::-1])
|
||
|
|
||
|
def test_sum_basic(self):
|
||
|
self.check_reduction_basic(array_sum)
|
||
|
|
||
|
def test_mean_basic(self):
|
||
|
self.check_reduction_basic(array_mean)
|
||
|
|
||
|
def test_var_basic(self):
|
||
|
self.check_reduction_basic(array_var, prec='double')
|
||
|
|
||
|
def test_std_basic(self):
|
||
|
self.check_reduction_basic(array_std)
|
||
|
|
||
|
def test_min_basic(self):
|
||
|
self.check_reduction_basic(array_min)
|
||
|
|
||
|
def test_max_basic(self):
|
||
|
self.check_reduction_basic(array_max)
|
||
|
|
||
|
def test_argmin_basic(self):
|
||
|
self.check_reduction_basic(array_argmin)
|
||
|
|
||
|
def test_argmax_basic(self):
|
||
|
self.check_reduction_basic(array_argmax)
|
||
|
|
||
|
def test_nanmin_basic(self):
|
||
|
self.check_reduction_basic(array_nanmin)
|
||
|
|
||
|
def test_nanmax_basic(self):
|
||
|
self.check_reduction_basic(array_nanmax)
|
||
|
|
||
|
def test_nanmean_basic(self):
|
||
|
self.check_reduction_basic(array_nanmean)
|
||
|
|
||
|
def test_nansum_basic(self):
|
||
|
self.check_reduction_basic(array_nansum)
|
||
|
|
||
|
def test_nanprod_basic(self):
|
||
|
self.check_reduction_basic(array_nanprod)
|
||
|
|
||
|
def test_nanstd_basic(self):
|
||
|
self.check_reduction_basic(array_nanstd)
|
||
|
|
||
|
def test_nanvar_basic(self):
|
||
|
self.check_reduction_basic(array_nanvar, prec='double')
|
||
|
|
||
|
def check_median_basic(self, pyfunc, array_variations):
|
||
|
cfunc = jit(nopython=True)(pyfunc)
|
||
|
def check(arr):
|
||
|
expected = pyfunc(arr)
|
||
|
got = cfunc(arr)
|
||
|
self.assertPreciseEqual(got, expected)
|
||
|
|
||
|
# Odd sizes
|
||
|
def check_odd(a):
|
||
|
check(a)
|
||
|
a = a.reshape((9, 7))
|
||
|
check(a)
|
||
|
check(a.T)
|
||
|
for a in array_variations(np.arange(63) + 10.5):
|
||
|
check_odd(a)
|
||
|
|
||
|
# Even sizes
|
||
|
def check_even(a):
|
||
|
check(a)
|
||
|
a = a.reshape((4, 16))
|
||
|
check(a)
|
||
|
check(a.T)
|
||
|
for a in array_variations(np.arange(64) + 10.5):
|
||
|
check_even(a)
|
||
|
|
||
|
@staticmethod
|
||
|
def _array_variations(a):
|
||
|
# Sorted, reversed, random, many duplicates, many NaNs, all NaNs
|
||
|
yield a
|
||
|
a = a[::-1].copy()
|
||
|
yield a
|
||
|
np.random.shuffle(a)
|
||
|
yield a
|
||
|
a[a % 4 >= 1] = 3.5
|
||
|
yield a
|
||
|
a[a % 4 >= 2] = np.nan
|
||
|
yield a
|
||
|
a[:] = np.nan
|
||
|
yield a
|
||
|
|
||
|
def test_median_basic(self):
|
||
|
pyfunc = array_median_global
|
||
|
|
||
|
def variations(a):
|
||
|
# Sorted, reversed, random, many duplicates
|
||
|
yield a
|
||
|
a = a[::-1].copy()
|
||
|
yield a
|
||
|
np.random.shuffle(a)
|
||
|
yield a
|
||
|
a[a % 4 >= 1] = 3.5
|
||
|
yield a
|
||
|
|
||
|
self.check_median_basic(pyfunc, variations)
|
||
|
|
||
|
def check_percentile_and_quantile(self, pyfunc, q_upper_bound):
|
||
|
cfunc = jit(nopython=True)(pyfunc)
|
||
|
|
||
|
def check(a, q, abs_tol=1e-12):
|
||
|
expected = pyfunc(a, q)
|
||
|
got = cfunc(a, q)
|
||
|
# NOTE: inf/nan is not checked, seems to be susceptible to upstream
|
||
|
# changes
|
||
|
finite = np.isfinite(expected)
|
||
|
if np.all(finite):
|
||
|
self.assertPreciseEqual(got, expected, abs_tol=abs_tol)
|
||
|
else:
|
||
|
self.assertPreciseEqual(got[finite], expected[finite],
|
||
|
abs_tol=abs_tol)
|
||
|
|
||
|
a = self.random.randn(27).reshape(3, 3, 3)
|
||
|
q = np.linspace(0, q_upper_bound, 14)[::-1]
|
||
|
check(a, q)
|
||
|
check(a, 0)
|
||
|
check(a, q_upper_bound / 2)
|
||
|
check(a, q_upper_bound)
|
||
|
|
||
|
not_finite = [np.nan, -np.inf, np.inf]
|
||
|
a.flat[:10] = self.random.choice(not_finite, 10)
|
||
|
self.random.shuffle(a)
|
||
|
self.random.shuffle(q)
|
||
|
check(a, q)
|
||
|
|
||
|
a = a.flatten().tolist()
|
||
|
q = q.flatten().tolist()
|
||
|
check(a, q)
|
||
|
check(tuple(a), tuple(q))
|
||
|
|
||
|
a = self.random.choice([1, 2, 3, 4], 10)
|
||
|
q = np.linspace(0, q_upper_bound, 5)
|
||
|
check(a, q)
|
||
|
|
||
|
# tests inspired by
|
||
|
# https://github.com/numpy/numpy/blob/345b2f6e/numpy/lib/tests/test_function_base.py
|
||
|
x = np.arange(8) * 0.5
|
||
|
np.testing.assert_equal(cfunc(x, 0), 0.)
|
||
|
np.testing.assert_equal(cfunc(x, q_upper_bound), 3.5)
|
||
|
np.testing.assert_equal(cfunc(x, q_upper_bound / 2), 1.75)
|
||
|
|
||
|
x = np.arange(12).reshape(3, 4)
|
||
|
q = np.array((0.25, 0.5, 1.0)) * q_upper_bound
|
||
|
np.testing.assert_equal(cfunc(x, q), [2.75, 5.5, 11.0])
|
||
|
|
||
|
x = np.arange(3 * 4 * 5 * 6).reshape(3, 4, 5, 6)
|
||
|
q = np.array((0.25, 0.50)) * q_upper_bound
|
||
|
np.testing.assert_equal(cfunc(x, q).shape, (2,))
|
||
|
|
||
|
q = np.array((0.25, 0.50, 0.75)) * q_upper_bound
|
||
|
np.testing.assert_equal(cfunc(x, q).shape, (3,))
|
||
|
|
||
|
x = np.arange(12).reshape(3, 4)
|
||
|
np.testing.assert_equal(cfunc(x, q_upper_bound / 2), 5.5)
|
||
|
self.assertTrue(np.isscalar(cfunc(x, q_upper_bound / 2)))
|
||
|
|
||
|
np.testing.assert_equal(cfunc([1, 2, 3], 0), 1)
|
||
|
|
||
|
a = np.array([2, 3, 4, 1])
|
||
|
cfunc(a, [q_upper_bound / 2])
|
||
|
np.testing.assert_equal(a, np.array([2, 3, 4, 1]))
|
||
|
|
||
|
def check_percentile_edge_cases(self, pyfunc, q_upper_bound=100):
|
||
|
cfunc = jit(nopython=True)(pyfunc)
|
||
|
|
||
|
def check(a, q, abs_tol=1e-14):
|
||
|
expected = pyfunc(a, q)
|
||
|
got = cfunc(a, q)
|
||
|
# NOTE: inf/nan is not checked, seems to be susceptible to upstream
|
||
|
# changes
|
||
|
finite = np.isfinite(expected)
|
||
|
if np.all(finite):
|
||
|
self.assertPreciseEqual(got, expected, abs_tol=abs_tol)
|
||
|
else:
|
||
|
self.assertPreciseEqual(got[finite], expected[finite],
|
||
|
abs_tol=abs_tol)
|
||
|
|
||
|
def convert_to_float_and_check(a, q, abs_tol=1e-14):
|
||
|
expected = pyfunc(a, q).astype(np.float64)
|
||
|
got = cfunc(a, q)
|
||
|
self.assertPreciseEqual(got, expected, abs_tol=abs_tol)
|
||
|
|
||
|
def _array_combinations(elements):
|
||
|
for i in range(1, 10):
|
||
|
for comb in combinations_with_replacement(elements, i):
|
||
|
yield np.array(comb)
|
||
|
|
||
|
# high number of combinations, many including non-finite values
|
||
|
q = (0, 0.1 * q_upper_bound, 0.2 * q_upper_bound, q_upper_bound)
|
||
|
element_pool = (1, -1, np.nan, np.inf, -np.inf)
|
||
|
for a in _array_combinations(element_pool):
|
||
|
check(a, q)
|
||
|
|
||
|
# edge cases - numpy exhibits behavioural differences across
|
||
|
# platforms, see: https://github.com/numpy/numpy/issues/13272
|
||
|
if q_upper_bound == 1:
|
||
|
_check = convert_to_float_and_check
|
||
|
else:
|
||
|
_check = check
|
||
|
|
||
|
a = np.array(5)
|
||
|
q = np.array(1)
|
||
|
_check(a, q)
|
||
|
|
||
|
a = 5
|
||
|
q = q_upper_bound / 2
|
||
|
_check(a, q)
|
||
|
|
||
|
def check_percentile_exceptions(self, pyfunc):
|
||
|
cfunc = jit(nopython=True)(pyfunc)
|
||
|
|
||
|
def check_err(a, q):
|
||
|
with self.assertRaises(ValueError) as raises:
|
||
|
cfunc(a, q)
|
||
|
self.assertEqual(
|
||
|
"Percentiles must be in the range [0, 100]",
|
||
|
str(raises.exception)
|
||
|
)
|
||
|
|
||
|
# Exceptions leak references
|
||
|
self.disable_leak_check()
|
||
|
|
||
|
a = np.arange(5)
|
||
|
check_err(a, -5) # q less than 0
|
||
|
check_err(a, (1, 10, 105)) # q contains value greater than 100
|
||
|
check_err(a, (1, 10, np.nan)) # q contains nan
|
||
|
|
||
|
with self.assertTypingError() as e:
|
||
|
a = np.arange(5) * 1j
|
||
|
q = 0.1
|
||
|
cfunc(a, q)
|
||
|
|
||
|
self.assertIn('Not supported for complex dtype', str(e.exception))
|
||
|
|
||
|
def check_quantile_exceptions(self, pyfunc):
|
||
|
cfunc = jit(nopython=True)(pyfunc)
|
||
|
|
||
|
def check_err(a, q):
|
||
|
with self.assertRaises(ValueError) as raises:
|
||
|
cfunc(a, q)
|
||
|
self.assertEqual(
|
||
|
"Quantiles must be in the range [0, 1]",
|
||
|
str(raises.exception)
|
||
|
)
|
||
|
|
||
|
# Exceptions leak references
|
||
|
self.disable_leak_check()
|
||
|
|
||
|
a = np.arange(5)
|
||
|
check_err(a, -0.5) # q less than 0
|
||
|
check_err(a, (0.1, 0.10, 1.05)) # q contains value greater than 1
|
||
|
check_err(a, (0.1, 0.10, np.nan)) # q contains nan
|
||
|
|
||
|
with self.assertTypingError() as e:
|
||
|
a = np.arange(5) * 1j
|
||
|
q = 0.1
|
||
|
cfunc(a, q)
|
||
|
|
||
|
self.assertIn('Not supported for complex dtype', str(e.exception))
|
||
|
|
||
|
def test_percentile_basic(self):
|
||
|
pyfunc = array_percentile_global
|
||
|
self.check_percentile_and_quantile(pyfunc, q_upper_bound=100)
|
||
|
self.check_percentile_edge_cases(pyfunc, q_upper_bound=100)
|
||
|
self.check_percentile_exceptions(pyfunc)
|
||
|
|
||
|
def test_nanpercentile_basic(self):
|
||
|
pyfunc = array_nanpercentile_global
|
||
|
self.check_percentile_and_quantile(pyfunc, q_upper_bound=100)
|
||
|
self.check_percentile_edge_cases(pyfunc, q_upper_bound=100)
|
||
|
self.check_percentile_exceptions(pyfunc)
|
||
|
|
||
|
def test_quantile_basic(self):
|
||
|
pyfunc = array_quantile_global
|
||
|
self.check_percentile_and_quantile(pyfunc, q_upper_bound=1)
|
||
|
self.check_percentile_edge_cases(pyfunc, q_upper_bound=1)
|
||
|
self.check_quantile_exceptions(pyfunc)
|
||
|
|
||
|
def test_nanquantile_basic(self):
|
||
|
pyfunc = array_nanquantile_global
|
||
|
self.check_percentile_and_quantile(pyfunc, q_upper_bound=1)
|
||
|
self.check_percentile_edge_cases(pyfunc, q_upper_bound=1)
|
||
|
self.check_quantile_exceptions(pyfunc)
|
||
|
|
||
|
def test_nanmedian_basic(self):
|
||
|
pyfunc = array_nanmedian_global
|
||
|
self.check_median_basic(pyfunc, self._array_variations)
|
||
|
|
||
|
def test_array_sum_global(self):
|
||
|
arr = np.arange(10, dtype=np.int32)
|
||
|
arrty = typeof(arr)
|
||
|
self.assertEqual(arrty.ndim, 1)
|
||
|
self.assertEqual(arrty.layout, 'C')
|
||
|
|
||
|
cfunc = njit((arrty,),)(array_sum_global)
|
||
|
self.assertEqual(np.sum(arr), cfunc(arr))
|
||
|
|
||
|
def test_array_prod_int_1d(self):
|
||
|
arr = np.arange(10, dtype=np.int32) + 1
|
||
|
arrty = typeof(arr)
|
||
|
self.assertEqual(arrty.ndim, 1)
|
||
|
self.assertEqual(arrty.layout, 'C')
|
||
|
|
||
|
cfunc = njit((arrty,))(array_prod)
|
||
|
self.assertEqual(arr.prod(), cfunc(arr))
|
||
|
|
||
|
def test_array_prod_float_1d(self):
|
||
|
arr = np.arange(10, dtype=np.float32) + 1 / 10
|
||
|
arrty = typeof(arr)
|
||
|
self.assertEqual(arrty.ndim, 1)
|
||
|
self.assertEqual(arrty.layout, 'C')
|
||
|
|
||
|
cfunc = njit((arrty,))(array_prod)
|
||
|
np.testing.assert_allclose(arr.prod(), cfunc(arr))
|
||
|
|
||
|
def test_array_prod_global(self):
|
||
|
arr = np.arange(10, dtype=np.int32)
|
||
|
arrty = typeof(arr)
|
||
|
self.assertEqual(arrty.ndim, 1)
|
||
|
self.assertEqual(arrty.layout, 'C')
|
||
|
|
||
|
cfunc = njit((arrty,))(array_prod_global)
|
||
|
np.testing.assert_allclose(np.prod(arr), cfunc(arr))
|
||
|
|
||
|
def check_cumulative(self, pyfunc):
|
||
|
arr = np.arange(2, 10, dtype=np.int16)
|
||
|
expected, got = run_comparative(pyfunc, arr)
|
||
|
self.assertPreciseEqual(got, expected)
|
||
|
arr = np.linspace(2, 8, 6)
|
||
|
expected, got = run_comparative(pyfunc, arr)
|
||
|
self.assertPreciseEqual(got, expected)
|
||
|
arr = arr.reshape((3, 2))
|
||
|
expected, got = run_comparative(pyfunc, arr)
|
||
|
self.assertPreciseEqual(got, expected)
|
||
|
|
||
|
def test_array_cumsum(self):
|
||
|
self.check_cumulative(array_cumsum)
|
||
|
|
||
|
def test_array_cumsum_global(self):
|
||
|
self.check_cumulative(array_cumsum_global)
|
||
|
|
||
|
def test_array_cumprod(self):
|
||
|
self.check_cumulative(array_cumprod)
|
||
|
|
||
|
def test_array_cumprod_global(self):
|
||
|
self.check_cumulative(array_cumprod_global)
|
||
|
|
||
|
def check_aggregation_magnitude(self, pyfunc, is_prod=False):
|
||
|
"""
|
||
|
Check that integer overflows are avoided (issue #931).
|
||
|
"""
|
||
|
# Overflows are avoided here (ints are cast either to intp
|
||
|
# or float64).
|
||
|
n_items = 2 if is_prod else 10 # avoid overflow on prod()
|
||
|
arr = (np.arange(n_items) + 40000).astype('int16')
|
||
|
npr, nbr = run_comparative(pyfunc, arr)
|
||
|
self.assertPreciseEqual(npr, nbr)
|
||
|
# Overflows are avoided for functions returning floats here.
|
||
|
# Other functions may wrap around.
|
||
|
arr = (np.arange(10) + 2**60).astype('int64')
|
||
|
npr, nbr = run_comparative(pyfunc, arr)
|
||
|
self.assertPreciseEqual(npr, nbr)
|
||
|
arr = arr.astype('uint64')
|
||
|
npr, nbr = run_comparative(pyfunc, arr)
|
||
|
self.assertPreciseEqual(npr, nbr)
|
||
|
|
||
|
def test_sum_magnitude(self):
|
||
|
self.check_aggregation_magnitude(array_sum)
|
||
|
self.check_aggregation_magnitude(array_sum_global)
|
||
|
|
||
|
def test_cumsum_magnitude(self):
|
||
|
self.check_aggregation_magnitude(array_cumsum)
|
||
|
self.check_aggregation_magnitude(array_cumsum_global)
|
||
|
|
||
|
def test_nancumsum_magnitude(self):
|
||
|
self.check_aggregation_magnitude(array_nancumsum, is_prod=True)
|
||
|
|
||
|
def test_prod_magnitude(self):
|
||
|
self.check_aggregation_magnitude(array_prod, is_prod=True)
|
||
|
self.check_aggregation_magnitude(array_prod_global, is_prod=True)
|
||
|
|
||
|
def test_cumprod_magnitude(self):
|
||
|
self.check_aggregation_magnitude(array_cumprod, is_prod=True)
|
||
|
self.check_aggregation_magnitude(array_cumprod_global, is_prod=True)
|
||
|
|
||
|
def test_nancumprod_magnitude(self):
|
||
|
self.check_aggregation_magnitude(array_nancumprod, is_prod=True)
|
||
|
|
||
|
def test_mean_magnitude(self):
|
||
|
self.check_aggregation_magnitude(array_mean)
|
||
|
self.check_aggregation_magnitude(array_mean_global)
|
||
|
|
||
|
def test_var_magnitude(self):
|
||
|
self.check_aggregation_magnitude(array_var)
|
||
|
self.check_aggregation_magnitude(array_var_global)
|
||
|
|
||
|
def test_std_magnitude(self):
|
||
|
self.check_aggregation_magnitude(array_std)
|
||
|
self.check_aggregation_magnitude(array_std_global)
|
||
|
|
||
|
def _do_check_nptimedelta(self, pyfunc, arr):
|
||
|
arrty = typeof(arr)
|
||
|
cfunc = jit(nopython=True)(pyfunc)
|
||
|
|
||
|
self.assertPreciseEqual(cfunc(arr), pyfunc(arr))
|
||
|
# Even vs. odd size, for np.median
|
||
|
self.assertPreciseEqual(cfunc(arr[:-1]), pyfunc(arr[:-1]))
|
||
|
# Test with different orders, for np.median
|
||
|
arr = arr[::-1].copy() # Keep 'C' layout
|
||
|
self.assertPreciseEqual(cfunc(arr), pyfunc(arr))
|
||
|
np.random.shuffle(arr)
|
||
|
self.assertPreciseEqual(cfunc(arr), pyfunc(arr))
|
||
|
# Test with a NaT
|
||
|
if 'median' not in pyfunc.__name__:
|
||
|
# Test with (val, NaT)^N (and with the random NaT from above)
|
||
|
# use a loop, there's some weird thing/bug with arr[1::2] = 'NaT'
|
||
|
|
||
|
# Further Numba has bug(s) relating to NaN/NaT handling in anything
|
||
|
# using a partition such as np.median
|
||
|
for x in range(1, len(arr), 2):
|
||
|
arr[x] = 'NaT'
|
||
|
self.assertPreciseEqual(cfunc(arr), pyfunc(arr))
|
||
|
# Test with all NaTs
|
||
|
arr.fill(arrty.dtype('NaT'))
|
||
|
self.assertPreciseEqual(cfunc(arr), pyfunc(arr))
|
||
|
|
||
|
def check_npdatetime(self, pyfunc):
|
||
|
arr = np.arange(10).astype(dtype='M8[Y]')
|
||
|
self._do_check_nptimedelta(pyfunc, arr)
|
||
|
|
||
|
def check_nptimedelta(self, pyfunc):
|
||
|
arr = np.arange(10).astype(dtype='m8[s]')
|
||
|
self._do_check_nptimedelta(pyfunc, arr)
|
||
|
|
||
|
def test_min_npdatetime(self):
|
||
|
self.check_npdatetime(array_min)
|
||
|
self.check_nptimedelta(array_min)
|
||
|
|
||
|
def test_max_npdatetime(self):
|
||
|
self.check_npdatetime(array_max)
|
||
|
self.check_nptimedelta(array_max)
|
||
|
|
||
|
def test_argmin_npdatetime(self):
|
||
|
self.check_npdatetime(array_argmin)
|
||
|
self.check_nptimedelta(array_argmin)
|
||
|
|
||
|
def test_argmax_npdatetime(self):
|
||
|
self.check_npdatetime(array_argmax)
|
||
|
self.check_nptimedelta(array_argmax)
|
||
|
|
||
|
def test_median_npdatetime(self):
|
||
|
self.check_nptimedelta(array_median_global)
|
||
|
|
||
|
def test_sum_npdatetime(self):
|
||
|
self.check_nptimedelta(array_sum)
|
||
|
|
||
|
def test_cumsum_npdatetime(self):
|
||
|
self.check_nptimedelta(array_cumsum)
|
||
|
|
||
|
def test_mean_npdatetime(self):
|
||
|
self.check_nptimedelta(array_mean)
|
||
|
|
||
|
def check_nan_cumulative(self, pyfunc):
|
||
|
cfunc = jit(nopython=True)(pyfunc)
|
||
|
|
||
|
def check(a):
|
||
|
expected = pyfunc(a)
|
||
|
got = cfunc(a)
|
||
|
self.assertPreciseEqual(expected, got)
|
||
|
|
||
|
def _set_some_values_to_nan(a):
|
||
|
p = a.size // 2 # set approx half elements to NaN
|
||
|
np.put(a, np.random.choice(range(a.size), p, replace=False), np.nan)
|
||
|
return a
|
||
|
|
||
|
def a_variations():
|
||
|
yield np.linspace(-1, 3, 60).reshape(3, 4, 5)
|
||
|
yield np.array([np.inf, 3, 4])
|
||
|
yield np.array([True, True, True, False])
|
||
|
yield np.arange(1, 10)
|
||
|
yield np.asfortranarray(np.arange(1, 64) - 33.3)
|
||
|
yield np.arange(1, 10, dtype=np.float32)[::-1]
|
||
|
|
||
|
for a in a_variations():
|
||
|
check(a) # no nans
|
||
|
check(_set_some_values_to_nan(a.astype(np.float64))) # about 50% nans
|
||
|
|
||
|
# edge cases
|
||
|
check(np.array([]))
|
||
|
check(np.full(10, np.nan))
|
||
|
|
||
|
parts = np.array([np.nan, 2, np.nan, 4, 5, 6, 7, 8, 9])
|
||
|
|
||
|
a = parts + 1j * parts[::-1]
|
||
|
a = a.reshape(3, 3)
|
||
|
check(a)
|
||
|
|
||
|
def test_nancumprod_basic(self):
|
||
|
self.check_cumulative(array_nancumprod)
|
||
|
self.check_nan_cumulative(array_nancumprod)
|
||
|
|
||
|
def test_nancumsum_basic(self):
|
||
|
self.check_cumulative(array_nancumsum)
|
||
|
self.check_nan_cumulative(array_nancumsum)
|
||
|
|
||
|
def test_ptp_basic(self):
|
||
|
pyfunc = array_ptp_global
|
||
|
cfunc = jit(nopython=True)(pyfunc)
|
||
|
|
||
|
def check(a):
|
||
|
expected = pyfunc(a)
|
||
|
got = cfunc(a)
|
||
|
self.assertPreciseEqual(expected, got)
|
||
|
|
||
|
def a_variations():
|
||
|
yield np.arange(10)
|
||
|
yield np.array([-1.1, np.nan, 2.2])
|
||
|
yield np.array([-np.inf, 5])
|
||
|
yield (4, 2, 5)
|
||
|
yield (1,)
|
||
|
yield np.full(5, 5)
|
||
|
yield [2.2, -2.3, 0.1]
|
||
|
a = np.linspace(-10, 10, 16).reshape(4, 2, 2)
|
||
|
yield a
|
||
|
yield np.asfortranarray(a)
|
||
|
yield a[::-1]
|
||
|
np.random.RandomState(0).shuffle(a)
|
||
|
yield a
|
||
|
yield 6
|
||
|
yield 6.5
|
||
|
yield -np.inf
|
||
|
yield 1 + 4j
|
||
|
yield [2.2, np.nan]
|
||
|
yield [2.2, np.inf]
|
||
|
yield ((4.1, 2.0, -7.6), (4.3, 2.7, 5.2))
|
||
|
yield np.full(5, np.nan)
|
||
|
yield 1 + np.nan * 1j
|
||
|
yield np.nan + np.nan * 1j
|
||
|
yield np.nan
|
||
|
|
||
|
for a in a_variations():
|
||
|
check(a)
|
||
|
|
||
|
def test_ptp_method(self):
|
||
|
# checks wiring of np.ndarray.ptp() only, `np.ptp` test above checks
|
||
|
# the actual alg
|
||
|
pyfunc = array_ptp
|
||
|
cfunc = jit(nopython=True)(pyfunc)
|
||
|
|
||
|
a = np.arange(10)
|
||
|
expected = pyfunc(a)
|
||
|
got = cfunc(a)
|
||
|
self.assertPreciseEqual(expected, got)
|
||
|
|
||
|
def test_ptp_complex(self):
|
||
|
pyfunc = array_ptp_global
|
||
|
cfunc = jit(nopython=True)(pyfunc)
|
||
|
|
||
|
def check(a):
|
||
|
expected = pyfunc(a)
|
||
|
got = cfunc(a)
|
||
|
self.assertPreciseEqual(expected, got)
|
||
|
|
||
|
def make_array(real_nan=False, imag_nan=False):
|
||
|
real = np.linspace(-4, 4, 25)
|
||
|
if real_nan:
|
||
|
real[4:9] = np.nan
|
||
|
imag = np.linspace(-5, 5, 25)
|
||
|
if imag_nan:
|
||
|
imag[7:12] = np.nan
|
||
|
return (real + 1j * imag).reshape(5, 5)
|
||
|
|
||
|
for real_nan, imag_nan in product([True, False], repeat=2):
|
||
|
comp = make_array(real_nan, imag_nan)
|
||
|
check(comp)
|
||
|
|
||
|
real = np.ones(8)
|
||
|
imag = np.arange(-4, 4)
|
||
|
comp = real + 1j * imag
|
||
|
check(comp)
|
||
|
comp = real - 1j * imag
|
||
|
check(comp)
|
||
|
|
||
|
comp = np.full((4, 4), fill_value=(1 - 1j))
|
||
|
check(comp)
|
||
|
|
||
|
def test_ptp_exceptions(self):
|
||
|
pyfunc = array_ptp_global
|
||
|
cfunc = jit(nopython=True)(pyfunc)
|
||
|
|
||
|
# Exceptions leak references
|
||
|
self.disable_leak_check()
|
||
|
|
||
|
with self.assertTypingError() as e:
|
||
|
cfunc(np.array((True, True, False)))
|
||
|
|
||
|
msg = "Boolean dtype is unsupported (as per NumPy)"
|
||
|
self.assertIn(msg, str(e.exception))
|
||
|
|
||
|
with self.assertRaises(ValueError) as e:
|
||
|
cfunc(np.array([]))
|
||
|
|
||
|
msg = "zero-size array reduction not possible"
|
||
|
self.assertIn(msg, str(e.exception))
|
||
|
|
||
|
def test_min_max_complex_basic(self):
|
||
|
pyfuncs = array_min_global, array_max_global
|
||
|
|
||
|
for pyfunc in pyfuncs:
|
||
|
cfunc = jit(nopython=True)(pyfunc)
|
||
|
|
||
|
def check(a):
|
||
|
expected = pyfunc(a)
|
||
|
got = cfunc(a)
|
||
|
self.assertPreciseEqual(expected, got)
|
||
|
|
||
|
real = np.linspace(-10, 10, 40)
|
||
|
real[:4] = real[-1]
|
||
|
imag = real * 2
|
||
|
a = real - imag * 1j
|
||
|
check(a)
|
||
|
|
||
|
for _ in range(10):
|
||
|
self.random.shuffle(real)
|
||
|
self.random.shuffle(imag)
|
||
|
dtype = self.random.choice([np.complex64, np.complex128])
|
||
|
a = real - imag * 1j
|
||
|
a[:4] = a[-1]
|
||
|
check(a.astype(dtype))
|
||
|
|
||
|
def test_nanmin_nanmax_complex_basic(self):
|
||
|
pyfuncs = array_nanmin, array_nanmax
|
||
|
|
||
|
for pyfunc in pyfuncs:
|
||
|
cfunc = jit(nopython=True)(pyfunc)
|
||
|
|
||
|
def check(a):
|
||
|
expected = pyfunc(a)
|
||
|
got = cfunc(a)
|
||
|
self.assertPreciseEqual(expected, got)
|
||
|
|
||
|
real = np.linspace(-10, 10, 40)
|
||
|
real[:4] = real[-1]
|
||
|
real[5:9] = np.nan
|
||
|
imag = real * 2
|
||
|
imag[7:12] = np.nan
|
||
|
a = real - imag * 1j
|
||
|
check(a)
|
||
|
|
||
|
for _ in range(10):
|
||
|
self.random.shuffle(real)
|
||
|
self.random.shuffle(imag)
|
||
|
a = real - imag * 1j
|
||
|
a[:4] = a[-1]
|
||
|
check(a)
|
||
|
|
||
|
def test_nanmin_nanmax_non_array_inputs(self):
|
||
|
pyfuncs = array_nanmin, array_nanmax
|
||
|
|
||
|
def check(a):
|
||
|
expected = pyfunc(a)
|
||
|
got = cfunc(a)
|
||
|
self.assertPreciseEqual(expected, got)
|
||
|
|
||
|
def a_variations():
|
||
|
yield [1, 6, 4, 2]
|
||
|
yield ((-10, 4, -12), (5, 200, -30))
|
||
|
yield np.array(3)
|
||
|
yield (2,)
|
||
|
yield 3.142
|
||
|
yield False
|
||
|
yield (np.nan, 3.142, -5.2, 3.0)
|
||
|
yield [np.inf, np.nan, -np.inf]
|
||
|
yield [(np.nan, 1.1), (-4.4, 8.7)]
|
||
|
|
||
|
for pyfunc in pyfuncs:
|
||
|
cfunc = jit(nopython=True)(pyfunc)
|
||
|
|
||
|
for a in a_variations():
|
||
|
check(a)
|
||
|
|
||
|
def test_argmax_axis_1d_2d_4d(self):
|
||
|
arr1d = np.array([0, 20, 3, 4])
|
||
|
arr2d = np.arange(6).reshape(2, 3)
|
||
|
arr2d[0,1] += 100
|
||
|
|
||
|
arr4d = np.arange(120).reshape(2, 3, 4, 5) + 10
|
||
|
arr4d[0, 1, 1, 2] += 100
|
||
|
arr4d[1, 0, 0, 0] -= 51
|
||
|
|
||
|
for arr in [arr1d, arr2d, arr4d]:
|
||
|
axes = list(range(arr.ndim)) + [
|
||
|
-(i+1) for i in range(arr.ndim)
|
||
|
]
|
||
|
py_functions = [
|
||
|
lambda a, _axis=axis: np.argmax(a, axis=_axis)
|
||
|
for axis in axes
|
||
|
]
|
||
|
c_functions = [
|
||
|
jit(nopython=True)(pyfunc) for pyfunc in py_functions
|
||
|
]
|
||
|
for cfunc in c_functions:
|
||
|
self.assertPreciseEqual(cfunc.py_func(arr), cfunc(arr))
|
||
|
|
||
|
def test_argmax_axis_out_of_range(self):
|
||
|
arr1d = np.arange(6)
|
||
|
arr2d = np.arange(6).reshape(2, 3)
|
||
|
|
||
|
@jit(nopython=True)
|
||
|
def jitargmax(arr, axis):
|
||
|
return np.argmax(arr, axis)
|
||
|
|
||
|
def assert_raises(arr, axis):
|
||
|
with self.assertRaisesRegex(ValueError, "axis.*out of bounds"):
|
||
|
jitargmax.py_func(arr, axis)
|
||
|
with self.assertRaisesRegex(ValueError, "axis.*out of bounds"):
|
||
|
jitargmax(arr, axis)
|
||
|
|
||
|
assert_raises(arr1d, 1)
|
||
|
assert_raises(arr1d, -2)
|
||
|
assert_raises(arr2d, -3)
|
||
|
assert_raises(arr2d, 2)
|
||
|
# Exceptions leak references
|
||
|
self.disable_leak_check()
|
||
|
|
||
|
def test_argmax_axis_must_be_integer(self):
|
||
|
arr = np.arange(6)
|
||
|
|
||
|
@jit(nopython=True)
|
||
|
def jitargmax(arr, axis):
|
||
|
return np.argmax(arr, axis)
|
||
|
|
||
|
with self.assertTypingError() as e:
|
||
|
jitargmax(arr, "foo")
|
||
|
self.assertIn("axis must be an integer", str(e.exception))
|
||
|
|
||
|
def test_argmax_method_axis(self):
|
||
|
arr2d = np.arange(6).reshape(2, 3)
|
||
|
|
||
|
def argmax(arr):
|
||
|
return arr2d.argmax(axis=0)
|
||
|
|
||
|
self.assertPreciseEqual(argmax(arr2d),
|
||
|
jit(nopython=True)(argmax)(arr2d))
|
||
|
|
||
|
def test_argmax_return_type(self):
|
||
|
# See issue #7853, return type should be intp not based on input type
|
||
|
arr2d = np.arange(6, dtype=np.uint8).reshape(2, 3)
|
||
|
|
||
|
def argmax(arr):
|
||
|
return arr2d.argmax(axis=0)
|
||
|
|
||
|
self.assertPreciseEqual(argmax(arr2d),
|
||
|
jit(nopython=True)(argmax)(arr2d))
|
||
|
|
||
|
def test_argmin_axis_1d_2d_4d(self):
|
||
|
arr1d = np.array([0, 20, 3, 4])
|
||
|
arr2d = np.arange(6).reshape(2, 3)
|
||
|
arr2d[0,1] += 100
|
||
|
|
||
|
arr4d = np.arange(120).reshape(2, 3, 4, 5) + 10
|
||
|
arr4d[0, 1, 1, 2] += 100
|
||
|
arr4d[1, 0, 0, 0] -= 51
|
||
|
|
||
|
for arr in [arr1d, arr2d, arr4d]:
|
||
|
axes = list(range(arr.ndim)) + [
|
||
|
-(i+1) for i in range(arr.ndim)
|
||
|
]
|
||
|
py_functions = [
|
||
|
lambda a, _axis=axis: np.argmin(a, axis=_axis)
|
||
|
for axis in axes
|
||
|
]
|
||
|
c_functions = [
|
||
|
jit(nopython=True)(pyfunc) for pyfunc in py_functions
|
||
|
]
|
||
|
for cfunc in c_functions:
|
||
|
self.assertPreciseEqual(cfunc.py_func(arr), cfunc(arr))
|
||
|
|
||
|
def test_argmin_axis_out_of_range(self):
|
||
|
arr1d = np.arange(6)
|
||
|
arr2d = np.arange(6).reshape(2, 3)
|
||
|
|
||
|
@jit(nopython=True)
|
||
|
def jitargmin(arr, axis):
|
||
|
return np.argmin(arr, axis)
|
||
|
|
||
|
def assert_raises(arr, axis):
|
||
|
with self.assertRaisesRegex(ValueError, "axis.*out of bounds"):
|
||
|
jitargmin.py_func(arr, axis)
|
||
|
with self.assertRaisesRegex(ValueError, "axis.*out of bounds"):
|
||
|
jitargmin(arr, axis)
|
||
|
|
||
|
assert_raises(arr1d, 1)
|
||
|
assert_raises(arr1d, -2)
|
||
|
assert_raises(arr2d, -3)
|
||
|
assert_raises(arr2d, 2)
|
||
|
|
||
|
# Exceptions leak references
|
||
|
self.disable_leak_check()
|
||
|
|
||
|
def test_argmin_axis_must_be_integer(self):
|
||
|
arr = np.arange(6)
|
||
|
|
||
|
@jit(nopython=True)
|
||
|
def jitargmin(arr, axis):
|
||
|
return np.argmin(arr, axis)
|
||
|
|
||
|
with self.assertTypingError() as e:
|
||
|
jitargmin(arr, "foo")
|
||
|
self.assertIn("axis must be an integer", str(e.exception))
|
||
|
|
||
|
def test_argmin_method_axis(self):
|
||
|
arr2d = np.arange(6).reshape(2, 3)
|
||
|
|
||
|
def argmin(arr):
|
||
|
return arr2d.argmin(axis=0)
|
||
|
|
||
|
self.assertPreciseEqual(argmin(arr2d),
|
||
|
jit(nopython=True)(argmin)(arr2d))
|
||
|
|
||
|
def test_argmin_return_type(self):
|
||
|
# See issue #7853, return type should be intp not based on input type
|
||
|
arr2d = np.arange(6, dtype=np.uint8).reshape(2, 3)
|
||
|
|
||
|
def argmin(arr):
|
||
|
return arr2d.argmin(axis=0)
|
||
|
|
||
|
self.assertPreciseEqual(argmin(arr2d),
|
||
|
jit(nopython=True)(argmin)(arr2d))
|
||
|
|
||
|
@classmethod
|
||
|
def install_generated_tests(cls):
|
||
|
# These form a testing product where each of the combinations are tested
|
||
|
|
||
|
# these function are tested in real and complex space
|
||
|
reduction_funcs = [array_sum, array_sum_global,
|
||
|
array_prod, array_prod_global,
|
||
|
array_mean, array_mean_global,
|
||
|
array_var, array_var_global,
|
||
|
array_std, array_std_global,
|
||
|
array_all, array_all_global,
|
||
|
array_any, array_any_global,
|
||
|
array_min, array_min_global,
|
||
|
array_amax, array_amin,
|
||
|
array_max, array_max_global,
|
||
|
array_nanmax, array_nanmin,
|
||
|
array_nansum,
|
||
|
]
|
||
|
|
||
|
# these functions only work in real space as no complex comparison
|
||
|
# operator is implemented
|
||
|
reduction_funcs_rspace = [array_argmin, array_argmin_global,
|
||
|
array_argmax, array_argmax_global]
|
||
|
|
||
|
reduction_funcs += [array_nanmean, array_nanstd, array_nanvar]
|
||
|
reduction_funcs += [array_nanprod]
|
||
|
|
||
|
dtypes_to_test = [np.int32, np.float32, np.bool_, np.complex64]
|
||
|
|
||
|
def install_tests(dtypes, funcs):
|
||
|
# Install tests on class
|
||
|
for dt in dtypes:
|
||
|
test_arrays = full_test_arrays(dt)
|
||
|
for red_func, test_array in product(funcs, test_arrays):
|
||
|
# Create the name for the test function
|
||
|
test_name = "test_{0}_{1}_{2}d"
|
||
|
test_name = test_name.format(red_func.__name__,
|
||
|
test_array.dtype.name,
|
||
|
test_array.ndim)
|
||
|
|
||
|
def new_test_function(self, redFunc=red_func,
|
||
|
testArray=test_array,
|
||
|
testName=test_name):
|
||
|
ulps = 1
|
||
|
if 'prod' in red_func.__name__ and \
|
||
|
np.iscomplexobj(testArray):
|
||
|
# prod family accumulate slightly more error on
|
||
|
# some architectures (power, 32bit) for complex input
|
||
|
ulps = 3
|
||
|
npr, nbr = run_comparative(redFunc, testArray)
|
||
|
self.assertPreciseEqual(npr, nbr, msg=testName,
|
||
|
prec="single", ulps=ulps)
|
||
|
|
||
|
# Install it into the class
|
||
|
setattr(cls, test_name, new_test_function)
|
||
|
|
||
|
# install tests for reduction functions that only work in real space
|
||
|
install_tests(dtypes_to_test[:-1], reduction_funcs_rspace)
|
||
|
|
||
|
# install tests for reduction functions
|
||
|
install_tests(dtypes_to_test, reduction_funcs)
|
||
|
|
||
|
|
||
|
TestArrayReductions.install_generated_tests()
|
||
|
|
||
|
|
||
|
class TestArrayReductionsExceptions(MemoryLeakMixin, TestCase):
|
||
|
|
||
|
# int64, size 0
|
||
|
zero_size = np.arange(0)
|
||
|
|
||
|
def check_exception(self, pyfunc, msg):
|
||
|
cfunc = jit(nopython=True)(pyfunc)
|
||
|
# make sure NumPy raises consistently/no behaviour change
|
||
|
with self.assertRaises(BaseException):
|
||
|
pyfunc(self.zero_size)
|
||
|
# check numba impl raises expected
|
||
|
with self.assertRaises(ValueError) as e:
|
||
|
cfunc(self.zero_size)
|
||
|
self.assertIn(msg, str(e.exception))
|
||
|
|
||
|
@classmethod
|
||
|
def install(cls):
|
||
|
|
||
|
fn_to_msg = dict()
|
||
|
empty_seq = "attempt to get {0} of an empty sequence"
|
||
|
op_no_ident = ("zero-size array to reduction operation "
|
||
|
"{0}")
|
||
|
for x in [array_argmax, array_argmax_global, array_argmin,
|
||
|
array_argmin_global]:
|
||
|
fn_to_msg[x] = empty_seq
|
||
|
for x in [array_max, array_max, array_min, array_min]:
|
||
|
fn_to_msg[x] = op_no_ident
|
||
|
|
||
|
name_template = "test_zero_size_array_{0}"
|
||
|
for fn, msg in fn_to_msg.items():
|
||
|
test_name = name_template.format(fn.__name__)
|
||
|
|
||
|
lmsg = msg.format(fn.__name__)
|
||
|
lmsg = lmsg.replace('array_','').replace('_global','')
|
||
|
def test_fn(self, func=fn, message=lmsg):
|
||
|
self.check_exception(func, message)
|
||
|
|
||
|
setattr(cls, test_name, test_fn)
|
||
|
|
||
|
TestArrayReductionsExceptions.install()
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
unittest.main()
|