summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/release/1.9.0-notes.rst18
-rw-r--r--doc/source/reference/c-api.iterator.rst22
-rw-r--r--numpy/core/include/numpy/ndarraytypes.h4
-rw-r--r--numpy/core/src/multiarray/array_assign_array.c8
-rw-r--r--numpy/core/src/multiarray/array_assign_scalar.c8
-rw-r--r--numpy/core/src/multiarray/arraytypes.c.src13
-rw-r--r--numpy/core/src/multiarray/common.c2
-rw-r--r--numpy/core/src/multiarray/common.h22
-rw-r--r--numpy/core/src/multiarray/ctors.c10
-rw-r--r--numpy/core/src/multiarray/einsum.c.src12
-rw-r--r--numpy/core/src/multiarray/item_selection.c77
-rw-r--r--numpy/core/src/multiarray/iterators.c12
-rw-r--r--numpy/core/src/multiarray/lowlevel_strided_loops.c.src8
-rw-r--r--numpy/core/src/multiarray/mapping.c9
-rw-r--r--numpy/core/src/multiarray/methods.c4
-rw-r--r--numpy/core/src/multiarray/multiarray_tests.c.src106
-rw-r--r--numpy/core/src/multiarray/multiarraymodule.c8
-rw-r--r--numpy/core/src/multiarray/nditer_api.c39
-rw-r--r--numpy/core/src/multiarray/nditer_constr.c17
-rw-r--r--numpy/core/src/multiarray/nditer_pywrap.c25
-rw-r--r--numpy/core/src/multiarray/nditer_templ.c.src10
-rw-r--r--numpy/core/src/umath/ufunc_object.c79
-rw-r--r--numpy/core/tests/test_nditer.py40
-rw-r--r--numpy/core/tests/test_numeric.py14
-rw-r--r--numpy/lib/function_base.py7
-rw-r--r--numpy/lib/src/_compiled_base.c10
-rw-r--r--numpy/lib/tests/test_function_base.py6
-rw-r--r--numpy/linalg/tests/test_regression.py6
-rw-r--r--numpy/testing/utils.py4
29 files changed, 452 insertions, 148 deletions
diff --git a/doc/release/1.9.0-notes.rst b/doc/release/1.9.0-notes.rst
index f999f29b5..2e457ad84 100644
--- a/doc/release/1.9.0-notes.rst
+++ b/doc/release/1.9.0-notes.rst
@@ -227,10 +227,26 @@ integer/float array that is being casted. Previously the casting was
allowed even if the result was truncated.
+NDIter
+~~~~~~
+When ``NpyIter_RemoveAxis`` is now called, the iterator range will be reset.
+
+When a multi index is being tracked and an iterator is not buffered, it is
+possible to use ``NpyIter_RemoveAxis``. In this case an iterator can shrink
+in size. Because the total size of an iterator is limited, the iterator
+may be too large before these calls. In this case its size will be set to ``-1``
+and an error issued not at construction time but when removing the multi
+index, setting the iterator range, or getting the next function.
+
+This has no effect on currently working code, but highlights the necessity
+of checking for an error return if these conditions can occur. In most
+cases the arrays being iterated are as large as the iterator so that such
+a problem cannot occur.
+
+
C-API
~~~~~
-None
Deprecations
============
diff --git a/doc/source/reference/c-api.iterator.rst b/doc/source/reference/c-api.iterator.rst
index 569d8ec56..084fdcbce 100644
--- a/doc/source/reference/c-api.iterator.rst
+++ b/doc/source/reference/c-api.iterator.rst
@@ -369,7 +369,14 @@ Construction and Destruction
Causes the iterator to track a multi-index.
This prevents the iterator from coalescing axes to
- produce bigger inner loops.
+ produce bigger inner loops. If the loop is also not buffered
+ and no index is being tracked (`NpyIter_RemoveAxis` can be called),
+ then the iterator size can be ``-1`` to indicate that the iterator
+ is too large. This can happen due to complex broadcasting and
+ will result in errors being created when the setting the iterator
+ range, removing the multi index, or getting the next function.
+ However, it is possible to remove axes again and use the iterator
+ normally if the size is small enough after removal.
.. cvar:: NPY_ITER_EXTERNAL_LOOP
@@ -412,8 +419,9 @@ Construction and Destruction
Indicates that arrays with a size of zero should be permitted.
Since the typical iteration loop does not naturally work with
- zero-sized arrays, you must check that the IterSize is non-zero
- before entering the iteration loop.
+ zero-sized arrays, you must check that the IterSize is larger
+ than zero before entering the iteration loop.
+ Currently only the operands are checked, not a forced shape.
.. cvar:: NPY_ITER_REDUCE_OK
@@ -721,7 +729,7 @@ Construction and Destruction
**WARNING**: This function may change the internal memory layout of
the iterator. Any cached functions or pointers from the iterator
- must be retrieved again!
+ must be retrieved again! The iterator range will be reset as well.
Returns ``NPY_SUCCEED`` or ``NPY_FAIL``.
@@ -887,7 +895,11 @@ Construction and Destruction
.. cfunction:: npy_intp NpyIter_GetIterSize(NpyIter* iter)
Returns the number of elements being iterated. This is the product
- of all the dimensions in the shape.
+ of all the dimensions in the shape. When a multi index is being tracked
+ (and `NpyIter_RemoveAxis` may be called) the size may be ``-1`` to
+ indicate an iterator is too large. Such an iterator is invalid, but
+ may become valid after `NpyIter_RemoveAxis` is called. It is not
+ necessary to check for this case.
.. cfunction:: npy_intp NpyIter_GetIterIndex(NpyIter* iter)
diff --git a/numpy/core/include/numpy/ndarraytypes.h b/numpy/core/include/numpy/ndarraytypes.h
index 27970ff77..cc0206d44 100644
--- a/numpy/core/include/numpy/ndarraytypes.h
+++ b/numpy/core/include/numpy/ndarraytypes.h
@@ -928,10 +928,11 @@ typedef int (PyArray_FinalizeFunc)(PyArrayObject *, PyObject *);
#define PyArray_IS_C_CONTIGUOUS(m) PyArray_CHKFLAGS(m, NPY_ARRAY_C_CONTIGUOUS)
#define PyArray_IS_F_CONTIGUOUS(m) PyArray_CHKFLAGS(m, NPY_ARRAY_F_CONTIGUOUS)
+/* the variable is used in some places, so always define it */
+#define NPY_BEGIN_THREADS_DEF PyThreadState *_save=NULL;
#if NPY_ALLOW_THREADS
#define NPY_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
#define NPY_END_ALLOW_THREADS Py_END_ALLOW_THREADS
-#define NPY_BEGIN_THREADS_DEF PyThreadState *_save=NULL;
#define NPY_BEGIN_THREADS do {_save = PyEval_SaveThread();} while (0);
#define NPY_END_THREADS do {if (_save) PyEval_RestoreThread(_save);} while (0);
#define NPY_BEGIN_THREADS_THRESHOLDED(loop_size) do { if (loop_size > 500) \
@@ -951,7 +952,6 @@ typedef int (PyArray_FinalizeFunc)(PyArrayObject *, PyObject *);
#else
#define NPY_BEGIN_ALLOW_THREADS
#define NPY_END_ALLOW_THREADS
-#define NPY_BEGIN_THREADS_DEF
#define NPY_BEGIN_THREADS
#define NPY_END_THREADS
#define NPY_BEGIN_THREADS_THRESHOLDED(loop_size)
diff --git a/numpy/core/src/multiarray/array_assign_array.c b/numpy/core/src/multiarray/array_assign_array.c
index ba18684a2..67c98d8b5 100644
--- a/numpy/core/src/multiarray/array_assign_array.c
+++ b/numpy/core/src/multiarray/array_assign_array.c
@@ -99,9 +99,7 @@ raw_array_assign_array(int ndim, npy_intp *shape,
dst_data, dst_strides_it,
src_data, src_strides_it);
- if (!needs_api) {
- NPY_END_THREADS;
- }
+ NPY_END_THREADS;
NPY_AUXDATA_FREE(transferdata);
@@ -194,9 +192,7 @@ raw_array_wheremasked_assign_array(int ndim, npy_intp *shape,
src_data, src_strides_it,
wheremask_data, wheremask_strides_it);
- if (!needs_api) {
- NPY_END_THREADS;
- }
+ NPY_END_THREADS;
NPY_AUXDATA_FREE(transferdata);
diff --git a/numpy/core/src/multiarray/array_assign_scalar.c b/numpy/core/src/multiarray/array_assign_scalar.c
index 93bc4e4b4..7c1b1f16a 100644
--- a/numpy/core/src/multiarray/array_assign_scalar.c
+++ b/numpy/core/src/multiarray/array_assign_scalar.c
@@ -86,9 +86,7 @@ raw_array_assign_scalar(int ndim, npy_intp *shape,
} NPY_RAW_ITER_ONE_NEXT(idim, ndim, coord,
shape_it, dst_data, dst_strides_it);
- if (!needs_api) {
- NPY_END_THREADS;
- }
+ NPY_END_THREADS;
NPY_AUXDATA_FREE(transferdata);
@@ -165,9 +163,7 @@ raw_array_wheremasked_assign_scalar(int ndim, npy_intp *shape,
dst_data, dst_strides_it,
wheremask_data, wheremask_strides_it);
- if (!needs_api) {
- NPY_END_THREADS;
- }
+ NPY_END_THREADS;
NPY_AUXDATA_FREE(transferdata);
diff --git a/numpy/core/src/multiarray/arraytypes.c.src b/numpy/core/src/multiarray/arraytypes.c.src
index 0f75c8aa5..a430e1ee4 100644
--- a/numpy/core/src/multiarray/arraytypes.c.src
+++ b/numpy/core/src/multiarray/arraytypes.c.src
@@ -3501,15 +3501,22 @@ static int
NPY_CLIPMODE clipmode)
{
npy_intp i, j, k, tmp;
+ NPY_BEGIN_THREADS_DEF;
+
+ NPY_BEGIN_THREADS;
switch(clipmode) {
case NPY_RAISE:
for (i = 0; i < n_outer; i++) {
for (j = 0; j < m_middle; j++) {
tmp = indarray[j];
- /* We don't know what axis we're operating on, so don't report it in case of an error. */
- if (check_and_adjust_index(&tmp, nindarray, -1) < 0)
+ /*
+ * We don't know what axis we're operating on,
+ * so don't report it in case of an error.
+ */
+ if (check_and_adjust_index(&tmp, nindarray, -1, _save) < 0) {
return 1;
+ }
if (NPY_LIKELY(nelem == 1)) {
*dest++ = *(src + tmp);
}
@@ -3571,6 +3578,8 @@ static int
}
break;
}
+
+ NPY_END_THREADS;
return 0;
}
/**end repeat**/
diff --git a/numpy/core/src/multiarray/common.c b/numpy/core/src/multiarray/common.c
index 48d13cd6e..0e8a21394 100644
--- a/numpy/core/src/multiarray/common.c
+++ b/numpy/core/src/multiarray/common.c
@@ -618,7 +618,7 @@ index2ptr(PyArrayObject *mp, npy_intp i)
return NULL;
}
dim0 = PyArray_DIMS(mp)[0];
- if (check_and_adjust_index(&i, dim0, 0) < 0)
+ if (check_and_adjust_index(&i, dim0, 0, NULL) < 0)
return NULL;
if (i == 0) {
return PyArray_DATA(mp);
diff --git a/numpy/core/src/multiarray/common.h b/numpy/core/src/multiarray/common.h
index ff1d4ec77..d64411115 100644
--- a/numpy/core/src/multiarray/common.h
+++ b/numpy/core/src/multiarray/common.h
@@ -2,9 +2,21 @@
#define _NPY_PRIVATE_COMMON_H_
#include <numpy/npy_common.h>
#include <numpy/npy_cpu.h>
+#include <numpy/ndarraytypes.h>
#define error_converting(x) (((x) == -1) && PyErr_Occurred())
+#ifdef NPY_ALLOW_THREADS
+#define NPY_BEGIN_THREADS_NDITER(iter) \
+ do { \
+ if (!NpyIter_IterationNeedsAPI(iter)) { \
+ NPY_BEGIN_THREADS_THRESHOLDED(NpyIter_GetIterSize(iter)); \
+ } \
+ } while(0)
+#else
+#define NPY_BEGIN_THREADS_NDITER(iter)
+#endif
+
/*
* Recursively examines the object to determine an appropriate dtype
* to use for converting to an ndarray.
@@ -70,12 +82,17 @@ convert_shape_to_string(npy_intp n, npy_intp *vals, char *ending);
* 0 <= *index < max_item, and returns 0.
* 'axis' should be the array axis that is being indexed over, if known. If
* unknown, use -1.
+ * If _save is NULL it is assumed the GIL is taken
+ * If _save is not NULL it is assumed the GIL is not taken and it
+ * is acquired in the case of an error
*/
static NPY_INLINE int
-check_and_adjust_index(npy_intp *index, npy_intp max_item, int axis)
+check_and_adjust_index(npy_intp *index, npy_intp max_item, int axis,
+ PyThreadState * _save)
{
/* Check that index is valid, taking into account negative indices */
if (NPY_UNLIKELY((*index < -max_item) || (*index >= max_item))) {
+ NPY_END_THREADS;
/* Try to be as clear as possible about what went wrong. */
if (axis >= 0) {
PyErr_Format(PyExc_IndexError,
@@ -85,8 +102,7 @@ check_and_adjust_index(npy_intp *index, npy_intp max_item, int axis)
} else {
PyErr_Format(PyExc_IndexError,
"index %"NPY_INTP_FMT" is out of bounds "
- "for size %"NPY_INTP_FMT,
- *index, max_item);
+ "for size %"NPY_INTP_FMT, *index, max_item);
}
return -1;
}
diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c
index 5bdd5b552..2ab46d258 100644
--- a/numpy/core/src/multiarray/ctors.c
+++ b/numpy/core/src/multiarray/ctors.c
@@ -2635,9 +2635,7 @@ PyArray_CopyAsFlat(PyArrayObject *dst, PyArrayObject *src, NPY_ORDER order)
}
}
- if (!needs_api) {
- NPY_END_THREADS;
- }
+ NPY_END_THREADS;
NPY_AUXDATA_FREE(transferdata);
NpyIter_Deallocate(dst_iter);
@@ -2845,6 +2843,7 @@ PyArray_Arange(double start, double stop, double step, int type_num)
PyArray_ArrFuncs *funcs;
PyObject *obj;
int ret;
+ NPY_BEGIN_THREADS_DEF;
if (_safe_ceil_to_intp((stop - start)/step, &length)) {
PyErr_SetString(PyExc_OverflowError,
@@ -2892,7 +2891,9 @@ PyArray_Arange(double start, double stop, double step, int type_num)
Py_DECREF(range);
return NULL;
}
+ NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(range));
funcs->fill(PyArray_DATA(range), length, range);
+ NPY_END_THREADS;
if (PyErr_Occurred()) {
goto fail;
}
@@ -2989,6 +2990,7 @@ PyArray_ArangeObj(PyObject *start, PyObject *stop, PyObject *step, PyArray_Descr
npy_intp length;
PyArray_Descr *native = NULL;
int swap;
+ NPY_BEGIN_THREADS_DEF;
/* Datetime arange is handled specially */
if ((dtype != NULL && (dtype->type_num == NPY_DATETIME ||
@@ -3105,7 +3107,9 @@ PyArray_ArangeObj(PyObject *start, PyObject *stop, PyObject *step, PyArray_Descr
Py_DECREF(range);
goto fail;
}
+ NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(range));
funcs->fill(PyArray_DATA(range), length, range);
+ NPY_END_THREADS;
if (PyErr_Occurred()) {
goto fail;
}
diff --git a/numpy/core/src/multiarray/einsum.c.src b/numpy/core/src/multiarray/einsum.c.src
index ebd381b06..b3148f573 100644
--- a/numpy/core/src/multiarray/einsum.c.src
+++ b/numpy/core/src/multiarray/einsum.c.src
@@ -22,6 +22,7 @@
#include <ctype.h>
#include "convert.h"
+#include "common.h"
#ifdef NPY_HAVE_SSE_INTRINSICS
#define EINSUM_USE_SSE1 1
@@ -2969,7 +2970,6 @@ PyArray_EinsteinSum(char *subscripts, npy_intp nop,
char **dataptr;
npy_intp *stride;
npy_intp *countptr;
- int needs_api = NpyIter_IterationNeedsAPI(iter);
NPY_BEGIN_THREADS_DEF;
iternext = NpyIter_GetIterNext(iter, NULL);
@@ -2982,19 +2982,15 @@ PyArray_EinsteinSum(char *subscripts, npy_intp nop,
stride = NpyIter_GetInnerStrideArray(iter);
countptr = NpyIter_GetInnerLoopSizePtr(iter);
- if (!needs_api) {
- NPY_BEGIN_THREADS;
- }
+ NPY_BEGIN_THREADS_NDITER(iter);
NPY_EINSUM_DBG_PRINT("Einsum loop\n");
do {
sop(nop, dataptr, stride, *countptr);
} while(iternext(iter));
- if (!needs_api) {
- NPY_END_THREADS;
- }
+ NPY_END_THREADS;
/* If the API was needed, it may have thrown an error */
- if (needs_api && PyErr_Occurred()) {
+ if (NpyIter_IterationNeedsAPI(iter) && PyErr_Occurred()) {
Py_DECREF(ret);
ret = NULL;
}
diff --git a/numpy/core/src/multiarray/item_selection.c b/numpy/core/src/multiarray/item_selection.c
index 5294cebd0..71c17c9c9 100644
--- a/numpy/core/src/multiarray/item_selection.c
+++ b/numpy/core/src/multiarray/item_selection.c
@@ -129,12 +129,15 @@ PyArray_TakeFrom(PyArrayObject *self0, PyObject *indices0, int axis,
func = PyArray_DESCR(self)->f->fasttake;
if (func == NULL) {
+ NPY_BEGIN_THREADS_DEF;
+ NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(self));
switch(clipmode) {
case NPY_RAISE:
for (i = 0; i < n; i++) {
for (j = 0; j < m; j++) {
tmp = ((npy_intp *)(PyArray_DATA(indices)))[j];
- if (check_and_adjust_index(&tmp, max_item, axis) < 0) {
+ if (check_and_adjust_index(&tmp, max_item, axis,
+ _save) < 0) {
goto fail;
}
tmp_src = src + tmp * chunk;
@@ -216,8 +219,10 @@ PyArray_TakeFrom(PyArrayObject *self0, PyObject *indices0, int axis,
}
break;
}
+ NPY_END_THREADS;
}
else {
+ /* no gil release, need it for error reporting */
err = func(dest, src, (npy_intp *)(PyArray_DATA(indices)),
max_item, n, m, nelem, clipmode);
if (err) {
@@ -300,7 +305,7 @@ PyArray_PutTo(PyArrayObject *self, PyObject* values0, PyObject *indices0,
for (i = 0; i < ni; i++) {
src = PyArray_BYTES(values) + chunk*(i % nv);
tmp = ((npy_intp *)(PyArray_DATA(indices)))[i];
- if (check_and_adjust_index(&tmp, max_item, 0) < 0) {
+ if (check_and_adjust_index(&tmp, max_item, 0, NULL) < 0) {
goto fail;
}
PyArray_Item_INCREF(src, PyArray_DESCR(self));
@@ -350,7 +355,7 @@ PyArray_PutTo(PyArrayObject *self, PyObject* values0, PyObject *indices0,
for (i = 0; i < ni; i++) {
src = PyArray_BYTES(values) + chunk * (i % nv);
tmp = ((npy_intp *)(PyArray_DATA(indices)))[i];
- if (check_and_adjust_index(&tmp, max_item, 0) < 0) {
+ if (check_and_adjust_index(&tmp, max_item, 0, NULL) < 0) {
goto fail;
}
memmove(dest + tmp * chunk, src, chunk);
@@ -2424,10 +2429,7 @@ PyArray_Nonzero(PyArrayObject *self)
PyObject *ret_tuple;
npy_intp ret_dims[2];
PyArray_NonzeroFunc *nonzero = PyArray_DESCR(self)->f->nonzero;
- char *data;
- npy_intp stride, count;
npy_intp nonzero_count;
- npy_intp *multi_index;
NpyIter *iter;
NpyIter_IterNextFunc *iternext;
@@ -2454,23 +2456,51 @@ PyArray_Nonzero(PyArrayObject *self)
/* If it's a one-dimensional result, don't use an iterator */
if (ndim <= 1) {
- npy_intp j;
+ npy_intp * multi_index = (npy_intp *)PyArray_DATA(ret);
+ char * data = PyArray_BYTES(self);
+ npy_intp stride = (ndim == 0) ? 0 : PyArray_STRIDE(self, 0);
+ npy_intp count = (ndim == 0) ? 1 : PyArray_DIM(self, 0);
+ NPY_BEGIN_THREADS_DEF;
- multi_index = (npy_intp *)PyArray_DATA(ret);
- data = PyArray_BYTES(self);
- stride = (ndim == 0) ? 0 : PyArray_STRIDE(self, 0);
- count = (ndim == 0) ? 1 : PyArray_DIM(self, 0);
+ /* nothing to do */
+ if (nonzero_count == 0) {
+ goto finish;
+ }
+
+ NPY_BEGIN_THREADS_THRESHOLDED(count);
+ /* avoid function call for bool */
if (PyArray_ISBOOL(self)) {
- /* avoid function call for bool */
- for (j = 0; j < count; ++j) {
- if (*data != 0) {
- *multi_index++ = j;
+ /*
+ * use fast memchr variant for sparse data, see gh-4370
+ * the fast bool count is followed by this sparse path is faster
+ * than combining the two loops, even for larger arrays
+ */
+ if (((double)nonzero_count / count) <= 0.1) {
+ npy_intp subsize;
+ npy_intp j = 0;
+ while (1) {
+ npy_memchr(data + j * stride, 0, stride, count - j,
+ &subsize, 1);
+ j += subsize;
+ if (j >= count) {
+ break;
+ }
+ *multi_index++ = j++;
+ }
+ }
+ else {
+ npy_intp j;
+ for (j = 0; j < count; ++j) {
+ if (*data != 0) {
+ *multi_index++ = j;
+ }
+ data += stride;
}
- data += stride;
}
}
else {
+ npy_intp j;
for (j = 0; j < count; ++j) {
if (nonzero(data, self)) {
*multi_index++ = j;
@@ -2479,6 +2509,8 @@ PyArray_Nonzero(PyArrayObject *self)
}
}
+ NPY_END_THREADS;
+
goto finish;
}
@@ -2498,6 +2530,8 @@ PyArray_Nonzero(PyArrayObject *self)
}
if (NpyIter_GetIterSize(iter) != 0) {
+ npy_intp * multi_index;
+ NPY_BEGIN_THREADS_DEF;
/* Get the pointers for inner loop iteration */
iternext = NpyIter_GetIterNext(iter, NULL);
if (iternext == NULL) {
@@ -2511,6 +2545,9 @@ PyArray_Nonzero(PyArrayObject *self)
Py_DECREF(ret);
return NULL;
}
+
+ NPY_BEGIN_THREADS_NDITER(iter);
+
dataptr = NpyIter_GetDataPtrArray(iter);
multi_index = (npy_intp *)PyArray_DATA(ret);
@@ -2533,6 +2570,8 @@ PyArray_Nonzero(PyArrayObject *self)
}
} while(iternext(iter));
}
+
+ NPY_END_THREADS;
}
NpyIter_Deallocate(iter);
@@ -2558,7 +2597,7 @@ finish:
else {
for (i = 0; i < ndim; ++i) {
PyArrayObject *view;
- stride = ndim*NPY_SIZEOF_INTP;
+ npy_intp stride = ndim * NPY_SIZEOF_INTP;
view = (PyArrayObject *)PyArray_New(Py_TYPE(self), 1,
&nonzero_count,
@@ -2601,7 +2640,7 @@ PyArray_MultiIndexGetItem(PyArrayObject *self, npy_intp *multi_index)
npy_intp shapevalue = shape[idim];
npy_intp ind = multi_index[idim];
- if (check_and_adjust_index(&ind, shapevalue, idim) < 0) {
+ if (check_and_adjust_index(&ind, shapevalue, idim, NULL) < 0) {
return NULL;
}
data += ind * strides[idim];
@@ -2630,7 +2669,7 @@ PyArray_MultiIndexSetItem(PyArrayObject *self, npy_intp *multi_index,
npy_intp shapevalue = shape[idim];
npy_intp ind = multi_index[idim];
- if (check_and_adjust_index(&ind, shapevalue, idim) < 0) {
+ if (check_and_adjust_index(&ind, shapevalue, idim, NULL) < 0) {
return -1;
}
data += ind * strides[idim];
diff --git a/numpy/core/src/multiarray/iterators.c b/numpy/core/src/multiarray/iterators.c
index e04ac3bec..81c6b2a8e 100644
--- a/numpy/core/src/multiarray/iterators.c
+++ b/numpy/core/src/multiarray/iterators.c
@@ -71,7 +71,7 @@ parse_index_entry(PyObject *op, npy_intp *step_size,
*n_steps = SINGLE_INDEX;
*step_size = 0;
if (check_index) {
- if (check_and_adjust_index(&i, max, axis) < 0) {
+ if (check_and_adjust_index(&i, max, axis, NULL) < 0) {
goto fail;
}
}
@@ -668,7 +668,7 @@ iter_subscript_int(PyArrayIterObject *self, PyArrayObject *ind)
itemsize = PyArray_DESCR(self->ao)->elsize;
if (PyArray_NDIM(ind) == 0) {
num = *((npy_intp *)PyArray_DATA(ind));
- if (check_and_adjust_index(&num, self->size, -1) < 0) {
+ if (check_and_adjust_index(&num, self->size, -1, NULL) < 0) {
PyArray_ITER_RESET(self);
return NULL;
}
@@ -702,7 +702,7 @@ iter_subscript_int(PyArrayIterObject *self, PyArrayObject *ind)
swap = (PyArray_ISNOTSWAPPED(ret) != PyArray_ISNOTSWAPPED(self->ao));
while (counter--) {
num = *((npy_intp *)(ind_it->dataptr));
- if (check_and_adjust_index(&num, self->size, -1) < 0) {
+ if (check_and_adjust_index(&num, self->size, -1, NULL) < 0) {
Py_DECREF(ind_it);
Py_DECREF(ret);
PyArray_ITER_RESET(self);
@@ -926,7 +926,7 @@ iter_ass_sub_int(PyArrayIterObject *self, PyArrayObject *ind,
copyswap = PyArray_DESCR(self->ao)->f->copyswap;
if (PyArray_NDIM(ind) == 0) {
num = *((npy_intp *)PyArray_DATA(ind));
- if (check_and_adjust_index(&num, self->size, -1) < 0) {
+ if (check_and_adjust_index(&num, self->size, -1, NULL) < 0) {
return -1;
}
PyArray_ITER_GOTO1D(self, num);
@@ -940,7 +940,7 @@ iter_ass_sub_int(PyArrayIterObject *self, PyArrayObject *ind,
counter = ind_it->size;
while (counter--) {
num = *((npy_intp *)(ind_it->dataptr));
- if (check_and_adjust_index(&num, self->size, -1) < 0) {
+ if (check_and_adjust_index(&num, self->size, -1, NULL) < 0) {
Py_DECREF(ind_it);
return -1;
}
@@ -1017,7 +1017,7 @@ iter_ass_subscript(PyArrayIterObject *self, PyObject *ind, PyObject *val)
PyErr_Clear();
}
else {
- if (check_and_adjust_index(&start, self->size, -1) < 0) {
+ if (check_and_adjust_index(&start, self->size, -1, NULL) < 0) {
goto finish;
}
retval = 0;
diff --git a/numpy/core/src/multiarray/lowlevel_strided_loops.c.src b/numpy/core/src/multiarray/lowlevel_strided_loops.c.src
index 042aa2a62..bd69c5ca9 100644
--- a/numpy/core/src/multiarray/lowlevel_strided_loops.c.src
+++ b/numpy/core/src/multiarray/lowlevel_strided_loops.c.src
@@ -1396,7 +1396,7 @@ mapiter_trivial_@name@(PyArrayObject *self, PyArrayObject *ind,
/* Check the indices beforehand */
while (itersize--) {
indval = *((npy_intp*)ind_ptr);
- if (check_and_adjust_index(&indval, fancy_dim, 1) < 0 ) {
+ if (check_and_adjust_index(&indval, fancy_dim, 1, NULL) < 0 ) {
return -1;
}
ind_ptr += ind_stride;
@@ -1426,7 +1426,7 @@ mapiter_trivial_@name@(PyArrayObject *self, PyArrayObject *ind,
while (itersize--) {
indval = *((npy_intp*)ind_ptr);
#if @isget@
- if (check_and_adjust_index(&indval, fancy_dim, 1) < 0 ) {
+ if (check_and_adjust_index(&indval, fancy_dim, 1, NULL) < 0 ) {
return -1;
}
#else
@@ -1560,7 +1560,7 @@ mapiter_@name@(PyArrayMapIterObject *mit)
#if @isget@ && @one_iter@
if (check_and_adjust_index(&indval, fancy_dims[i],
- iteraxis) < 0 ) {
+ iteraxis, NULL) < 0 ) {
return -1;
}
#else
@@ -1662,7 +1662,7 @@ mapiter_@name@(PyArrayMapIterObject *mit)
#if @isget@ && @one_iter@
if (check_and_adjust_index(&indval, fancy_dims[i],
- iteraxis) < 0 ) {
+ iteraxis, NULL) < 0 ) {
NPY_AUXDATA_FREE(transferdata);
return -1;
}
diff --git a/numpy/core/src/multiarray/mapping.c b/numpy/core/src/multiarray/mapping.c
index 6d7e4ffe3..cafec7689 100644
--- a/numpy/core/src/multiarray/mapping.c
+++ b/numpy/core/src/multiarray/mapping.c
@@ -799,7 +799,7 @@ get_item_pointer(PyArrayObject *self, char **ptr,
*ptr = PyArray_BYTES(self);
for (i=0; i < index_num; i++) {
if ((check_and_adjust_index(&(indices[i].value),
- PyArray_DIMS(self)[i], i)) < 0) {
+ PyArray_DIMS(self)[i], i, NULL)) < 0) {
return -1;
}
*ptr += PyArray_STRIDE(self, i) * indices[i].value;
@@ -842,7 +842,8 @@ get_view_from_index(PyArrayObject *self, PyArrayObject **view,
switch (indices[i].type) {
case HAS_INTEGER:
if ((check_and_adjust_index(&indices[i].value,
- PyArray_DIMS(self)[orig_dim], orig_dim)) < 0) {
+ PyArray_DIMS(self)[orig_dim], orig_dim,
+ NULL)) < 0) {
return -1;
}
data_ptr += PyArray_STRIDE(self, orig_dim) * indices[i].value;
@@ -2366,7 +2367,7 @@ PyArray_MapIterCheckIndices(PyArrayMapIterObject *mit)
while (itersize--) {
indval = *((npy_intp*)data);
if (check_and_adjust_index(&indval,
- outer_dim, outer_axis) < 0) {
+ outer_dim, outer_axis, NULL) < 0) {
return -1;
}
data += stride;
@@ -2400,7 +2401,7 @@ PyArray_MapIterCheckIndices(PyArrayMapIterObject *mit)
while (itersize--) {
indval = *((npy_intp*)*iterptr);
if (check_and_adjust_index(&indval,
- outer_dim, outer_axis) < 0) {
+ outer_dim, outer_axis, NULL) < 0) {
Py_DECREF(intp_type);
NpyIter_Deallocate(op_iter);
diff --git a/numpy/core/src/multiarray/methods.c b/numpy/core/src/multiarray/methods.c
index 91f639650..b68745085 100644
--- a/numpy/core/src/multiarray/methods.c
+++ b/numpy/core/src/multiarray/methods.c
@@ -647,7 +647,7 @@ array_toscalar(PyArrayObject *self, PyObject *args)
return NULL;
}
- if (check_and_adjust_index(&value, size, -1) < 0) {
+ if (check_and_adjust_index(&value, size, -1, NULL) < 0) {
return NULL;
}
@@ -724,7 +724,7 @@ array_setscalar(PyArrayObject *self, PyObject *args)
return NULL;
}
- if (check_and_adjust_index(&value, size, -1) < 0) {
+ if (check_and_adjust_index(&value, size, -1, NULL) < 0) {
return NULL;
}
diff --git a/numpy/core/src/multiarray/multiarray_tests.c.src b/numpy/core/src/multiarray/multiarray_tests.c.src
index 6285d0d82..bd0366bd5 100644
--- a/numpy/core/src/multiarray/multiarray_tests.c.src
+++ b/numpy/core/src/multiarray/multiarray_tests.c.src
@@ -720,6 +720,109 @@ array_indexing(PyObject *NPY_UNUSED(self), PyObject *args)
}
+/*
+ * Test nditer of too large arrays using remove axis, etc.
+ */
+static PyObject *
+test_nditer_too_large(PyObject *NPY_UNUSED(self), PyObject *args) {
+ NpyIter *iter;
+ PyObject *array_tuple, *arr;
+ PyArrayObject *arrays[NPY_MAXARGS];
+ npy_uint32 op_flags[NPY_MAXARGS];
+ Py_ssize_t nop;
+ int i, axis, mode;
+
+ npy_intp index[NPY_MAXARGS] = {0};
+ char *msg;
+
+ if (!PyArg_ParseTuple(args, "Oii", &array_tuple, &axis, &mode)) {
+ return NULL;
+ }
+
+ if (!PyTuple_CheckExact(array_tuple)) {
+ PyErr_SetString(PyExc_ValueError, "tuple required as first argument");
+ return NULL;
+ }
+ nop = PyTuple_Size(array_tuple);
+ if (nop > NPY_MAXARGS) {
+ PyErr_SetString(PyExc_ValueError, "tuple must be smaller then maxargs");
+ return NULL;
+ }
+
+ for (i=0; i < nop; i++) {
+ arr = PyTuple_GET_ITEM(array_tuple, i);
+ if (!PyArray_CheckExact(arr)) {
+ PyErr_SetString(PyExc_ValueError, "require base class ndarray");
+ return NULL;
+ }
+ arrays[i] = (PyArrayObject *)arr;
+ op_flags[i] = NPY_ITER_READONLY;
+ }
+
+ iter = NpyIter_MultiNew(nop, arrays, NPY_ITER_MULTI_INDEX | NPY_ITER_RANGED,
+ NPY_KEEPORDER, NPY_NO_CASTING, op_flags, NULL);
+
+ if (iter == NULL) {
+ return NULL;
+ }
+
+ /* Remove an axis (negative, do not remove any) */
+ if (axis >= 0) {
+ if (!NpyIter_RemoveAxis(iter, axis)) {
+ goto fail;
+ }
+ }
+
+ switch (mode) {
+ /* Test IterNext getting */
+ case 0:
+ if (NpyIter_GetIterNext(iter, NULL) == NULL) {
+ goto fail;
+ }
+ break;
+ case 1:
+ if (NpyIter_GetIterNext(iter, &msg) == NULL) {
+ PyErr_SetString(PyExc_ValueError, msg);
+ goto fail;
+ }
+ break;
+ /* Test Multi Index removal */
+ case 2:
+ if (!NpyIter_RemoveMultiIndex(iter)) {
+ goto fail;
+ }
+ break;
+ /* Test GotoMultiIndex (just 0 hardcoded) */
+ case 3:
+ if (!NpyIter_GotoMultiIndex(iter, index)) {
+ goto fail;
+ }
+ break;
+ /* Test setting iterrange (hardcoded range of 0, 1) */
+ case 4:
+ if (!NpyIter_ResetToIterIndexRange(iter, 0, 1, NULL)) {
+ goto fail;
+ }
+ break;
+ case 5:
+ if (!NpyIter_ResetToIterIndexRange(iter, 0, 1, &msg)) {
+ PyErr_SetString(PyExc_ValueError, msg);
+ goto fail;
+ }
+ break;
+ /* Do nothing */
+ default:
+ break;
+ }
+
+ NpyIter_Deallocate(iter);
+ Py_RETURN_NONE;
+ fail:
+ NpyIter_Deallocate(iter);
+ return NULL;
+}
+
+
static PyMethodDef Multiarray_TestsMethods[] = {
{"test_neighborhood_iterator",
test_neighborhood_iterator,
@@ -747,6 +850,9 @@ static PyMethodDef Multiarray_TestsMethods[] = {
{"array_indexing",
array_indexing,
METH_VARARGS, NULL},
+ {"test_nditer_too_large",
+ test_nditer_too_large,
+ METH_VARARGS, NULL},
{NULL, NULL, 0, NULL} /* Sentinel */
};
diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c
index cfaf7799a..2b8fedf9a 100644
--- a/numpy/core/src/multiarray/multiarraymodule.c
+++ b/numpy/core/src/multiarray/multiarraymodule.c
@@ -2774,9 +2774,7 @@ PyArray_Where(PyObject *condition, PyObject *x, PyObject *y)
needs_api = NpyIter_IterationNeedsAPI(iter);
- if (!needs_api) {
- NPY_BEGIN_THREADS_THRESHOLDED(NpyIter_GetIterSize(iter));
- }
+ NPY_BEGIN_THREADS_NDITER(iter);
if (NpyIter_GetIterSize(iter) != 0) {
NpyIter_IterNextFunc *iternext = NpyIter_GetIterNext(iter, NULL);
@@ -2836,9 +2834,7 @@ PyArray_Where(PyObject *condition, PyObject *x, PyObject *y)
} while (iternext(iter));
}
- if (!needs_api) {
- NPY_END_THREADS;
- }
+ NPY_END_THREADS;
/* Get the result from the iterator object array */
ret = (PyObject*)NpyIter_GetOperandArray(iter)[0];
diff --git a/numpy/core/src/multiarray/nditer_api.c b/numpy/core/src/multiarray/nditer_api.c
index d2c956426..ca407b1c1 100644
--- a/numpy/core/src/multiarray/nditer_api.c
+++ b/numpy/core/src/multiarray/nditer_api.c
@@ -14,6 +14,7 @@
/* Indicate that this .c file is allowed to include the header */
#define NPY_ITERATOR_IMPLEMENTATION_CODE
#include "nditer_impl.h"
+#include "scalarmathmodule.h"
/* Internal helper functions private to this file */
static npy_intp
@@ -127,13 +128,23 @@ NpyIter_RemoveAxis(NpyIter *iter, int axis)
perm[idim] = p;
}
- /* Adjust the iteration size */
- NIT_ITERSIZE(iter) /= NAD_SHAPE(axisdata_del);
-
/* Shift all the axisdata structures by one */
axisdata = NIT_INDEX_AXISDATA(axisdata_del, 1);
memmove(axisdata_del, axisdata, (ndim-1-xdim)*sizeof_axisdata);
+ /* Adjust the iteration size and reset iterend */
+ NIT_ITERSIZE(iter) = 1;
+ axisdata = NIT_AXISDATA(iter);
+ for (idim = 0; idim < ndim-1; ++idim) {
+ if (npy_mul_with_overflow_intp(&NIT_ITERSIZE(iter),
+ NIT_ITERSIZE(iter), NAD_SHAPE(axisdata))) {
+ NIT_ITERSIZE(iter) = -1;
+ break;
+ }
+ NIT_ADVANCE_AXISDATA(axisdata, 1);
+ }
+ NIT_ITEREND(iter) = NIT_ITERSIZE(iter);
+
/* Shrink the iterator */
NIT_NDIM(iter) = ndim - 1;
/* If it is now 0-d fill the singleton dimension */
@@ -166,6 +177,11 @@ NpyIter_RemoveMultiIndex(NpyIter *iter)
itflags = NIT_ITFLAGS(iter);
if (itflags&NPY_ITFLAG_HASMULTIINDEX) {
+ if (NIT_ITERSIZE(iter) < 0) {
+ PyErr_SetString(PyExc_ValueError, "iterator is too large");
+ return NPY_FAIL;
+ }
+
NIT_ITFLAGS(iter) = itflags & ~NPY_ITFLAG_HASMULTIINDEX;
npyiter_coalesce_axes(iter);
}
@@ -349,6 +365,15 @@ NpyIter_ResetToIterIndexRange(NpyIter *iter,
}
if (istart < 0 || iend > NIT_ITERSIZE(iter)) {
+ if (NIT_ITERSIZE(iter) < 0) {
+ if (errmsg == NULL) {
+ PyErr_SetString(PyExc_ValueError, "iterator is too large");
+ }
+ else {
+ *errmsg = "iterator is too large";
+ }
+ return NPY_FAIL;
+ }
if (errmsg == NULL) {
PyErr_Format(PyExc_ValueError,
"Out-of-bounds range [%d, %d) passed to "
@@ -454,6 +479,10 @@ NpyIter_GotoMultiIndex(NpyIter *iter, npy_intp *multi_index)
}
if (iterindex < NIT_ITERSTART(iter) || iterindex >= NIT_ITEREND(iter)) {
+ if (NIT_ITERSIZE(iter) < 0) {
+ PyErr_SetString(PyExc_ValueError, "iterator is too large");
+ return NPY_FAIL;
+ }
PyErr_SetString(PyExc_IndexError,
"Iterator GotoMultiIndex called with a multi-index outside the "
"restricted iteration range");
@@ -574,6 +603,10 @@ NpyIter_GotoIterIndex(NpyIter *iter, npy_intp iterindex)
}
if (iterindex < NIT_ITERSTART(iter) || iterindex >= NIT_ITEREND(iter)) {
+ if (NIT_ITERSIZE(iter) < 0) {
+ PyErr_SetString(PyExc_ValueError, "iterator is too large");
+ return NPY_FAIL;
+ }
PyErr_SetString(PyExc_IndexError,
"Iterator GotoIterIndex called with an iterindex outside the "
"iteration range.");
diff --git a/numpy/core/src/multiarray/nditer_constr.c b/numpy/core/src/multiarray/nditer_constr.c
index 47523863e..41f0b97c4 100644
--- a/numpy/core/src/multiarray/nditer_constr.c
+++ b/numpy/core/src/multiarray/nditer_constr.c
@@ -1656,8 +1656,21 @@ npyiter_fill_axisdata(NpyIter *iter, npy_uint32 flags, npyiter_opitflags *op_itf
for (idim = 0; idim < ndim; ++idim) {
if (npy_mul_with_overflow_intp(&NIT_ITERSIZE(iter),
NIT_ITERSIZE(iter), broadcast_shape[idim])) {
- PyErr_SetString(PyExc_ValueError, "iterator is too large");
- return 0;
+ if ((itflags & NPY_ITFLAG_HASMULTIINDEX) &&
+ !(itflags & NPY_ITFLAG_HASINDEX) &&
+ !(itflags & NPY_ITFLAG_BUFFER)) {
+ /*
+ * If RemoveAxis may be called, the size check is delayed
+ * until either the multi index is removed, or GetIterNext
+ * is called.
+ */
+ NIT_ITERSIZE(iter) = -1;
+ break;
+ }
+ else {
+ PyErr_SetString(PyExc_ValueError, "iterator is too large");
+ return 0;
+ }
}
}
/* The range defaults to everything */
diff --git a/numpy/core/src/multiarray/nditer_pywrap.c b/numpy/core/src/multiarray/nditer_pywrap.c
index 339a713cd..1ca5de8d6 100644
--- a/numpy/core/src/multiarray/nditer_pywrap.c
+++ b/numpy/core/src/multiarray/nditer_pywrap.c
@@ -37,12 +37,16 @@ struct NewNpyArrayIterObject_tag {
char writeflags[NPY_MAXARGS];
};
-static void npyiter_cache_values(NewNpyArrayIterObject *self)
+static int npyiter_cache_values(NewNpyArrayIterObject *self)
{
NpyIter *iter = self->iter;
/* iternext and get_multi_index functions */
self->iternext = NpyIter_GetIterNext(iter, NULL);
+ if (self->iternext == NULL) {
+ return -1;
+ }
+
if (NpyIter_HasMultiIndex(iter) && !NpyIter_HasDelayedBufAlloc(iter)) {
self->get_multi_index = NpyIter_GetGetMultiIndex(iter, NULL);
}
@@ -67,6 +71,7 @@ static void npyiter_cache_values(NewNpyArrayIterObject *self)
/* The read/write settings */
NpyIter_GetReadFlags(iter, self->readflags);
NpyIter_GetWriteFlags(iter, self->writeflags);
+ return 0;
}
static PyObject *
@@ -803,7 +808,9 @@ npyiter_init(NewNpyArrayIterObject *self, PyObject *args, PyObject *kwds)
}
/* Cache some values for the member functions to use */
- npyiter_cache_values(self);
+ if (npyiter_cache_values(self) < 0) {
+ goto fail;
+ }
if (NpyIter_GetIterSize(self->iter) == 0) {
self->started = 1;
@@ -1068,7 +1075,10 @@ NpyIter_NestedIters(PyObject *NPY_UNUSED(self),
}
/* Cache some values for the member functions to use */
- npyiter_cache_values(iter);
+ if (npyiter_cache_values(iter) < 0) {
+ Py_DECREF(ret);
+ goto fail;
+ }
if (NpyIter_GetIterSize(iter->iter) == 0) {
iter->started = 1;
@@ -1242,7 +1252,10 @@ npyiter_copy(NewNpyArrayIterObject *self)
}
/* Cache some values for the member functions to use */
- npyiter_cache_values(iter);
+ if (npyiter_cache_values(iter) < 0) {
+ Py_DECREF(iter);
+ return NULL;
+ }
iter->started = self->started;
iter->finished = self->finished;
@@ -1287,7 +1300,9 @@ npyiter_remove_axis(NewNpyArrayIterObject *self, PyObject *args)
return NULL;
}
/* RemoveAxis invalidates cached values */
- npyiter_cache_values(self);
+ if (npyiter_cache_values(self) < 0) {
+ return NULL;
+ }
/* RemoveAxis also resets the iterator */
if (NpyIter_GetIterSize(self->iter) == 0) {
self->started = 1;
diff --git a/numpy/core/src/multiarray/nditer_templ.c.src b/numpy/core/src/multiarray/nditer_templ.c.src
index 59aae244b..8976b132e 100644
--- a/numpy/core/src/multiarray/nditer_templ.c.src
+++ b/numpy/core/src/multiarray/nditer_templ.c.src
@@ -347,6 +347,16 @@ NpyIter_GetIterNext(NpyIter *iter, char **errmsg)
int ndim = NIT_NDIM(iter);
int nop = NIT_NOP(iter);
+ if (NIT_ITERSIZE(iter) < 0) {
+ if (errmsg == NULL) {
+ PyErr_SetString(PyExc_ValueError, "iterator is too large");
+ }
+ else {
+ *errmsg = "iterator is too large";
+ }
+ return NULL;
+ }
+
/*
* When there is just one iteration and buffering is disabled
* the iternext function is very simple.
diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c
index dcf5c16b5..233855460 100644
--- a/numpy/core/src/umath/ufunc_object.c
+++ b/numpy/core/src/umath/ufunc_object.c
@@ -1153,9 +1153,7 @@ trivial_two_operand_loop(PyArrayObject **op,
innerloop(data, count, stride, innerloopdata);
- if (!needs_api) {
- NPY_END_THREADS;
- }
+ NPY_END_THREADS;
}
static void
@@ -1186,9 +1184,7 @@ trivial_three_operand_loop(PyArrayObject **op,
innerloop(data, count, stride, innerloopdata);
- if (!needs_api) {
- NPY_END_THREADS;
- }
+ NPY_END_THREADS;
}
/*
@@ -1270,7 +1266,6 @@ iterator_loop(PyUFuncObject *ufunc,
npy_uint32 op_flags[NPY_MAXARGS];
NpyIter *iter;
char *baseptrs[NPY_MAXARGS];
- int needs_api;
NpyIter_IterNextFunc *iternext;
char **dataptr;
@@ -1325,8 +1320,6 @@ iterator_loop(PyUFuncObject *ufunc,
return -1;
}
- needs_api = NpyIter_IterationNeedsAPI(iter);
-
/* Copy any allocated outputs */
op_it = NpyIter_GetOperandArray(iter);
for (i = nin; i < nop; ++i) {
@@ -1370,9 +1363,7 @@ iterator_loop(PyUFuncObject *ufunc,
stride = NpyIter_GetInnerStrideArray(iter);
count_ptr = NpyIter_GetInnerLoopSizePtr(iter);
- if (!needs_api) {
- NPY_BEGIN_THREADS_THRESHOLDED(NpyIter_GetIterSize(iter));
- }
+ NPY_BEGIN_THREADS_NDITER(iter);
/* Execute the loop */
do {
@@ -1380,9 +1371,7 @@ iterator_loop(PyUFuncObject *ufunc,
innerloop(dataptr, count_ptr, stride, innerloopdata);
} while (iternext(iter));
- if (!needs_api) {
- NPY_END_THREADS;
- }
+ NPY_END_THREADS;
}
NpyIter_Deallocate(iter);
@@ -1579,8 +1568,6 @@ execute_fancy_ufunc_loop(PyUFuncObject *ufunc,
PyArrayObject **op_it;
npy_uint32 iter_flags;
- NPY_BEGIN_THREADS_DEF;
-
if (wheremask != NULL) {
if (nop + 1 > NPY_MAXARGS) {
PyErr_SetString(PyExc_ValueError,
@@ -1669,6 +1656,7 @@ execute_fancy_ufunc_loop(PyUFuncObject *ufunc,
NpyAuxData *innerloopdata;
npy_intp fixed_strides[2*NPY_MAXARGS];
PyArray_Descr **iter_dtypes;
+ NPY_BEGIN_THREADS_DEF;
/* Validate that the prepare_ufunc_output didn't mess with pointers */
for (i = nin; i < nop; ++i) {
@@ -1708,9 +1696,7 @@ execute_fancy_ufunc_loop(PyUFuncObject *ufunc,
strides = NpyIter_GetInnerStrideArray(iter);
countptr = NpyIter_GetInnerLoopSizePtr(iter);
- if (!needs_api) {
- NPY_BEGIN_THREADS;
- }
+ NPY_BEGIN_THREADS_NDITER(iter);
NPY_UF_DBG_PRINT("Actual inner loop:\n");
/* Execute the loop */
@@ -1721,9 +1707,7 @@ execute_fancy_ufunc_loop(PyUFuncObject *ufunc,
*countptr, innerloopdata);
} while (iternext(iter));
- if (!needs_api) {
- NPY_END_THREADS;
- }
+ NPY_END_THREADS;
NPY_AUXDATA_FREE(innerloopdata);
}
@@ -2167,6 +2151,11 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
*/
inner_strides = (npy_intp *)PyArray_malloc(
NPY_SIZEOF_INTP * (nop+core_dim_ixs_size));
+ if (inner_strides == NULL) {
+ PyErr_NoMemory();
+ retval = -1;
+ goto fail;
+ }
/* Copy the strides after the first nop */
idim = nop;
for (i = 0; i < nop; ++i) {
@@ -2273,11 +2262,13 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
PyErr_Format(PyExc_ValueError,
"ufunc %s ",
ufunc_name);
+ retval = -1;
goto fail;
default:
PyErr_Format(PyExc_ValueError,
"ufunc %s has an invalid identity for reduction",
ufunc_name);
+ retval = -1;
goto fail;
}
}
@@ -2742,9 +2733,7 @@ reduce_loop(NpyIter *iter, char **dataptrs, npy_intp *strides,
return -1;
}
- if (!needs_api) {
- NPY_BEGIN_THREADS_THRESHOLDED(NpyIter_GetIterSize(iter));
- }
+ NPY_BEGIN_THREADS_NDITER(iter);
if (skip_first_count > 0) {
do {
@@ -2797,9 +2786,7 @@ reduce_loop(NpyIter *iter, char **dataptrs, npy_intp *strides,
} while (iternext(iter));
finish_loop:
- if (!needs_api) {
- NPY_END_THREADS;
- }
+ NPY_END_THREADS;
return (needs_api && PyErr_Occurred()) ? -1 : 0;
}
@@ -3126,9 +3113,7 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out,
needs_api = NpyIter_IterationNeedsAPI(iter);
- if (!needs_api) {
- NPY_BEGIN_THREADS_THRESHOLDED(NpyIter_GetIterSize(iter));
- }
+ NPY_BEGIN_THREADS_NDITER(iter);
do {
@@ -3158,9 +3143,7 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out,
}
} while (iternext(iter));
- if (!needs_api) {
- NPY_END_THREADS;
- }
+ NPY_END_THREADS;
}
else if (iter == NULL) {
char *dataptr_copy[3];
@@ -3221,9 +3204,7 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out,
innerloop(dataptr_copy, &count,
stride_copy, innerloopdata);
- if (!needs_api) {
- NPY_END_THREADS;
- }
+ NPY_END_THREADS;
}
}
@@ -3274,7 +3255,7 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind,
op_axes_arrays[2]};
npy_uint32 op_flags[3];
int i, idim, ndim, otype_final;
- int needs_api, need_outer_iterator;
+ int need_outer_iterator;
NpyIter *iter = NULL;
@@ -3519,11 +3500,7 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind,
stride_copy[1] = stride1;
stride_copy[2] = stride0;
- needs_api = NpyIter_IterationNeedsAPI(iter);
-
- if (!needs_api) {
- NPY_BEGIN_THREADS;
- }
+ NPY_BEGIN_THREADS_NDITER(iter);
do {
@@ -3560,9 +3537,7 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind,
}
} while (iternext(iter));
- if (!needs_api) {
- NPY_END_THREADS;
- }
+ NPY_END_THREADS;
}
else if (iter == NULL) {
char *dataptr_copy[3];
@@ -3575,7 +3550,7 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind,
/* Execute the loop with no iterators */
npy_intp stride0 = 0, stride1 = PyArray_STRIDE(op[1], axis);
- needs_api = PyDataType_REFCHK(op_dtypes[0]);
+ int needs_api = PyDataType_REFCHK(op_dtypes[0]);
NPY_UF_DBG_PRINT("UFunc: Reduce loop with no iterators\n");
@@ -3619,9 +3594,7 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind,
}
}
- if (!needs_api) {
- NPY_END_THREADS;
- }
+ NPY_END_THREADS;
}
finish:
@@ -5190,9 +5163,7 @@ ufunc_at(PyUFuncObject *ufunc, PyObject *args)
i--;
}
- if (!needs_api) {
- NPY_END_THREADS;
- }
+ NPY_END_THREADS;
if (err_msg) {
PyErr_SetString(PyExc_ValueError, err_msg);
diff --git a/numpy/core/tests/test_nditer.py b/numpy/core/tests/test_nditer.py
index 3eac5428f..0055c038b 100644
--- a/numpy/core/tests/test_nditer.py
+++ b/numpy/core/tests/test_nditer.py
@@ -6,7 +6,7 @@ import numpy as np
from numpy import array, arange, nditer, all
from numpy.compat import asbytes, sixu
from numpy.testing import *
-
+from numpy.core.multiarray_tests import test_nditer_too_large
def iter_multi_index(i):
@@ -2586,6 +2586,44 @@ def test_iter_too_large():
size = np.iinfo(np.intp).max // 1024
arr = np.lib.stride_tricks.as_strided(np.zeros(1), (size,), (0,))
assert_raises(ValueError, nditer, (arr, arr[:, None]))
+ # test the same for multiindex. That may get more interesting when
+ # removing 0 dimensional axis is allowed (since an iterator can grow then)
+ assert_raises(ValueError, nditer,
+ (arr, arr[:, None]), flags=['multi_index'])
+
+
+def test_iter_too_large_with_multiindex():
+ # When a multi index is being tracked, the error is delayed this
+ # checks the delayed error messages and getting below that by
+ # removing an axis.
+ base_size = 2**10
+ num = 1
+ while base_size**num < np.iinfo(np.intp).max:
+ num += 1
+
+ shape_template = [1, 1] * num
+ arrays = []
+ for i in range(num):
+ shape = shape_template[:]
+ shape[i * 2] = 2**10
+ arrays.append(np.empty(shape))
+ arrays = tuple(arrays)
+
+ # arrays are now too large to be broadcast. The different modes test
+ # different nditer functionality with or without GIL.
+ for mode in range(6):
+ assert_raises(ValueError, test_nditer_too_large, arrays, -1, mode)
+ # but if we do nothing with the nditer, it can be constructed:
+ test_nditer_too_large(arrays, -1, 7)
+
+ # When an axis is removed, things should work again (half the time):
+ for i in range(num):
+ for mode in range(6):
+ # an axis with size 1024 is removed:
+ test_nditer_too_large(arrays, i*2, mode)
+ # an axis with size 1 is removed:
+ assert_raises(ValueError, test_nditer_too_large,
+ arrays, i*2 + 1, mode)
if __name__ == "__main__":
diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py
index 8f2d36c55..d08fd983e 100644
--- a/numpy/core/tests/test_numeric.py
+++ b/numpy/core/tests/test_numeric.py
@@ -936,6 +936,20 @@ class TestNonzero(TestCase):
assert_equal(np.nonzero(x['a'].T), ([0, 1, 1, 2], [1, 1, 2, 0]))
assert_equal(np.nonzero(x['b'].T), ([0, 0, 1, 2, 2], [0, 1, 2, 0, 2]))
+ def test_sparse(self):
+ # test special sparse condition boolean code path
+ for i in range(20):
+ c = np.zeros(200, dtype=np.bool)
+ c[i::20] = True
+ assert_equal(np.nonzero(c)[0], np.arange(i, 200 + i, 20))
+
+ c = np.zeros(400, dtype=np.bool)
+ c[10 + i:20 + i] = True
+ c[20 + i*2] = True
+ assert_equal(np.nonzero(c)[0],
+ np.concatenate((np.arange(10 +i, 20 + i), [20 +i*2])))
+
+
class TestIndex(TestCase):
def test_boolean(self):
a = rand(3, 5, 8)
diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py
index 62ced0509..7b2d077c3 100644
--- a/numpy/lib/function_base.py
+++ b/numpy/lib/function_base.py
@@ -1593,6 +1593,7 @@ class vectorize(object):
cache=False):
self.pyfunc = pyfunc
self.cache = cache
+ self._ufunc = None # Caching to improve default performance
if doc is None:
self.__doc__ = pyfunc.__doc__
@@ -1616,9 +1617,6 @@ class vectorize(object):
excluded = set()
self.excluded = set(excluded)
- if self.otypes and not self.excluded:
- self._ufunc = None # Caching to improve default performance
-
def __call__(self, *args, **kwargs):
"""
Return arrays with the results of `pyfunc` broadcast (vectorized) over
@@ -1652,7 +1650,8 @@ class vectorize(object):
def _get_ufunc_and_otypes(self, func, args):
"""Return (ufunc, otypes)."""
# frompyfunc will fail if args is empty
- assert args
+ if not args:
+ raise ValueError('args can not be empty')
if self.otypes:
otypes = self.otypes
diff --git a/numpy/lib/src/_compiled_base.c b/numpy/lib/src/_compiled_base.c
index 652268f24..f70cd2bab 100644
--- a/numpy/lib/src/_compiled_base.c
+++ b/numpy/lib/src/_compiled_base.c
@@ -1417,6 +1417,9 @@ _packbits( void *In,
npy_intp out_Nm1;
int maxi, remain, nonzero, j;
char *outptr,*inptr;
+ NPY_BEGIN_THREADS_DEF;
+
+ NPY_BEGIN_THREADS_THRESHOLDED(out_N);
outptr = Out; /* pointer to output buffer */
inptr = In; /* pointer to input buffer */
@@ -1451,6 +1454,8 @@ _packbits( void *In,
*outptr = build;
outptr += out_stride;
}
+
+ NPY_END_THREADS;
return;
}
@@ -1468,6 +1473,9 @@ _unpackbits(void *In,
unsigned char mask;
int i, index;
char *inptr, *outptr;
+ NPY_BEGIN_THREADS_DEF;
+
+ NPY_BEGIN_THREADS_THRESHOLDED(in_N);
outptr = Out;
inptr = In;
@@ -1480,6 +1488,8 @@ _unpackbits(void *In,
}
inptr += in_stride;
}
+
+ NPY_END_THREADS;
return;
}
diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py
index 3e102cf6a..6c11b0385 100644
--- a/numpy/lib/tests/test_function_base.py
+++ b/numpy/lib/tests/test_function_base.py
@@ -721,6 +721,12 @@ class TestVectorize(TestCase):
assert_array_equal(f(x), x*x)
assert_equal(_calls[0], len(x))
+ def test_otypes(self):
+ f = np.vectorize(lambda x: x)
+ f.otypes = 'i'
+ x = np.arange(5)
+ assert_array_equal(f(x), x)
+
class TestDigitize(TestCase):
def test_forward(self):
diff --git a/numpy/linalg/tests/test_regression.py b/numpy/linalg/tests/test_regression.py
index 4ff14a6a5..b0b2b2e30 100644
--- a/numpy/linalg/tests/test_regression.py
+++ b/numpy/linalg/tests/test_regression.py
@@ -69,5 +69,11 @@ class TestRegression(TestCase):
bp = linalg.cholesky(b)
assert_array_equal(ap, bp)
+ def test_large_svd_32bit(self):
+ # See gh-4442, 64bit would require very large/slow matrices.
+ x = np.eye(1000, 66)
+ np.linalg.svd(x)
+
+
if __name__ == '__main__':
run_module_suite()
diff --git a/numpy/testing/utils.py b/numpy/testing/utils.py
index 4905898d2..01c0e769b 100644
--- a/numpy/testing/utils.py
+++ b/numpy/testing/utils.py
@@ -318,7 +318,9 @@ def assert_equal(actual,desired,err_msg='',verbose=True):
# as before
except (TypeError, ValueError, NotImplementedError):
pass
- if desired != actual :
+
+ # Explicitly use __eq__ for comparison, ticket #2552
+ if not (desired == actual):
raise AssertionError(msg)
def print_assert_equal(test_string, actual, desired):