From db1701baf57965e8533293426a0c3b403e81f39a Mon Sep 17 00:00:00 2001 From: "Travis E. Oliphant" Date: Thu, 21 Jun 2012 04:56:49 -0500 Subject: [PATCH] BUG: Fix boolean indexing to previous behavior by adding an additional check before using the new code path. Add tests. --- doc/release/2.0.0-notes.rst | 14 +++++++----- numpy/core/src/multiarray/mapping.c | 7 ++++-- numpy/core/tests/test_multiarray.py | 35 +++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/doc/release/2.0.0-notes.rst b/doc/release/2.0.0-notes.rst index 7a9d43fa165e..933716faa7ac 100644 --- a/doc/release/2.0.0-notes.rst +++ b/doc/release/2.0.0-notes.rst @@ -24,12 +24,9 @@ The default casting rule for UFunc out= parameters has been changed from rule are likely bugs, so this change may expose previously undetected errors in projects that depend on NumPy. -Full-array boolean indexing used to allow boolean arrays with a size -non-broadcastable to the array size. Now it forces this to be broadcastable. -Since this affects some legacy code, this change will require discussion -during alpha or early beta testing, and a decision to either keep the -stricter behavior, or add in a hack to allow the previous behavior to -work. +Full-array boolean indexing has been optimized to use a different, +optimized code path. This code path should produce the same results, +but any feedback about changes to your code would be appreciated. Attempting to write to a read-only array (one with ``arr.flags.writeable`` set to ``False``) used to raise either a @@ -42,6 +39,11 @@ performance. Because of the nature of floating-point arithmetic, this may subtly change some results, just as linking NumPy to a different BLAS implementations such as MKL can. +If upgrading from 1.5, then generally in 1.6 and 1.7 there have been +substantial code added and some code paths altered, particularly in +the areas of type resolution and buffered iteration over universal +functions. This might have an impact on your code particularly if +you relied on accidental behavior in the past. New features ============ diff --git a/numpy/core/src/multiarray/mapping.c b/numpy/core/src/multiarray/mapping.c index 769fb653eab6..7abb3ff7fe13 100644 --- a/numpy/core/src/multiarray/mapping.c +++ b/numpy/core/src/multiarray/mapping.c @@ -1023,8 +1023,10 @@ array_subscript(PyArrayObject *self, PyObject *op) } /* Boolean indexing special case */ + /* The SIZE check might be overly cautious */ if (PyArray_Check(op) && (PyArray_TYPE((PyArrayObject *)op) == NPY_BOOL) - && (PyArray_NDIM(self) == PyArray_NDIM((PyArrayObject *)op))) { + && (PyArray_NDIM(self) == PyArray_NDIM((PyArrayObject *)op)) + && (PyArray_SIZE((PyArrayObject *)op) == PyArray_SIZE(self))) { return (PyObject *)array_boolean_subscript(self, (PyArrayObject *)op, NPY_CORDER); } @@ -1269,7 +1271,8 @@ array_ass_sub(PyArrayObject *self, PyObject *ind, PyObject *op) /* Boolean indexing special case */ if (PyArray_Check(ind) && (PyArray_TYPE((PyArrayObject *)ind) == NPY_BOOL) && - (PyArray_NDIM(self) == PyArray_NDIM((PyArrayObject *)ind))) { + (PyArray_NDIM(self) == PyArray_NDIM((PyArrayObject *)ind)) && + (PyArray_SIZE(self) == PyArray_SIZE((PyArrayObject *)ind))) { int retcode; PyArrayObject *op_arr; PyArray_Descr *dtype = NULL; diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index bab83031b640..82ead8958c5c 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -1058,6 +1058,41 @@ def test_tuple(self): x[:,:,(0,)] = 2.0 assert_array_equal(x, array([[[2.0]]])) + def test_mask(self): + x = array([1,2,3,4]) + m = array([0,1],bool) + assert_array_equal(x[m], array([2])) + + def test_mask2(self): + x = array([[1,2,3,4],[5,6,7,8]]) + m = array([0,1],bool) + m2 = array([[0,1],[1,0]], bool) + m3 = array([[0,1]], bool) + assert_array_equal(x[m], array([[5,6,7,8]])) + assert_array_equal(x[m2], array([2,5])) + assert_array_equal(x[m3], array([2])) + + def test_assign_mask(self): + x = array([1,2,3,4]) + m = array([0,1],bool) + x[m] = 5 + assert_array_equal(x, array([1,5,3,4])) + + def test_assign_mask(self): + xorig = array([[1,2,3,4],[5,6,7,8]]) + m = array([0,1],bool) + m2 = array([[0,1],[1,0]],bool) + m3 = array([[0,1]], bool) + x = xorig.copy() + x[m] = 10 + assert_array_equal(x, array([[1,2,3,4],[10,10,10,10]])) + x = xorig.copy() + x[m2] = 10 + assert_array_equal(x, array([[1,10,3,4],[10,6,7,8]])) + x = xorig.copy() + x[m3] = 10 + assert_array_equal(x, array([[1,10,3,4],[5,6,7,8]])) + class TestStringCompare(TestCase): def test_string(self):