diff options
author | Sebastian Berg <sebastian@sipsolutions.net> | 2016-03-11 00:13:58 +0100 |
---|---|---|
committer | Sebastian Berg <sebastian@sipsolutions.net> | 2016-03-12 18:01:35 +0100 |
commit | 4443c4d2a0186e6580b1849d11129e81c16e5efc (patch) | |
tree | 72f8a788ce7d29cd2cf148026f215c61f08ae611 | |
parent | 8ccfedefe9126ceac422158be59c5e5d8fba237b (diff) | |
download | numpy-4443c4d2a0186e6580b1849d11129e81c16e5efc.tar.gz |
BUG: Fix decref before incref for in-place accumulate
closes gh-7402
-rw-r--r-- | numpy/core/src/umath/ufunc_object.c | 17 | ||||
-rw-r--r-- | numpy/core/tests/test_ufunc.py | 16 |
2 files changed, 30 insertions, 3 deletions
diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c index 9e8c3c985..243df4095 100644 --- a/numpy/core/src/umath/ufunc_object.c +++ b/numpy/core/src/umath/ufunc_object.c @@ -3239,17 +3239,22 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, NPY_BEGIN_THREADS_NDITER(iter); do { - dataptr_copy[0] = dataptr[0]; dataptr_copy[1] = dataptr[1]; dataptr_copy[2] = dataptr[0]; /* Copy the first element to start the reduction */ if (otype == NPY_OBJECT) { + /* + * Input (dataptr[0]) and output (dataptr[1]) may point + * to the same memory (i.e. np.add.accumulate(a, out=a)). + * In that case need to incref before decref to avoid the + * possibility of the reference count being zero temporarily. + */ + Py_XINCREF(*(PyObject **)dataptr_copy[1]); Py_XDECREF(*(PyObject **)dataptr_copy[0]); *(PyObject **)dataptr_copy[0] = *(PyObject **)dataptr_copy[1]; - Py_XINCREF(*(PyObject **)dataptr_copy[0]); } else { memcpy(dataptr_copy[0], dataptr_copy[1], itemsize); @@ -3302,10 +3307,16 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, /* Copy the first element to start the reduction */ if (otype == NPY_OBJECT) { + /* + * Input (dataptr[0]) and output (dataptr[1]) may point + * to the same memory (i.e. np.add.accumulate(a, out=a, axis=0)). + * In that case need to incref before decref to avoid the + * possibility of the reference count being zero temporarily. + */ + Py_XINCREF(*(PyObject **)dataptr_copy[1]); Py_XDECREF(*(PyObject **)dataptr_copy[0]); *(PyObject **)dataptr_copy[0] = *(PyObject **)dataptr_copy[1]; - Py_XINCREF(*(PyObject **)dataptr_copy[0]); } else { memcpy(dataptr_copy[0], dataptr_copy[1], itemsize); diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py index eb0985386..ab8cecff0 100644 --- a/numpy/core/tests/test_ufunc.py +++ b/numpy/core/tests/test_ufunc.py @@ -649,6 +649,22 @@ class TestUfunc(TestCase): assert_equal(np.array([[1]], dtype=object).sum(), 1) assert_equal(np.array([[[1, 2]]], dtype=object).sum((0, 1)), [1, 2]) + def test_object_array_accumulate_inplace(self): + # Checks that in-place accumulates work, see also gh-7402 + arr = np.ones(4, dtype=object) + arr[:] = [[1] for i in range(4)] + # Twice reproduced also for tuples: + np.add.accumulate(arr, out=arr) + np.add.accumulate(arr, out=arr) + assert_array_equal(arr, np.array([[1]*i for i in [1, 3, 6, 10]])) + + # And the same if the axis argument is used + arr = np.ones((2, 4), dtype=object) + arr[0, :] = [[2] for i in range(4)] + np.add.accumulate(arr, out=arr, axis=-1) + np.add.accumulate(arr, out=arr, axis=-1) + assert_array_equal(arr[0, :], np.array([[2]*i for i in [1, 3, 6, 10]])) + def test_object_scalar_multiply(self): # Tickets #2469 and #4482 arr = np.matrix([1, 2], dtype=object) |