diff options
-rw-r--r-- | doc/release/2.0.0-notes.rst | 7 | ||||
-rw-r--r-- | numpy/core/numeric.py | 90 | ||||
-rw-r--r-- | numpy/core/src/multiarray/dtype_transfer.c | 4 | ||||
-rw-r--r-- | numpy/core/tests/test_numeric.py | 124 | ||||
-rw-r--r-- | numpy/fft/fftpack.py | 2 | ||||
-rw-r--r-- | numpy/fft/tests/test_fftpack.py | 12 | ||||
-rw-r--r-- | numpy/fft/tests/test_helper.py | 67 |
7 files changed, 272 insertions, 34 deletions
diff --git a/doc/release/2.0.0-notes.rst b/doc/release/2.0.0-notes.rst index defdc9c00..7c2bfa8a5 100644 --- a/doc/release/2.0.0-notes.rst +++ b/doc/release/2.0.0-notes.rst @@ -94,6 +94,13 @@ A generic sampling function has been added which will generate samples from a given array-like. The samples can be with or without replacement, and with uniform or given non-uniform probabilities. +New function isclose +-------------------- + +Returns a boolean array where two arrays are element-wise equal within a +tolerance. Both relative and absolute tolerance can be specified. The +function is NA aware. + Preliminary multi-dimensional support in the polynomial package --------------------------------------------------------------- diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index bb13d573e..846429016 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -12,7 +12,7 @@ __all__ = ['newaxis', 'ndarray', 'flatiter', 'nditer', 'nested_iters', 'ufunc', 'array_repr', 'array_str', 'set_string_function', 'little_endian', 'require', 'fromiter', 'array_equal', 'array_equiv', - 'indices', 'fromfunction', + 'indices', 'fromfunction', 'isclose', 'load', 'loads', 'isscalar', 'binary_repr', 'base_repr', 'ones', 'identity', 'allclose', 'compare_chararrays', 'putmask', 'seterr', 'geterr', 'setbufsize', 'getbufsize', @@ -2024,6 +2024,94 @@ def allclose(a, b, rtol=1.e-5, atol=1.e-8): y = y[~xinf] return all(less_equal(absolute(x-y), atol + rtol * absolute(y))) +def isclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False): + """ + Returns a boolean array where two arrays are element-wise equal within a + tolerance. + + The tolerance values are positive, typically very small numbers. The + relative difference (`rtol` * abs(`b`)) and the absolute difference + `atol` are added together to compare against the absolute difference + between `a` and `b`. + + Parameters + ---------- + a, b : array_like + Input arrays to compare. + rtol : float + The relative tolerance parameter (see Notes). + atol : float + The absolute tolerance parameter (see Notes). + equal_nan : bool + Whether to compare NaN's as equal. If True, NaN's in `a` will be + considered equal to NaN's in `b` in the output array. + + Returns + ------- + y : array_like + Returns a boolean array of where `a` and `b` are equal within the + given tolerance. If both `a` and `b` are scalars, returns a single + boolean value. + + See Also + -------- + allclose + + Notes + ----- + .. versionadded:: 1.7.0 + + For finite values, isclose uses the following equation to test whether + two floating point values are equivalent. + + absolute(`a` - `b`) <= (`atol` + `rtol` * absolute(`b`)) + + The above equation is not symmetric in `a` and `b`, so that + `isclose(a, b)` might be different from `isclose(b, a)` in + some rare cases. + + Examples + -------- + >>> np.isclose([1e10,1e-7], [1.00001e10,1e-8]) + array([True, False]) + >>> np.isclose([1e10,1e-8], [1.00001e10,1e-9]) + array([True, True]) + >>> np.isclose([1e10,1e-8], [1.0001e10,1e-9]) + array([False, True]) + >>> np.isclose([1.0, np.nan], [1.0, np.nan]) + array([True, False]) + >>> np.isclose([1.0, np.nan], [1.0, np.nan], equal_nan=True) + array([True, True]) + """ + def within_tol(x, y, atol, rtol): + result = less_equal(abs(x-y), atol + rtol * abs(y)) + if isscalar(a) and isscalar(b): + result = bool(result) + return result + x = array(a, copy=False, subok=True, ndmin=1) + y = array(b, copy=False, subok=True, ndmin=1) + xfin = isfinite(x) + yfin = isfinite(y) + if all(xfin, skipna=True) and all(yfin, skipna=True): + return within_tol(x, y, atol, rtol) + else: + finite = xfin & yfin + cond = zeros_like(finite, subok=True, maskna=finite.flags.maskna) + # Because we're using boolean indexing, x & y must be the same shape. + # Ideally, we'd just do x, y = broadcast_arrays(x, y). It's in + # lib.stride_tricks, though, so we can't import it here. + x = x * ones_like(cond) + y = y * ones_like(cond) + # Avoid subtraction with infinite/nan values and indexing with na... + finite[isna(finite)] = False + cond[finite] = within_tol(x[finite], y[finite], atol, rtol) + # Check for equality of infinite values... + cond[~finite] = (x[~finite] == y[~finite]) + if equal_nan: + # Make NaN == NaN + cond[isnan(x) & isnan(y)] = True + return cond + def array_equal(a1, a2): """ True if two arrays have the same shape and elements, False otherwise. diff --git a/numpy/core/src/multiarray/dtype_transfer.c b/numpy/core/src/multiarray/dtype_transfer.c index 47c487338..46acd046f 100644 --- a/numpy/core/src/multiarray/dtype_transfer.c +++ b/numpy/core/src/multiarray/dtype_transfer.c @@ -3846,9 +3846,9 @@ PyArray_CastRawArrays(npy_intp count, } /* Check data alignment */ - aligned = (((npy_intp)src_dtype | src_stride) & + aligned = (((npy_intp)src | src_stride) & (src_dtype->alignment - 1)) == 0 && - (((npy_intp)dst_dtype | dst_stride) & + (((npy_intp)dst | dst_stride) & (dst_dtype->alignment - 1)) == 0; /* Get the function to do the casting */ diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py index de41f0c1f..5233d0f88 100644 --- a/numpy/core/tests/test_numeric.py +++ b/numpy/core/tests/test_numeric.py @@ -1220,6 +1220,130 @@ class TestAllclose(object): assert_array_equal(y,array([0,inf])) +class TestIsclose(object): + rtol = 1e-5 + atol = 1e-8 + + def setup(self): + atol = self.atol + rtol = self.rtol + arr = array([100,1000]) + aran = arange(125).reshape((5,5,5)) + + self.all_close_tests = [ + ([1, 0], [1, 0]), + ([atol], [0]), + ([1], [1 + rtol + atol]), + (arr, arr + arr*rtol), + (arr, arr + arr*rtol + atol), + (aran, aran + aran*rtol), + (inf, inf), + (inf, [inf]), + ([inf, -inf], [inf, -inf]), + ] + self.none_close_tests = [ + ([inf, 0], [1, inf]), + ([inf, -inf], [1, 0]), + ([inf, inf], [1, -inf]), + ([inf, inf], [1, 0]), + ([nan, 0], [nan, -inf]), + ([atol*2], [0]), + ([1], [1 + rtol + atol*2]), + (aran, aran + rtol*1.1*aran + atol*1.1), + (array([inf, 1]), array([0, inf])), + ] + self.some_close_tests = [ + ([inf, 0], [inf, atol*2]), + ([atol, 1, 1e6*(1 + 2*rtol) + atol], [0, nan, 1e6]), + (arange(3), [0, 1, 2.1]), + (nan, [nan, nan, nan]), + ([0], [atol, inf, -inf, nan]), + (0, [atol, inf, -inf, nan]), + ] + self.some_close_results = [ + [True, False], + [True, False, False], + [True, True, False], + [False, False, False], + [True, False, False, False], + [True, False, False, False], + ] + + def test_ip_isclose(self): + self.setup() + tests = self.some_close_tests + results = self.some_close_results + for (x, y), result in zip(tests, results): + yield (assert_array_equal, isclose(x, y), result) + + def tst_all_isclose(self, x, y): + assert_(all(isclose(x, y)), "%s and %s not close" % (x, y)) + + def tst_none_isclose(self, x, y): + msg = "%s and %s shouldn't be close" + assert_(not any(isclose(x, y)), msg % (x, y)) + + def tst_isclose_allclose(self, x, y): + msg = "isclose.all() and allclose aren't same for %s and %s" + assert_array_equal(isclose(x, y).all(), allclose(x, y), msg % (x, y)) + + def test_ip_all_isclose(self): + self.setup() + for (x,y) in self.all_close_tests: + yield (self.tst_all_isclose, x, y) + + def test_ip_none_isclose(self): + self.setup() + for (x,y) in self.none_close_tests: + yield (self.tst_none_isclose, x, y) + + def test_ip_isclose_allclose(self): + self.setup() + tests = (self.all_close_tests + self.none_close_tests + + self.some_close_tests) + for (x, y) in tests: + yield (self.tst_isclose_allclose, x, y) + + def test_equal_nan(self): + assert_array_equal(isclose(nan, nan, equal_nan=True), [True]) + arr = array([1.0, nan]) + assert_array_equal(isclose(arr, arr, equal_nan=True), [True, True]) + + def test_masked_arrays(self): + x = np.ma.masked_where([True, True, False], np.arange(3)) + assert_(type(x) == type(isclose(2, x))) + + x = np.ma.masked_where([True, True, False], [nan, inf, nan]) + assert_(type(x) == type(isclose(inf, x))) + + x = np.ma.masked_where([True, True, False], [nan, nan, nan]) + y = isclose(nan, x, equal_nan=True) + assert_(type(x) == type(y)) + # Ensure that the mask isn't modified... + assert_array_equal([True, True, False], y.mask) + + def test_maskna_arrays(self): + x = array([NA, 1, 2, 3]) + y = array([0, 1, 2, NA]) + assert_array_equal(isclose(x, y), array([NA, True, True, NA])) + + assert_array_equal(isclose(NA, arange(3)), array([NA, NA, NA])) + + x = array([NA, nan, 2, 3]) + y = array([nan, 1, 2, NA]) + assert_array_equal(isclose(x, y), array([NA, False, True, NA])) + + def test_scalar_return(self): + assert_(isscalar(isclose(1, 1))) + + def test_no_parameter_modification(self): + x = array([inf, 1]) + y = array([0, inf]) + isclose(x, y) + assert_array_equal(x, array([inf, 1])) + assert_array_equal(y, array([0, inf])) + + class TestStdVar(TestCase): def setUp(self): self.A = array([1,-1,1,-1]) diff --git a/numpy/fft/fftpack.py b/numpy/fft/fftpack.py index 2f19ba3d8..80d21ec1d 100644 --- a/numpy/fft/fftpack.py +++ b/numpy/fft/fftpack.py @@ -511,7 +511,7 @@ def _cook_nd_args(a, s=None, axes=None, invreal=0): if len(s) != len(axes): raise ValueError("Shape and axes have different lengths.") if invreal and shapeless: - s[axes[-1]] = (s[axes[-1]] - 1) * 2 + s[-1] = (a.shape[axes[-1]] - 1) * 2 return s, axes diff --git a/numpy/fft/tests/test_fftpack.py b/numpy/fft/tests/test_fftpack.py index 4f70d3bc5..dceb3bd89 100644 --- a/numpy/fft/tests/test_fftpack.py +++ b/numpy/fft/tests/test_fftpack.py @@ -1,18 +1,22 @@ -from numpy.testing import * +from __future__ import division + import numpy as np +from numpy.testing import TestCase, run_module_suite, assert_array_almost_equal def fft1(x): L = len(x) phase = -2j*np.pi*(np.arange(L)/float(L)) - phase = np.arange(L).reshape(-1,1) * phase - return np.sum(x*np.exp(phase),axis=1) + phase = np.arange(L).reshape(-1, 1) * phase + return np.sum(x*np.exp(phase), axis=1) class TestFFTShift(TestCase): + def test_fft_n(self): - self.assertRaises(ValueError,np.fft.fft,[1,2,3],0) + self.assertRaises(ValueError, np.fft.fft, [1, 2, 3], 0) class TestFFT1D(TestCase): + def test_basic(self): rand = np.random.random x = rand(30) + 1j*rand(30) diff --git a/numpy/fft/tests/test_helper.py b/numpy/fft/tests/test_helper.py index 8ddac931f..c5578d390 100644 --- a/numpy/fft/tests/test_helper.py +++ b/numpy/fft/tests/test_helper.py @@ -2,48 +2,63 @@ # Copied from fftpack.helper by Pearu Peterson, October 2005 """ Test functions for fftpack.helper module """ +from __future__ import division -from numpy.testing import * -from numpy.fft import fftshift,ifftshift,fftfreq - +import numpy as np +from numpy.testing import TestCase, run_module_suite, assert_array_almost_equal +from numpy import fft from numpy import pi -def random(size): - return rand(*size) class TestFFTShift(TestCase): + def test_definition(self): - x = [0,1,2,3,4,-4,-3,-2,-1] - y = [-4,-3,-2,-1,0,1,2,3,4] - assert_array_almost_equal(fftshift(x),y) - assert_array_almost_equal(ifftshift(y),x) - x = [0,1,2,3,4,-5,-4,-3,-2,-1] - y = [-5,-4,-3,-2,-1,0,1,2,3,4] - assert_array_almost_equal(fftshift(x),y) - assert_array_almost_equal(ifftshift(y),x) + x = [0, 1, 2, 3, 4, -4, -3, -2, -1] + y = [-4, -3, -2, -1, 0, 1, 2, 3, 4] + assert_array_almost_equal(fft.fftshift(x), y) + assert_array_almost_equal(fft.ifftshift(y), x) + x = [0, 1, 2, 3, 4, -5, -4, -3, -2, -1] + y = [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4] + assert_array_almost_equal(fft.fftshift(x), y) + assert_array_almost_equal(fft.ifftshift(y), x) def test_inverse(self): for n in [1,4,9,100,211]: - x = random((n,)) - assert_array_almost_equal(ifftshift(fftshift(x)),x) - + x = np.random.random((n,)) + assert_array_almost_equal(fft.ifftshift(fft.fftshift(x)), x) + def test_axes_keyword(self): freqs = [[ 0, 1, 2], [ 3, 4, -4], [-3, -2, -1]] shifted = [[-1, -3, -2], [ 2, 0, 1], [-4, 3, 4]] - assert_array_almost_equal(fftshift(freqs, axes=(0, 1)), shifted) - assert_array_almost_equal(fftshift(freqs, axes=0), fftshift(freqs, axes=(0,))) - assert_array_almost_equal(ifftshift(shifted, axes=(0, 1)), freqs) - assert_array_almost_equal(ifftshift(shifted, axes=0), ifftshift(shifted, axes=(0,))) + assert_array_almost_equal(fft.fftshift(freqs, axes=(0, 1)), shifted) + assert_array_almost_equal(fft.fftshift(freqs, axes=0), + fft.fftshift(freqs, axes=(0,))) + assert_array_almost_equal(fft.ifftshift(shifted, axes=(0, 1)), freqs) + assert_array_almost_equal(fft.ifftshift(shifted, axes=0), + fft.ifftshift(shifted, axes=(0,))) class TestFFTFreq(TestCase): + def test_definition(self): - x = [0,1,2,3,4,-4,-3,-2,-1] - assert_array_almost_equal(9*fftfreq(9),x) - assert_array_almost_equal(9*pi*fftfreq(9,pi),x) - x = [0,1,2,3,4,-5,-4,-3,-2,-1] - assert_array_almost_equal(10*fftfreq(10),x) - assert_array_almost_equal(10*pi*fftfreq(10,pi),x) + x = [0, 1, 2, 3, 4, -4, -3, -2, -1] + assert_array_almost_equal(9*fft.fftfreq(9), x) + assert_array_almost_equal(9*pi*fft.fftfreq(9, pi), x) + x = [0, 1, 2, 3, 4, -5, -4, -3, -2, -1] + assert_array_almost_equal(10*fft.fftfreq(10), x) + assert_array_almost_equal(10*pi*fft.fftfreq(10, pi), x) + + +class TestIRFFTN(TestCase): + + def test_not_last_axis_success(self): + ar, ai = np.random.random((2, 16, 8, 32)) + a = ar + 1j*ai + + axes = (-2,) + + # Should not raise error + fft.irfftn(a, axes=axes) if __name__ == "__main__": |