summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/release/1.8.0-notes.rst13
-rw-r--r--numpy/core/numeric.py7
-rw-r--r--numpy/core/src/multiarray/convert_datatype.c50
-rw-r--r--numpy/core/src/multiarray/ctors.c58
-rw-r--r--numpy/core/src/multiarray/number.c20
-rw-r--r--numpy/core/src/umath/loops.c.src3
-rw-r--r--numpy/core/src/umath/simd.inc.src183
-rw-r--r--numpy/core/tests/test_api.py16
-rw-r--r--numpy/core/tests/test_numeric.py97
-rw-r--r--numpy/core/tests/test_records.py12
-rw-r--r--numpy/core/tests/test_ufunc.py30
-rw-r--r--numpy/lib/arraypad.py2
-rw-r--r--numpy/lib/tests/test_arraypad.py8
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)