diff options
-rw-r--r-- | doc/release/upcoming_changes/14942.compatibility.rst | 5 | ||||
-rw-r--r-- | numpy/core/src/multiarray/arraytypes.c.src | 115 | ||||
-rw-r--r-- | numpy/core/src/multiarray/item_selection.c | 275 | ||||
-rw-r--r-- | numpy/core/tests/test_multiarray.py | 14 |
4 files changed, 183 insertions, 226 deletions
diff --git a/doc/release/upcoming_changes/14942.compatibility.rst b/doc/release/upcoming_changes/14942.compatibility.rst new file mode 100644 index 000000000..41a2457e2 --- /dev/null +++ b/doc/release/upcoming_changes/14942.compatibility.rst @@ -0,0 +1,5 @@ +Fasttake slot is deprecated and now NULL'ed +------------------------------------------- +The fasttake slot for all dtypes is now never used internally +and always set to NULL. No downstream project should be using +this, so no compatibility issue is expected. diff --git a/numpy/core/src/multiarray/arraytypes.c.src b/numpy/core/src/multiarray/arraytypes.c.src index 077fb0ec8..0f52df486 100644 --- a/numpy/core/src/multiarray/arraytypes.c.src +++ b/numpy/core/src/multiarray/arraytypes.c.src @@ -3973,119 +3973,6 @@ static void #define OBJECT_fastputmask NULL -/* - ***************************************************************************** - ** FASTTAKE ** - ***************************************************************************** - */ - - -/**begin repeat - * - * #name = BOOL, - * BYTE, UBYTE, SHORT, USHORT, INT, UINT, - * LONG, ULONG, LONGLONG, ULONGLONG, - * HALF, FLOAT, DOUBLE, LONGDOUBLE, - * CFLOAT, CDOUBLE, CLONGDOUBLE, - * DATETIME, TIMEDELTA# - * #type = npy_bool, - * npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint, - * npy_long, npy_ulong, npy_longlong, npy_ulonglong, - * npy_half, npy_float, npy_double, npy_longdouble, - * npy_cfloat, npy_cdouble, npy_clongdouble, - * npy_datetime, npy_timedelta# -*/ -static int -@name@_fasttake(@type@ *dest, @type@ *src, npy_intp *indarray, - npy_intp nindarray, npy_intp n_outer, - npy_intp m_middle, npy_intp nelem, - 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, _save) < 0) { - return 1; - } - if (NPY_LIKELY(nelem == 1)) { - *dest++ = *(src + tmp); - } - else { - for (k = 0; k < nelem; k++) { - *dest++ = *(src + tmp*nelem + k); - } - } - } - src += nelem*nindarray; - } - break; - case NPY_WRAP: - for (i = 0; i < n_outer; i++) { - for (j = 0; j < m_middle; j++) { - tmp = indarray[j]; - if (tmp < 0) { - while (tmp < 0) { - tmp += nindarray; - } - } - else if (tmp >= nindarray) { - while (tmp >= nindarray) { - tmp -= nindarray; - } - } - if (NPY_LIKELY(nelem == 1)) { - *dest++ = *(src+tmp); - } - else { - for (k = 0; k < nelem; k++) { - *dest++ = *(src+tmp*nelem+k); - } - } - } - src += nelem*nindarray; - } - break; - case NPY_CLIP: - for (i = 0; i < n_outer; i++) { - for (j = 0; j < m_middle; j++) { - tmp = indarray[j]; - if (tmp < 0) { - tmp = 0; - } - else if (tmp >= nindarray) { - tmp = nindarray - 1; - } - if (NPY_LIKELY(nelem == 1)) { - *dest++ = *(src + tmp); - } - else { - for (k = 0; k < nelem; k++) { - *dest++ = *(src + tmp*nelem + k); - } - } - } - src += nelem*nindarray; - } - break; - } - - NPY_END_THREADS; - return 0; -} -/**end repeat**/ - -#define OBJECT_fasttake NULL /* ***************************************************************************** @@ -4446,7 +4333,7 @@ static PyArray_ArrFuncs _Py@NAME@_ArrFuncs = { NULL, (PyArray_FastClipFunc*)NULL, (PyArray_FastPutmaskFunc*)@from@_fastputmask, - (PyArray_FastTakeFunc*)@from@_fasttake, + (PyArray_FastTakeFunc*)NULL, (PyArray_ArgFunc*)@from@_argmin }; diff --git a/numpy/core/src/multiarray/item_selection.c b/numpy/core/src/multiarray/item_selection.c index e2c3ebd00..000ff6ef9 100644 --- a/numpy/core/src/multiarray/item_selection.c +++ b/numpy/core/src/multiarray/item_selection.c @@ -21,11 +21,168 @@ #include "lowlevel_strided_loops.h" #include "array_assign.h" -#include "item_selection.h" #include "npy_sort.h" #include "npy_partition.h" #include "npy_binsearch.h" #include "alloc.h" +#include "arraytypes.h" + + + +static NPY_GCC_OPT_3 NPY_INLINE int +npy_fasttake_impl( + char *dest, char *src, const npy_intp *indices, + npy_intp n, npy_intp m, npy_intp max_item, + npy_intp nelem, npy_intp chunk, + NPY_CLIPMODE clipmode, npy_intp itemsize, int needs_refcounting, + PyArray_Descr *dtype, int axis) +{ + NPY_BEGIN_THREADS_DEF; + NPY_BEGIN_THREADS_DESCR(dtype); + switch (clipmode) { + case NPY_RAISE: + for (npy_intp i = 0; i < n; i++) { + for (npy_intp j = 0; j < m; j++) { + npy_intp tmp = indices[j]; + if (check_and_adjust_index(&tmp, max_item, axis, + _save) < 0) { + return -1; + } + char *tmp_src = src + tmp * chunk; + if (needs_refcounting) { + for (npy_intp k = 0; k < nelem; k++) { + PyArray_Item_INCREF(tmp_src, dtype); + PyArray_Item_XDECREF(dest, dtype); + memmove(dest, tmp_src, itemsize); + dest += itemsize; + tmp_src += itemsize; + } + } + else { + memmove(dest, tmp_src, chunk); + dest += chunk; + } + } + src += chunk*max_item; + } + break; + case NPY_WRAP: + for (npy_intp i = 0; i < n; i++) { + for (npy_intp j = 0; j < m; j++) { + npy_intp tmp = indices[j]; + if (tmp < 0) { + while (tmp < 0) { + tmp += max_item; + } + } + else if (tmp >= max_item) { + while (tmp >= max_item) { + tmp -= max_item; + } + } + char *tmp_src = src + tmp * chunk; + if (needs_refcounting) { + for (npy_intp k = 0; k < nelem; k++) { + PyArray_Item_INCREF(tmp_src, dtype); + PyArray_Item_XDECREF(dest, dtype); + memmove(dest, tmp_src, itemsize); + dest += itemsize; + tmp_src += itemsize; + } + } + else { + memmove(dest, tmp_src, chunk); + dest += chunk; + } + } + src += chunk*max_item; + } + break; + case NPY_CLIP: + for (npy_intp i = 0; i < n; i++) { + for (npy_intp j = 0; j < m; j++) { + npy_intp tmp = indices[j]; + if (tmp < 0) { + tmp = 0; + } + else if (tmp >= max_item) { + tmp = max_item - 1; + } + char *tmp_src = src + tmp * chunk; + if (needs_refcounting) { + for (npy_intp k = 0; k < nelem; k++) { + PyArray_Item_INCREF(tmp_src, dtype); + PyArray_Item_XDECREF(dest, dtype); + memmove(dest, tmp_src, itemsize); + dest += itemsize; + tmp_src += itemsize; + } + } + else { + memmove(dest, tmp_src, chunk); + dest += chunk; + } + } + src += chunk*max_item; + } + break; + } + + NPY_END_THREADS; + return 0; +} + + +/* + * Helper function instantiating npy_fasttake_impl in different branches + * to allow the compiler to optimize each to the specific itemsize. + */ +static NPY_GCC_OPT_3 int +npy_fasttake( + char *dest, char *src, const npy_intp *indices, + npy_intp n, npy_intp m, npy_intp max_item, + npy_intp nelem, npy_intp chunk, + NPY_CLIPMODE clipmode, npy_intp itemsize, int needs_refcounting, + PyArray_Descr *dtype, int axis) +{ + if (!needs_refcounting) { + if (chunk == 1) { + return npy_fasttake_impl( + dest, src, indices, n, m, max_item, nelem, chunk, + clipmode, itemsize, needs_refcounting, dtype, axis); + } + if (chunk == 2) { + return npy_fasttake_impl( + dest, src, indices, n, m, max_item, nelem, chunk, + clipmode, itemsize, needs_refcounting, dtype, axis); + } + if (chunk == 4) { + return npy_fasttake_impl( + dest, src, indices, n, m, max_item, nelem, chunk, + clipmode, itemsize, needs_refcounting, dtype, axis); + } + if (chunk == 8) { + return npy_fasttake_impl( + dest, src, indices, n, m, max_item, nelem, chunk, + clipmode, itemsize, needs_refcounting, dtype, axis); + } + if (chunk == 16) { + return npy_fasttake_impl( + dest, src, indices, n, m, max_item, nelem, chunk, + clipmode, itemsize, needs_refcounting, dtype, axis); + } + if (chunk == 32) { + return npy_fasttake_impl( + dest, src, indices, n, m, max_item, nelem, chunk, + clipmode, itemsize, needs_refcounting, dtype, axis); + } + } + + return npy_fasttake_impl( + dest, src, indices, n, m, max_item, nelem, chunk, + clipmode, itemsize, needs_refcounting, dtype, axis); +} + /*NUMPY_API * Take @@ -35,12 +192,10 @@ PyArray_TakeFrom(PyArrayObject *self0, PyObject *indices0, int axis, PyArrayObject *out, NPY_CLIPMODE clipmode) { PyArray_Descr *dtype; - PyArray_FastTakeFunc *func; PyArrayObject *obj = NULL, *self, *indices; - npy_intp nd, i, j, n, m, k, max_item, tmp, chunk, itemsize, nelem; + npy_intp nd, i, n, m, max_item, chunk, itemsize, nelem; npy_intp shape[NPY_MAXDIMS]; - char *src, *dest, *tmp_src; - int err; + npy_bool needs_refcounting; indices = NULL; @@ -122,9 +277,10 @@ PyArray_TakeFrom(PyArrayObject *self0, PyObject *indices0, int axis, nelem = chunk; itemsize = PyArray_ITEMSIZE(obj); chunk = chunk * itemsize; - src = PyArray_DATA(self); - dest = PyArray_DATA(obj); + char *src = PyArray_DATA(self); + char *dest = PyArray_DATA(obj); needs_refcounting = PyDataType_REFCHK(PyArray_DESCR(self)); + npy_intp *indices_data = (npy_intp *)PyArray_DATA(indices); if ((max_item == 0) && (PyArray_SIZE(obj) != 0)) { /* Index error, since that is the usual error for raise mode */ @@ -133,107 +289,10 @@ PyArray_TakeFrom(PyArrayObject *self0, PyObject *indices0, int axis, goto fail; } - 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, - _save) < 0) { - goto fail; - } - tmp_src = src + tmp * chunk; - if (needs_refcounting) { - for (k=0; k < nelem; k++) { - PyArray_Item_INCREF(tmp_src, PyArray_DESCR(self)); - PyArray_Item_XDECREF(dest, PyArray_DESCR(self)); - memmove(dest, tmp_src, itemsize); - dest += itemsize; - tmp_src += itemsize; - } - } - else { - memmove(dest, tmp_src, chunk); - dest += chunk; - } - } - src += chunk*max_item; - } - break; - case NPY_WRAP: - for (i = 0; i < n; i++) { - for (j = 0; j < m; j++) { - tmp = ((npy_intp *)(PyArray_DATA(indices)))[j]; - if (tmp < 0) { - while (tmp < 0) { - tmp += max_item; - } - } - else if (tmp >= max_item) { - while (tmp >= max_item) { - tmp -= max_item; - } - } - tmp_src = src + tmp * chunk; - if (needs_refcounting) { - for (k=0; k < nelem; k++) { - PyArray_Item_INCREF(tmp_src, PyArray_DESCR(self)); - PyArray_Item_XDECREF(dest, PyArray_DESCR(self)); - memmove(dest, tmp_src, itemsize); - dest += itemsize; - tmp_src += itemsize; - } - } - else { - memmove(dest, tmp_src, chunk); - dest += chunk; - } - } - src += chunk*max_item; - } - break; - case NPY_CLIP: - for (i = 0; i < n; i++) { - for (j = 0; j < m; j++) { - tmp = ((npy_intp *)(PyArray_DATA(indices)))[j]; - if (tmp < 0) { - tmp = 0; - } - else if (tmp >= max_item) { - tmp = max_item - 1; - } - tmp_src = src + tmp * chunk; - if (needs_refcounting) { - for (k=0; k < nelem; k++) { - PyArray_Item_INCREF(tmp_src, PyArray_DESCR(self)); - PyArray_Item_XDECREF(dest, PyArray_DESCR(self)); - memmove(dest, tmp_src, itemsize); - dest += itemsize; - tmp_src += itemsize; - } - } - else { - memmove(dest, tmp_src, chunk); - dest += chunk; - } - } - src += chunk*max_item; - } - 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) { - goto fail; - } + if (npy_fasttake( + dest, src, indices_data, n, m, max_item, nelem, chunk, + clipmode, itemsize, needs_refcounting, dtype, axis) < 0) { + goto fail; } Py_XDECREF(indices); diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index bf1fcc76e..5a1135914 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -4484,11 +4484,10 @@ class TestCompress: class TestPutmask: def tst_basic(self, x, T, mask, val): np.putmask(x, mask, val) - assert_equal(x[mask], T(val)) - assert_equal(x.dtype, T) + assert_equal(x[mask], np.array(val, T)) def test_ip_types(self): - unchecked_types = [bytes, unicode, np.void, object] + unchecked_types = [bytes, unicode, np.void] x = np.random.random(1000)*100 mask = x < 40 @@ -4499,6 +4498,10 @@ class TestPutmask: if T not in unchecked_types: self.tst_basic(x.copy().astype(T), T, mask, val) + # Also test string of a length which uses an untypical length + dt = np.dtype("S3") + self.tst_basic(x.astype(dt), dt.type, mask, dt.type(val)[:3]) + def test_mask_size(self): assert_raises(ValueError, np.putmask, np.array([1, 2, 3]), [True], 5) @@ -4538,7 +4541,7 @@ class TestTake: assert_array_equal(x.take(ind, axis=0), x) def test_ip_types(self): - unchecked_types = [bytes, unicode, np.void, object] + unchecked_types = [bytes, unicode, np.void] x = np.random.random(24)*100 x.shape = 2, 3, 4 @@ -4547,6 +4550,9 @@ class TestTake: if T not in unchecked_types: self.tst_basic(x.copy().astype(T)) + # Also test string of a length which uses an untypical length + self.tst_basic(x.astype("S3")) + def test_raise(self): x = np.random.random(24)*100 x.shape = 2, 3, 4 |