diff options
-rw-r--r-- | doc/release/1.8.0-notes.rst | 13 | ||||
-rw-r--r-- | numpy/core/numeric.py | 7 | ||||
-rw-r--r-- | numpy/core/src/multiarray/convert_datatype.c | 50 | ||||
-rw-r--r-- | numpy/core/src/multiarray/ctors.c | 58 | ||||
-rw-r--r-- | numpy/core/src/multiarray/number.c | 20 | ||||
-rw-r--r-- | numpy/core/src/umath/loops.c.src | 3 | ||||
-rw-r--r-- | numpy/core/src/umath/simd.inc.src | 183 | ||||
-rw-r--r-- | numpy/core/tests/test_api.py | 16 | ||||
-rw-r--r-- | numpy/core/tests/test_numeric.py | 97 | ||||
-rw-r--r-- | numpy/core/tests/test_records.py | 12 | ||||
-rw-r--r-- | numpy/core/tests/test_ufunc.py | 30 | ||||
-rw-r--r-- | numpy/lib/arraypad.py | 2 | ||||
-rw-r--r-- | numpy/lib/tests/test_arraypad.py | 8 |
13 files changed, 453 insertions, 46 deletions
diff --git a/doc/release/1.8.0-notes.rst b/doc/release/1.8.0-notes.rst index e89fd87ce..33d3d1b0e 100644 --- a/doc/release/1.8.0-notes.rst +++ b/doc/release/1.8.0-notes.rst @@ -85,6 +85,19 @@ IO compatibility with large files Large NPZ files >2GB can be loaded on 64-bit systems. +Binary operations with non-arrays as second argument +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Binary operations of the form ``<array-or-subclass> * <non-array-subclass>`` +where ``<non-array-subclass>`` declares an ``__array_priority__`` higher than +that of ``<array-or-subclass>`` will now unconditionally return +*NotImplemented*, giving ``<non-array-subclass>`` a chance to handle the +operation. Previously, `NotImplemented` would only be returned if +``<non-array-subclass>`` actually implemented the reversed operation, and after +a (potentially expensive) array conversion of ``<non-array-subclass>`` had been +attempted. (`bug <https://github.com/numpy/numpy/issues/3375>`_, `pull request +<https://github.com/numpy/numpy/pull/3501>`_) + New Features ============ diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index 12b23cb83..13ee89744 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -2124,7 +2124,12 @@ def allclose(a, b, rtol=1.e-5, atol=1.e-8): x = x[~xinf] y = y[~xinf] - return all(less_equal(abs(x-y), atol + rtol * abs(y))) + # ignore invalid fpe's + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + r = all(less_equal(abs(x-y), atol + rtol * abs(y))) + + return r def isclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False): """ diff --git a/numpy/core/src/multiarray/convert_datatype.c b/numpy/core/src/multiarray/convert_datatype.c index 0d28ead89..fa88fa31a 100644 --- a/numpy/core/src/multiarray/convert_datatype.c +++ b/numpy/core/src/multiarray/convert_datatype.c @@ -226,24 +226,36 @@ PyArray_AdaptFlexibleDType(PyObject *data_obj, PyArray_Descr *data_dtype, break; case NPY_OBJECT: size = 64; - /* - * If we're adapting a string dtype for an array of string - * objects, call GetArrayParamsFromObject to figure out - * maximum string size, and use that as new dtype size. - */ if ((flex_type_num == NPY_STRING || flex_type_num == NPY_UNICODE) && data_obj != NULL) { - /* - * Convert data array to list of objects since - * GetArrayParamsFromObject won't iterate through - * items in an array. - */ - list = PyArray_ToList(data_obj); - if (list != NULL) { + if (PyArray_CheckScalar(data_obj)) { + PyObject *scalar = PyArray_ToList(data_obj); + if (scalar != NULL) { + PyObject *s = PyObject_Str(scalar); + if (s == NULL) { + Py_DECREF(scalar); + Py_DECREF(*flex_dtype); + *flex_dtype = NULL; + return; + } + else { + size = PyObject_Length(s); + Py_DECREF(s); + } + Py_DECREF(scalar); + } + } + else if (PyArray_Check(data_obj)) { + /* + * Convert data array to list of objects since + * GetArrayParamsFromObject won't iterate over + * array. + */ + list = PyArray_ToList(data_obj); result = PyArray_GetArrayParamsFromObject( list, - flex_dtype, + *flex_dtype, 0, &dtype, &ndim, dims, &arr, NULL); if (result == 0 && dtype != NULL) { @@ -256,6 +268,18 @@ PyArray_AdaptFlexibleDType(PyObject *data_obj, PyArray_Descr *data_dtype, } Py_DECREF(list); } + else if (PyArray_IsPythonScalar(data_obj)) { + PyObject *s = PyObject_Str(data_obj); + if (s == NULL) { + Py_DECREF(*flex_dtype); + *flex_dtype = NULL; + return; + } + else { + size = PyObject_Length(s); + Py_DECREF(s); + } + } } break; case NPY_STRING: diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c index 15f815f87..ace147ca5 100644 --- a/numpy/core/src/multiarray/ctors.c +++ b/numpy/core/src/multiarray/ctors.c @@ -521,7 +521,7 @@ PyArray_AssignFromSequence(PyArrayObject *self, PyObject *v) */ static int -discover_itemsize(PyObject *s, int nd, int *itemsize) +discover_itemsize(PyObject *s, int nd, int *itemsize, int size_as_string) { int n, r, i; @@ -532,14 +532,26 @@ discover_itemsize(PyObject *s, int nd, int *itemsize) if ((nd == 0) || PyString_Check(s) || #if defined(NPY_PY3K) - PyMemoryView_Check(s) || + PyMemoryView_Check(s) || #else - PyBuffer_Check(s) || + PyBuffer_Check(s) || #endif - PyUnicode_Check(s)) { + PyUnicode_Check(s)) { /* If an object has no length, leave it be */ - n = PyObject_Length(s); + if (size_as_string && s != NULL && !PyString_Check(s)) { + PyObject *s_string = PyObject_Str(s); + if (s_string) { + n = PyObject_Length(s_string); + Py_DECREF(s_string); + } + else { + n = -1; + } + } + else { + n = PyObject_Length(s); + } if (n == -1) { PyErr_Clear(); } @@ -557,7 +569,7 @@ discover_itemsize(PyObject *s, int nd, int *itemsize) return -1; } - r = discover_itemsize(e,nd-1,itemsize); + r = discover_itemsize(e, nd - 1, itemsize, size_as_string); Py_DECREF(e); if (r == -1) { return -1; @@ -1528,7 +1540,12 @@ PyArray_GetArrayParamsFromObject(PyObject *op, if ((*out_dtype)->elsize == 0 && PyTypeNum_ISEXTENDED((*out_dtype)->type_num)) { int itemsize = 0; - if (discover_itemsize(op, *out_ndim, &itemsize) < 0) { + int size_as_string = 0; + if ((*out_dtype)->type_num == NPY_STRING || + (*out_dtype)->type_num == NPY_UNICODE) { + size_as_string = 1; + } + if (discover_itemsize(op, *out_ndim, &itemsize, size_as_string) < 0) { Py_DECREF(*out_dtype); if (PyErr_Occurred() && PyErr_GivenExceptionMatches(PyErr_Occurred(), @@ -3474,9 +3491,9 @@ PyArray_FromIter(PyObject *obj, PyArray_Descr *dtype, npy_intp count) goto done; } elcount = (count < 0) ? 0 : count; - if ((elsize=dtype->elsize) == 0) { - PyErr_SetString(PyExc_ValueError, "Must specify length "\ - "when using variable-size data-type."); + if ((elsize = dtype->elsize) == 0) { + PyErr_SetString(PyExc_ValueError, + "Must specify length when using variable-size data-type."); goto done; } @@ -3485,8 +3502,8 @@ PyArray_FromIter(PyObject *obj, PyArray_Descr *dtype, npy_intp count) * reference counts before throwing away any memory. */ if (PyDataType_REFCHK(dtype)) { - PyErr_SetString(PyExc_ValueError, "cannot create "\ - "object arrays from iterator"); + PyErr_SetString(PyExc_ValueError, + "cannot create object arrays from iterator"); goto done; } @@ -3513,7 +3530,7 @@ PyArray_FromIter(PyObject *obj, PyArray_Descr *dtype, npy_intp count) } if (new_data == NULL) { PyErr_SetString(PyExc_MemoryError, - "cannot allocate array memory"); + "cannot allocate array memory"); Py_DECREF(value); goto done; } @@ -3521,16 +3538,21 @@ PyArray_FromIter(PyObject *obj, PyArray_Descr *dtype, npy_intp count) } PyArray_DIMS(ret)[0] = i + 1; - if (((item = index2ptr(ret, i)) == NULL) - || (PyArray_DESCR(ret)->f->setitem(value, item, ret) == -1)) { + if (((item = index2ptr(ret, i)) == NULL) || + (PyArray_DESCR(ret)->f->setitem(value, item, ret) == -1)) { Py_DECREF(value); goto done; } Py_DECREF(value); } + + if (PyErr_Occurred()) { + goto done; + } if (i < count) { - PyErr_SetString(PyExc_ValueError, "iterator too short"); + PyErr_SetString(PyExc_ValueError, + "iterator too short"); goto done; } @@ -3539,11 +3561,13 @@ PyArray_FromIter(PyObject *obj, PyArray_Descr *dtype, npy_intp count) * (assuming realloc is reasonably good about reusing space...) */ if (i == 0) { + /* The size cannot be zero for PyDataMem_RENEW. */ i = 1; } new_data = PyDataMem_RENEW(PyArray_DATA(ret), i * elsize); if (new_data == NULL) { - PyErr_SetString(PyExc_MemoryError, "cannot allocate array memory"); + PyErr_SetString(PyExc_MemoryError, + "cannot allocate array memory"); goto done; } ((PyArrayObject_fields *)ret)->data = new_data; diff --git a/numpy/core/src/multiarray/number.c b/numpy/core/src/multiarray/number.c index 9e13d0f29..392370667 100644 --- a/numpy/core/src/multiarray/number.c +++ b/numpy/core/src/multiarray/number.c @@ -209,6 +209,26 @@ PyArray_GenericBinaryFunction(PyArrayObject *m1, PyObject *m2, PyObject *op) Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } + + if (!PyArray_Check(m2)) { + /* + * Catch priority inversion and punt, but only if it's guaranteed + * that we were called through m1 and the other guy is not an array + * at all. Note that some arrays need to pass through here even + * with priorities inverted, for example: float(17) * np.matrix(...) + * + * See also: + * - https://github.com/numpy/numpy/issues/3502 + * - https://github.com/numpy/numpy/issues/3503 + */ + double m1_prio = PyArray_GetPriority(m1, NPY_SCALAR_PRIORITY); + double m2_prio = PyArray_GetPriority(m2, NPY_SCALAR_PRIORITY); + if (m1_prio < m2_prio) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + } + return PyObject_CallFunction(op, "OO", m1, m2); } diff --git a/numpy/core/src/umath/loops.c.src b/numpy/core/src/umath/loops.c.src index a99bdb9d2..0559fb416 100644 --- a/numpy/core/src/umath/loops.c.src +++ b/numpy/core/src/umath/loops.c.src @@ -1351,6 +1351,9 @@ NPY_NO_EXPORT void NPY_NO_EXPORT void @TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) { + if (run_binary_simd_@kind@_@TYPE@(args, dimensions, steps)) { + return; + } BINARY_LOOP { const @type@ in1 = *(@type@ *)ip1; const @type@ in2 = *(@type@ *)ip2; diff --git a/numpy/core/src/umath/simd.inc.src b/numpy/core/src/umath/simd.inc.src index dc3b6ad8e..98e2beb30 100644 --- a/numpy/core/src/umath/simd.inc.src +++ b/numpy/core/src/umath/simd.inc.src @@ -24,6 +24,7 @@ #endif #include <assert.h> #include <stdlib.h> +#include <string.h> /* for memcpy */ int PyUFunc_getfperr(void); void PyUFunc_clearfperr(void); @@ -59,6 +60,19 @@ void PyUFunc_clearfperr(void); ((abs(args[2] - args[0]) >= (vsize)) || (abs(args[2] - args[0]) == 0)) && \ abs(args[2] - args[1]) >= (esize)) +#define IS_BLOCKABLE_BINARY_BOOL(esize, vsize) \ + (steps[0] == (esize) && steps[0] == steps[1] && steps[2] == (1) && \ + npy_is_aligned(args[1], (esize)) && \ + npy_is_aligned(args[0], (esize))) + +#define IS_BLOCKABLE_BINARY_SCALAR1_BOOL(esize, vsize) \ + (steps[0] == 0 && steps[1] == (esize) && steps[2] == (1) && \ + npy_is_aligned(args[1], (esize))) + +#define IS_BLOCKABLE_BINARY_SCALAR2_BOOL(esize, vsize) \ + (steps[0] == (esize) && steps[1] == 0 && steps[2] == (1) && \ + npy_is_aligned(args[0], (esize))) + /* align var to alignment */ #define LOOP_BLOCK_ALIGN_VAR(var, type, alignment)\ npy_intp i, peel = npy_aligned_block_offset(var, sizeof(type),\ @@ -72,6 +86,35 @@ void PyUFunc_clearfperr(void); #define LOOP_BLOCKED_END\ for (; i < n; i++) +/* fanout two bits to two bytes */ +static const npy_int16 fanout_2[] = { + 0x0000, + 0x0001, + 0x0100, + 0x0101, +}; + +/* fanout four bits to four bytes */ +static const npy_int32 fanout_4[] = { + 0x00000000, + 0x00000001, + 0x00000100, + 0x00000101, + 0x00010000, + 0x00010001, + 0x00010100, + 0x00010101, + 0x01000000, + 0x01000001, + 0x01000100, + 0x01000101, + 0x01010000, + 0x01010001, + 0x01010100, + 0x01010101 +}; + + /* * Dispatcher functions * decide whether the operation can be vectorized and run it @@ -122,7 +165,6 @@ run_@name@_simd_@func@_@TYPE@(char **args, npy_intp *dimensions, npy_intp *steps /**begin repeat1 * Arithmetic * # kind = add, subtract, multiply, divide# - * # OP = +, -, *, /# */ #if @vector@ && defined HAVE_EMMINTRIN_H @@ -168,6 +210,55 @@ run_binary_simd_@kind@_@TYPE@(char **args, npy_intp *dimensions, npy_intp *steps /**end repeat1**/ +/**begin repeat1 + * #kind = equal, not_equal, less, less_equal, greater, greater_equal, + * logical_and, logical_or# + * #simd = 1, 1, 1, 1, 1, 1, 0, 0# + */ + +#if @vector@ && @simd@ && defined HAVE_EMMINTRIN_H + +/* prototypes */ +static void +sse2_binary_@kind@_@TYPE@(npy_bool * op, @type@ * ip1, @type@ * ip2, + npy_intp n); +static void +sse2_binary_scalar1_@kind@_@TYPE@(npy_bool * op, @type@ * ip1, @type@ * ip2, + npy_intp n); +static void +sse2_binary_scalar2_@kind@_@TYPE@(npy_bool * op, @type@ * ip1, @type@ * ip2, + npy_intp n); + +#endif + +static NPY_INLINE int +run_binary_simd_@kind@_@TYPE@(char **args, npy_intp *dimensions, npy_intp *steps) +{ +#if @vector@ && @simd@ && defined HAVE_EMMINTRIN_H + @type@ * ip1 = (@type@ *)args[0]; + @type@ * ip2 = (@type@ *)args[1]; + npy_bool * op = (npy_bool *)args[2]; + npy_intp n = dimensions[0]; + /* argument one scalar */ + if (IS_BLOCKABLE_BINARY_SCALAR1_BOOL(sizeof(@type@), 16)) { + sse2_binary_scalar1_@kind@_@TYPE@(op, ip1, ip2, n); + return 1; + } + /* argument two scalar */ + else if (IS_BLOCKABLE_BINARY_SCALAR2_BOOL(sizeof(@type@), 16)) { + sse2_binary_scalar2_@kind@_@TYPE@(op, ip1, ip2, n); + return 1; + } + else if (IS_BLOCKABLE_BINARY_BOOL(sizeof(@type@), 16)) { + sse2_binary_@kind@_@TYPE@(op, ip1, ip2, n); + return 1; + } +#endif + return 0; +} + +/**end repeat1**/ + /**end repeat**/ /* @@ -281,7 +372,10 @@ static NPY_INLINE npy_double sse2_horizontal_@VOP@___m128d(__m128d v) * #vtype = __m128, __m128d# * #vpre = _mm, _mm# * #vsuf = ps, pd# + * #vsufs = ss, sd# * #nan = NPY_NANF, NPY_NAN# + * #mtype = npy_int32, npy_int16# + * #fanout = fanout_4, fanout_2# */ @@ -407,6 +501,93 @@ sse2_binary_scalar2_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_i /**end repeat1**/ +/**begin repeat1 + * #kind = equal, not_equal, less, less_equal, greater, greater_equal# + * #OP = ==, !=, <, <=, >, >=# + * #VOP = cmpeq, cmpneq, cmplt, cmple, cmpgt, cmpge# +*/ + +/* sets invalid fpu flag on QNaN for consistency with packed compare */ +static NPY_INLINE int +sse2_ordered_cmp_@kind@_@TYPE@(const @type@ a, const @type@ b) +{ + @type@ tmp; + @vtype@ v = @vpre@_@VOP@_@vsufs@(@vpre@_load_@vsufs@(&a), + @vpre@_load_@vsufs@(&b)); + @vpre@_store_@vsufs@(&tmp, v); + return sizeof(@type@) == 4 ? + (*(npy_uint32 *)&tmp) & 1 : (*(npy_uint64 *)&tmp) & 1; +} + +static void +sse2_binary_@kind@_@TYPE@(npy_bool * op, @type@ * ip1, @type@ * ip2, npy_intp n) +{ + npy_bool * r; + LOOP_BLOCK_ALIGN_VAR(ip1, @type@, 16) { + op[i] = sse2_ordered_cmp_@kind@_@TYPE@(ip1[i], ip2[i]); + } + r = &op[i]; + LOOP_BLOCKED(@type@, 16) { + @vtype@ a = @vpre@_load_@vsuf@(&ip1[i]); + @vtype@ b = @vpre@_loadu_@vsuf@(&ip2[i]); + @vtype@ c = @vpre@_@VOP@_@vsuf@(a, b); + @mtype@ ir = @fanout@[@vpre@_movemask_@vsuf@(c)]; + /* may be unaligned */ + memcpy(r, &ir, sizeof(ir)); + r += sizeof(ir); + } + LOOP_BLOCKED_END { + op[i] = sse2_ordered_cmp_@kind@_@TYPE@(ip1[i], ip2[i]); + } +} + + +static void +sse2_binary_scalar1_@kind@_@TYPE@(npy_bool * op, @type@ * ip1, @type@ * ip2, npy_intp n) +{ + npy_bool * r; + @vtype@ a = @vpre@_set1_@vsuf@(ip1[0]); + LOOP_BLOCK_ALIGN_VAR(ip2, @type@, 16) { + op[i] = sse2_ordered_cmp_@kind@_@TYPE@(ip1[0], ip2[i]); + } + r = &op[i]; + LOOP_BLOCKED(@type@, 16) { + @vtype@ b = @vpre@_load_@vsuf@(&ip2[i]); + @vtype@ c = @vpre@_@VOP@_@vsuf@(a, b); + @mtype@ ir = @fanout@[@vpre@_movemask_@vsuf@(c)]; + /* may be unaligned */ + memcpy(r, &ir, sizeof(ir)); + r += sizeof(ir); + } + LOOP_BLOCKED_END { + op[i] = sse2_ordered_cmp_@kind@_@TYPE@(ip1[0], ip2[i]); + } +} + + +static void +sse2_binary_scalar2_@kind@_@TYPE@(npy_bool * op, @type@ * ip1, @type@ * ip2, npy_intp n) +{ + npy_bool * r; + @vtype@ b = @vpre@_set1_@vsuf@(ip2[0]); + LOOP_BLOCK_ALIGN_VAR(ip1, @type@, 16) { + op[i] = sse2_ordered_cmp_@kind@_@TYPE@(ip1[i], ip2[0]); + } + r = &op[i]; + LOOP_BLOCKED(@type@, 16) { + @vtype@ a = @vpre@_load_@vsuf@(&ip1[i]); + @vtype@ c = @vpre@_@VOP@_@vsuf@(a, b); + @mtype@ ir = @fanout@[@vpre@_movemask_@vsuf@(c)]; + /* may be unaligned */ + memcpy(r, &ir, sizeof(ir)); + r += sizeof(ir); + } + LOOP_BLOCKED_END { + op[i] = sse2_ordered_cmp_@kind@_@TYPE@(ip1[i], ip2[0]); + } +} +/**end repeat1**/ + static void sse2_sqrt_@TYPE@(@type@ * op, @type@ * ip, const npy_intp n) { diff --git a/numpy/core/tests/test_api.py b/numpy/core/tests/test_api.py index c681b57ab..b94f4772e 100644 --- a/numpy/core/tests/test_api.py +++ b/numpy/core/tests/test_api.py @@ -259,6 +259,22 @@ def test_array_astype(): assert_equal(a, b) assert_equal(b.dtype, np.dtype('U10')) + a = np.array(123456789012345678901234567890, dtype='O').astype('S') + assert_array_equal(a, np.array(b'1234567890' * 3, dtype='S30')) + a = np.array(123456789012345678901234567890, dtype='O').astype('U') + assert_array_equal(a, np.array(sixu('1234567890' * 3), dtype='U30')) + + a = np.array([123456789012345678901234567890], dtype='O').astype('S') + assert_array_equal(a, np.array(b'1234567890' * 3, dtype='S30')) + a = np.array([123456789012345678901234567890], dtype='O').astype('U') + assert_array_equal(a, np.array(sixu('1234567890' * 3), dtype='U30')) + + a = np.array(123456789012345678901234567890, dtype='S') + assert_array_equal(a, np.array(b'1234567890' * 3, dtype='S30')) + a = np.array(123456789012345678901234567890, dtype='U') + assert_array_equal(a, np.array(sixu('1234567890' * 3), dtype='U30')) + + def test_copyto_fromscalar(): a = np.arange(6, dtype='f4').reshape(2,3) diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py index 2e727fdd7..751722ffb 100644 --- a/numpy/core/tests/test_numeric.py +++ b/numpy/core/tests/test_numeric.py @@ -293,6 +293,53 @@ class TestBoolArray(TestCase): assert_array_equal(self.im ^ False, self.im) +class TestBoolCmp(TestCase): + def setUp(self): + self.f = ones(256, dtype=np.float32) + self.ef = ones(self.f.size, dtype=np.bool) + self.d = ones(128, dtype=np.float64) + self.ed = ones(self.d.size, dtype=np.bool) + # generate values for all permutation of 256bit simd vectors + s = 0 + for i in range(32): + self.f[s:s+8] = [i & 2**x for x in range(8)] + self.ef[s:s+8] = [(i & 2**x) != 0 for x in range(8)] + s += 8 + s = 0 + for i in range(16): + self.d[s:s+4] = [i & 2**x for x in range(4)] + self.ed[s:s+4] = [(i & 2**x) != 0 for x in range(4)] + s += 4 + + def test_float(self): + # offset for alignment test + for i in range(4): + assert_array_equal(self.f[i:] != 0, self.ef[i:]) + assert_array_equal(self.f[i:] > 0, self.ef[i:]) + assert_array_equal(self.f[i:] - 1 >= 0, self.ef[i:]) + assert_array_equal(self.f[i:] == 0, ~self.ef[i:]) + assert_array_equal(-self.f[i:] < 0, self.ef[i:]) + assert_array_equal(-self.f[i:] + 1 <= 0, self.ef[i:]) + + assert_array_equal(0 != self.f[i:], self.ef[i:]) + assert_array_equal(np.zeros_like(self.f)[i:] != self.f[i:], + self.ef[i:]) + + def test_double(self): + # offset for alignment test + for i in range(2): + assert_array_equal(self.d[i:] != 0, self.ed[i:]) + assert_array_equal(self.d[i:] > 0, self.ed[i:]) + assert_array_equal(self.d[i:] - 1 >= 0, self.ed[i:]) + assert_array_equal(self.d[i:] == 0, ~self.ed[i:]) + assert_array_equal(-self.d[i:] < 0, self.ed[i:]) + assert_array_equal(-self.d[i:] + 1 <= 0, self.ed[i:]) + + assert_array_equal(0 != self.d[i:], self.ed[i:]) + assert_array_equal(np.zeros_like(self.d)[i:] != self.d[i:], + self.ed[i:]) + + class TestSeterr(TestCase): def test_default(self): err = geterr() @@ -346,13 +393,12 @@ class TestFloatExceptions(TestCase): "Type %s raised wrong fpe error '%s'." % (ftype, exc)) def assert_op_raises_fpe(self, fpeerr, flop, sc1, sc2): - """Check that fpe exception is raised. - - Given a floating operation `flop` and two scalar values, check that - the operation raises the floating point exception specified by - `fpeerr`. Tests all variants with 0-d array scalars as well. + # Check that fpe exception is raised. + # + # Given a floating operation `flop` and two scalar values, check that + # the operation raises the floating point exception specified by + #`fpeerr`. Tests all variants with 0-d array scalars as well. - """ self.assert_raises_fpe(fpeerr, flop, sc1, sc2); self.assert_raises_fpe(fpeerr, flop, sc1[()], sc2); self.assert_raises_fpe(fpeerr, flop, sc1, sc2[()]); @@ -360,7 +406,7 @@ class TestFloatExceptions(TestCase): @dec.knownfailureif(True, "See ticket 1755") def test_floating_exceptions(self): - """Test basic arithmetic function errors""" + # Test basic arithmetic function errors oldsettings = np.seterr(all='raise') try: # Test for all real and complex float types @@ -476,9 +522,11 @@ class TestTypes(TestCase): assert_equal(promote_func(array([b]),u64), np.dtype(uint64)) assert_equal(promote_func(array([i8]),f64), np.dtype(float64)) assert_equal(promote_func(array([u16]),f64), np.dtype(float64)) + # uint and int are treated as the same "kind" for # the purposes of array-scalar promotion. assert_equal(promote_func(array([u16]), i32), np.dtype(uint16)) + # float and complex are treated as the same "kind" for # the purposes of array-scalar promotion, so that you can do # (0j + float32array) to get a complex64 array instead of @@ -588,6 +636,11 @@ class TestTypes(TestCase): assert_raises(TypeError, np.can_cast, 'i4', None) assert_raises(TypeError, np.can_cast, None, 'i4') + +# Custom exception class to test exception propagation in fromiter +class NIterError(Exception): pass + + class TestFromiter(TestCase): def makegen(self): for x in range(24): @@ -607,12 +660,8 @@ class TestFromiter(TestCase): a20 = fromiter(self.makegen(), int, 20) self.assertTrue(len(a) == len(expected)) self.assertTrue(len(a20) == 20) - try: - fromiter(self.makegen(), int, len(expected) + 10) - except ValueError: - pass - else: - self.fail() + self.assertRaises(ValueError, fromiter, + self.makegen(), int, len(expected) + 10) def test_values(self): expected = array(list(self.makegen())) @@ -621,6 +670,28 @@ class TestFromiter(TestCase): self.assertTrue(alltrue(a == expected,axis=0)) self.assertTrue(alltrue(a20 == expected[:20],axis=0)) + def load_data(self, n, eindex): + # Utility method for the issue 2592 tests. + # Raise an exception at the desired index in the iterator. + for e in range(n): + if e == eindex: + raise NIterError('error at index %s' % eindex) + yield e + + def test_2592(self): + # Test iteration exceptions are correctly raised. + count, eindex = 10, 5 + self.assertRaises(NIterError, np.fromiter, + self.load_data(count, eindex), dtype=int, count=count) + + def test_2592_edge(self): + # Test iter. exceptions, edge case (exception at end of iterator). + count = 10 + eindex = count-1 + self.assertRaises(NIterError, np.fromiter, + self.load_data(count, eindex), dtype=int, count=count) + + class TestNonzero(TestCase): def test_nonzero_trivial(self): assert_equal(np.count_nonzero(array([])), 0) diff --git a/numpy/core/tests/test_records.py b/numpy/core/tests/test_records.py index 753dec13e..8c9ce5c70 100644 --- a/numpy/core/tests/test_records.py +++ b/numpy/core/tests/test_records.py @@ -7,6 +7,7 @@ from numpy.compat import asbytes, asunicode import warnings import collections +import pickle class TestFromrecords(TestCase): @@ -146,6 +147,17 @@ class TestRecord(TestCase): y = self.data[['col2', 'col1']] assert_equal(x[0][0], y[0][1]) + def test_pickle_1(self): + # Issue #1529 + a = np.array([(1, [])], dtype=[('a', np.int32), ('b', np.int32, 0)]) + assert_equal(a, pickle.loads(pickle.dumps(a))) + assert_equal(a[0], pickle.loads(pickle.dumps(a[0]))) + + def test_pickle_2(self): + a = self.data + assert_equal(a, pickle.loads(pickle.dumps(a))) + assert_equal(a[0], pickle.loads(pickle.dumps(a[0]))) + def test_find_duplicate(): l1 = [1, 2, 3, 4, 5, 6] diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py index ad489124e..e4698abc6 100644 --- a/numpy/core/tests/test_ufunc.py +++ b/numpy/core/tests/test_ufunc.py @@ -834,5 +834,35 @@ class TestUfunc(TestCase): dtype=rational); assert_equal(result, expected); + def test_custom_array_like(self): + class MyThing(object): + __array_priority__ = 1000 + + rmul_count = 0 + getitem_count = 0 + + def __init__(self, shape): + self.shape = shape + + def __len__(self): + return self.shape[0] + + def __getitem__(self, i): + MyThing.getitem_count += 1 + if not isinstance(i, tuple): + i = (i,) + if len(i) > len(self.shape): + raise IndexError("boo") + + return MyThing(self.shape[len(i):]) + + def __rmul__(self, other): + MyThing.rmul_count += 1 + return self + + np.float64(5)*MyThing((3, 3)) + assert_(MyThing.rmul_count == 1, MyThing.rmul_count) + assert_(MyThing.getitem_count <= 2, MyThing.getitem_count) + if __name__ == "__main__": run_module_suite() diff --git a/numpy/lib/arraypad.py b/numpy/lib/arraypad.py index c80f6f54d..90b5fcfcd 100644 --- a/numpy/lib/arraypad.py +++ b/numpy/lib/arraypad.py @@ -1079,7 +1079,7 @@ def _validate_lengths(narray, number_elements): normshp = _normalize_shape(narray, number_elements) for i in normshp: chk = [1 if x is None else x for x in i] - chk = [1 if x > 0 else -1 for x in chk] + chk = [1 if x >= 0 else -1 for x in chk] if (chk[0] < 0) or (chk[1] < 0): fmt = "%s cannot contain negative values." raise ValueError(fmt % (number_elements,)) diff --git a/numpy/lib/tests/test_arraypad.py b/numpy/lib/tests/test_arraypad.py index c6acd4db0..ec96f5c8c 100644 --- a/numpy/lib/tests/test_arraypad.py +++ b/numpy/lib/tests/test_arraypad.py @@ -486,6 +486,14 @@ class TestEdge(TestCase): assert_array_equal(a, b) +class TestZeroPadWidth(TestCase): + def test_zero_pad_width(self): + arr = np.arange(30) + arr = np.reshape(arr, (6, 5)) + for pad_width in (0, (0, 0), ((0, 0), (0, 0))): + assert_array_equal(arr, pad(arr, pad_width, mode='constant')) + + class ValueError1(TestCase): def test_check_simple(self): arr = np.arange(30) |