diff options
author | njsmith <njs@pobox.com> | 2013-04-12 07:12:27 -0700 |
---|---|---|
committer | njsmith <njs@pobox.com> | 2013-04-12 07:12:27 -0700 |
commit | 6ffa2ea9744e01129382b902649edfd50e848401 (patch) | |
tree | 0e3d603bc848c02977c083e8a888a011d0a5d64a /numpy | |
parent | f1b20f92f8a959359d01de5cf05f3d6babe8e145 (diff) | |
parent | 1f8443764550ec4626469f6afd8e78de10e62b2e (diff) | |
download | numpy-6ffa2ea9744e01129382b902649edfd50e848401.tar.gz |
Merge pull request #3225 from seberg/cleanup_subscript-merge
Cleanup subscript rebased
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/src/multiarray/arrayobject.c | 4 | ||||
-rw-r--r-- | numpy/core/src/multiarray/convert_datatype.c | 2 | ||||
-rw-r--r-- | numpy/core/src/multiarray/mapping.c | 668 | ||||
-rw-r--r-- | numpy/core/src/multiarray/mapping.h | 14 | ||||
-rw-r--r-- | numpy/core/src/multiarray/sequence.c | 4 | ||||
-rw-r--r-- | numpy/core/tests/test_indexing.py | 110 |
6 files changed, 482 insertions, 320 deletions
diff --git a/numpy/core/src/multiarray/arrayobject.c b/numpy/core/src/multiarray/arrayobject.c index b559d4aba..ad196e010 100644 --- a/numpy/core/src/multiarray/arrayobject.c +++ b/numpy/core/src/multiarray/arrayobject.c @@ -1172,12 +1172,12 @@ _void_compare(PyArrayObject *self, PyArrayObject *other, int cmp_op) if NPY_TITLE_KEY(key, value) { continue; } - a = PyArray_EnsureAnyArray(array_subscript(self, key)); + a = array_subscript_asarray(self, key); if (a == NULL) { Py_XDECREF(res); return NULL; } - b = array_subscript(other, key); + b = array_subscript_asarray(other, key); if (b == NULL) { Py_XDECREF(res); Py_DECREF(a); diff --git a/numpy/core/src/multiarray/convert_datatype.c b/numpy/core/src/multiarray/convert_datatype.c index 586b85b1e..e1483f4e1 100644 --- a/numpy/core/src/multiarray/convert_datatype.c +++ b/numpy/core/src/multiarray/convert_datatype.c @@ -1817,7 +1817,7 @@ PyArray_ConvertToCommonType(PyObject *op, int *retn) if (PyArray_Check(op)) { for (i = 0; i < n; i++) { - mps[i] = (PyArrayObject *) array_big_item((PyArrayObject *)op, i); + mps[i] = (PyArrayObject *) array_item_asarray((PyArrayObject *)op, i); } if (!PyArray_ISCARRAY((PyArrayObject *)op)) { for (i = 0; i < n; i++) { diff --git a/numpy/core/src/multiarray/mapping.c b/numpy/core/src/multiarray/mapping.c index 6cc00edab..0b4022874 100644 --- a/numpy/core/src/multiarray/mapping.c +++ b/numpy/core/src/multiarray/mapping.c @@ -42,8 +42,29 @@ array_length(PyArrayObject *self) } } +/* Get array item as scalar type */ NPY_NO_EXPORT PyObject * -array_big_item(PyArrayObject *self, npy_intp i) +array_item_asscalar(PyArrayObject *self, npy_intp i) +{ + char *item; + npy_intp dim0; + + /* Bounds check and get the data pointer */ + dim0 = PyArray_DIM(self, 0); + if (i < 0) { + i += dim0; + } + if (i < 0 || i >= dim0) { + PyErr_SetString(PyExc_IndexError, "index out of bounds"); + return NULL; + } + item = PyArray_BYTES(self) + i * PyArray_STRIDE(self, 0); + return PyArray_Scalar(item, PyArray_DESCR(self), (PyObject *)self); +} + +/* Get array item as ndarray type */ +NPY_NO_EXPORT PyObject * +array_item_asarray(PyArrayObject *self, npy_intp i) { char *item; PyArrayObject *ret; @@ -86,40 +107,23 @@ array_big_item(PyArrayObject *self, npy_intp i) return (PyObject *)ret; } -NPY_NO_EXPORT int -_array_ass_item(PyArrayObject *self, Py_ssize_t i, PyObject *v) -{ - return array_ass_big_item(self, (npy_intp) i, v); -} - -/* contains optimization for 1-d arrays */ +/* Get array item at given index */ NPY_NO_EXPORT PyObject * -array_item_nice(PyArrayObject *self, Py_ssize_t _i) +array_item(PyArrayObject *self, Py_ssize_t _i) { /* Workaround Python 2.4: Py_ssize_t not the same as npyint_p */ npy_intp i = _i; if (PyArray_NDIM(self) == 1) { - char *item; - npy_intp dim0; - - /* Bounds check and get the data pointer */ - dim0 = PyArray_DIM(self, 0); - if (check_and_adjust_index(&i, dim0, 0) < 0) { - return NULL; - } - item = PyArray_BYTES(self) + i * PyArray_STRIDE(self, 0); - - return PyArray_Scalar(item, PyArray_DESCR(self), (PyObject *)self); + return array_item_asscalar(self, (npy_intp) i); } else { - return PyArray_Return( - (PyArrayObject *) array_big_item(self, (npy_intp) i)); + return array_item_asarray(self, (npy_intp) i); } } NPY_NO_EXPORT int -array_ass_big_item(PyArrayObject *self, npy_intp i, PyObject *v) +array_ass_item_object(PyArrayObject *self, npy_intp i, PyObject *v) { PyArrayObject *tmp; char *item; @@ -138,14 +142,14 @@ array_ass_big_item(PyArrayObject *self, npy_intp i, PyObject *v) if (PyArray_NDIM(self) == 0) { PyErr_SetString(PyExc_IndexError, - "0-d arrays can't be indexed."); + "0-d arrays can't be indexed"); return -1; } /* For multi-dimensional arrays, use CopyObject */ if (PyArray_NDIM(self) > 1) { - tmp = (PyArrayObject *)array_big_item(self, i); + tmp = (PyArrayObject *)array_item_asarray(self, i); if(tmp == NULL) { return -1; } @@ -161,7 +165,7 @@ array_ass_big_item(PyArrayObject *self, npy_intp i, PyObject *v) } item = PyArray_BYTES(self) + i * PyArray_STRIDE(self, 0); - return PyArray_DESCR(self)->f->setitem(v, item, self); + return PyArray_SETITEM(self, item, v); } /* -------------------------------------------------------------- */ @@ -306,6 +310,12 @@ PyArray_GetMap(PyArrayMapIterObject *mit) return (PyObject *)ret; } +NPY_NO_EXPORT int +array_ass_item(PyArrayObject *self, Py_ssize_t i, PyObject *v) +{ + return array_ass_item_object(self, (npy_intp) i, v); +} + static int PyArray_SetMap(PyArrayMapIterObject *mit, PyObject *op) { @@ -562,23 +572,31 @@ array_subscript_simple(PyArrayObject *self, PyObject *op) * Unfortunately, SciPy and possibly other code seems to rely * on the lenient coercion. :( */ + if (!(PyArray_Check(op) && (PyArray_SIZE((PyArrayObject*)op) > 1))) { #if 0 /*PY_VERSION_HEX >= 0x02050000*/ - PyObject *ind = PyNumber_Index(op); - if (ind != NULL) { - value = PyArray_PyIntAsIntp(ind); - Py_DECREF(ind); - } - else { - value = -1; - } + PyObject *ind = PyNumber_Index(op); + if (ind != NULL) { + value = PyArray_PyIntAsIntp(ind); + Py_DECREF(ind); + } + else { + value = -1; + } #else - value = PyArray_PyIntAsIntp(op); + value = PyArray_PyIntAsIntp(op); #endif - if (value == -1 && PyErr_Occurred()) { - PyErr_Clear(); - } - else { - return array_big_item(self, value); + if (value == -1 && PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + /* Operand is not an integer type */ + PyErr_Clear(); + } + else { + return NULL; + } + } + else { + return array_item_asarray(self, value); + } } /* Standard (view-based) Indexing */ @@ -914,17 +932,180 @@ array_ass_boolean_subscript(PyArrayObject *self, return 0; } -NPY_NO_EXPORT PyObject * -array_subscript(PyArrayObject *self, PyObject *op) + +/* Check if ind is a tuple and if it has as many elements as arr has axes. */ +static NPY_INLINE int +_is_full_index(PyObject *ind, PyArrayObject *arr) { - int nd, fancy; - PyArrayObject *other; - PyArrayMapIterObject *mit; + return PyTuple_Check(ind) && (PyTuple_GET_SIZE(ind) == PyArray_NDIM(arr)); +} + +/* + * Returns 0 if tuple-object seq is not a tuple of integers. + * If the return value is positive, vals will be filled with the elements + * from the tuple. + * Returns -1 on error. + */ +static int +_tuple_of_integers(PyObject *seq, npy_intp *vals, int maxvals) +{ + int i; PyObject *obj; + npy_intp temp; - if (PyString_Check(op) || PyUnicode_Check(op)) { + for(i=0; i<maxvals; i++) { + obj = PyTuple_GET_ITEM(seq, i); + if ((PyArray_Check(obj) && PyArray_NDIM((PyArrayObject *)obj) > 0) + || PyList_Check(obj)) { + return 0; + } + temp = PyArray_PyIntAsIntp(obj); + if (error_converting(temp)) { + PyErr_Clear(); + return 0; + } + if (!PyIndex_Check_Or_Unsupported(obj)) { + if (DEPRECATE("non-integer scalar index. In a future numpy " + "release, this will raise an error.") < 0) { + return -1; + } + } + vals[i] = temp; + } + return 1; +} + + +/* return TRUE if ellipses are found else return FALSE */ +static npy_bool +_check_ellipses(PyObject *op) +{ + if ((op == Py_Ellipsis) || PyString_Check(op) || PyUnicode_Check(op)) { + return NPY_TRUE; + } + else if (PyBool_Check(op) || PyArray_IsScalar(op, Bool) || + (PyArray_Check(op) && + (PyArray_DIMS((PyArrayObject *)op)==0) && + PyArray_ISBOOL((PyArrayObject *)op))) { + return NPY_TRUE; + } + else if (PySequence_Check(op)) { + Py_ssize_t n, i; PyObject *temp; + n = PySequence_Size(op); + i = 0; + while (i < n) { + temp = PySequence_GetItem(op, i); + if (temp == Py_Ellipsis) { + Py_DECREF(temp); + return NPY_TRUE; + } + Py_DECREF(temp); + i++; + } + } + return NPY_FALSE; +} + +NPY_NO_EXPORT PyObject * +array_subscript_fancy(PyArrayObject *self, PyObject *op, int fancy) + { + int oned; + PyObject *other; + PyArrayMapIterObject *mit; + + oned = ((PyArray_NDIM(self) == 1) && + !(PyTuple_Check(op) && PyTuple_GET_SIZE(op) > 1)); + + /* wrap arguments into a mapiter object */ + mit = (PyArrayMapIterObject *) PyArray_MapIterNew(op, oned, fancy); + if (mit == NULL) { + return NULL; + } + if (oned) { + PyArrayIterObject *it; + PyObject *rval; + it = (PyArrayIterObject *) PyArray_IterNew((PyObject *)self); + if (it == NULL) { + Py_DECREF(mit); + return NULL; + } + rval = iter_subscript(it, mit->indexobj); + Py_DECREF(it); + Py_DECREF(mit); + return rval; + } + if (PyArray_MapIterBind(mit, self) != 0) { + return NULL; + } + other = (PyObject *)PyArray_GetMap(mit); + Py_DECREF(mit); + return other; +} + +/* make sure subscript always returns an array object */ +NPY_NO_EXPORT PyObject * +array_subscript_asarray(PyArrayObject *self, PyObject *op) +{ + return PyArray_EnsureAnyArray(array_subscript(self, op)); +} + +NPY_NO_EXPORT PyObject * +array_subscript_fromobject(PyArrayObject *self, PyObject *op) +{ + int fancy; + npy_intp vals[NPY_MAXDIMS]; + + /* Integer index */ + if (PyArray_IsIntegerScalar(op) || (PyIndex_Check(op) && + !PySequence_Check(op))) { + npy_intp value = PyArray_PyIntAsIntp(op); + if (value == -1 && PyErr_Occurred()) { + /* fail on error */ + PyErr_SetString(PyExc_IndexError, + "cannot convert index to integer"); + return NULL; + } + else { + return array_item(self, (Py_ssize_t) value); + } + } + /* optimization for a tuple of integers */ + if (PyArray_NDIM(self) > 1 && _is_full_index(op, self)) { + int ret = _tuple_of_integers(op, vals, PyArray_NDIM(self)); + /* In case an exception occurred (e.g. in PyErr_WarnEx) */ + if (ret < 0) { + return NULL; + } + else if (ret > 0) { + int idim, ndim = PyArray_NDIM(self); + npy_intp *shape = PyArray_DIMS(self); + npy_intp *strides = PyArray_STRIDES(self); + char *item = PyArray_BYTES(self); + for (idim = 0; idim < ndim; idim++) { + npy_intp v = vals[idim]; + if (check_and_adjust_index(&v, shape[idim], idim) < 0) { + return NULL; + } + item += v * strides[idim]; + } + return PyArray_Scalar(item, PyArray_DESCR(self), (PyObject *)self); + } + } + + if ((PyNumber_Check(op) || PyArray_IsScalar(op, Number)) && + !PyIndex_Check_Or_Unsupported(op)) { + if (DEPRECATE("non-integer scalar index. In a future numpy " + "release, this will raise an error.") < 0) { + return NULL; + } + } + + /* Check for single field access */ + if (PyString_Check(op) || PyUnicode_Check(op)) { + PyObject *temp, *obj; + if (PyDataType_HASFIELDS(PyArray_DESCR(self))) { obj = PyDict_GetItem(PyArray_DESCR(self)->fields, op); if (obj != NULL) { @@ -944,7 +1125,7 @@ array_subscript(PyArrayObject *self, PyObject *op) temp = PyUnicode_AsUnicodeEscapeString(op); } PyErr_Format(PyExc_ValueError, - "field named %s not found.", + "field named %s not found", PyBytes_AsString(temp)); if (temp != op) { Py_DECREF(temp); @@ -957,6 +1138,7 @@ array_subscript(PyArrayObject *self, PyObject *op) PySequence_Check(op) && !PyTuple_Check(op)) { int seqlen, i; + PyObject *obj; seqlen = PySequence_Size(op); for (i = 0; i < seqlen; i++) { obj = PySequence_GetItem(op, i); @@ -967,7 +1149,7 @@ array_subscript(PyArrayObject *self, PyObject *op) Py_DECREF(obj); } /* - * extract multiple fields if all elements in sequence + * Extract multiple fields if all elements in sequence * are either string or unicode (i.e. no break occurred). */ fancy = ((seqlen > 0) && (i == seqlen)); @@ -988,15 +1170,19 @@ array_subscript(PyArrayObject *self, PyObject *op) } } + /* Check for Ellipsis index */ if (op == Py_Ellipsis) { Py_INCREF(self); return (PyObject *)self; } if (PyArray_NDIM(self) == 0) { + int nd; + /* Check for None index */ if (op == Py_None) { return add_new_axes_0d(self, 1); } + /* Check for (empty) tuple index */ if (PyTuple_Check(op)) { if (0 == PyTuple_GET_SIZE(op)) { Py_INCREF(self); @@ -1026,56 +1212,64 @@ array_subscript(PyArrayObject *self, PyObject *op) NULL); } } - PyErr_SetString(PyExc_IndexError, "0-d arrays can't be indexed."); + PyErr_SetString(PyExc_IndexError, + "0-dimensional arrays can't be indexed"); return NULL; } + fancy = fancy_indexing_check(op); + if (fancy != SOBJ_NOTFANCY) { + return array_subscript_fancy(self, op, fancy); + } + else { + return array_subscript_simple(self, op); + } +} + +NPY_NO_EXPORT PyObject * +array_subscript(PyArrayObject *self, PyObject *op) +{ + int fancy; + PyObject *ret = NULL; + if (!PyArray_Check(op)) { + ret = array_subscript_fromobject(self, op); + } + /* Boolean indexing special case */ - /* The SIZE check might be overly cautious */ - if (PyArray_Check(op) && (PyArray_TYPE((PyArrayObject *)op) == NPY_BOOL) + /* The SIZE check is to ensure old behaviour for non-matching arrays. */ + else if (PyArray_ISBOOL((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); } - - fancy = fancy_indexing_check(op); - if (fancy != SOBJ_NOTFANCY) { - int oned; - - oned = ((PyArray_NDIM(self) == 1) && - !(PyTuple_Check(op) && PyTuple_GET_SIZE(op) > 1)); - - /* wrap arguments into a mapiter object */ - mit = (PyArrayMapIterObject *) PyArray_MapIterNew(op, oned, fancy); - if (mit == NULL) { - return NULL; - } - if (oned) { - PyArrayIterObject *it; - PyObject *rval; - it = (PyArrayIterObject *) PyArray_IterNew((PyObject *)self); - if (it == NULL) { - Py_DECREF(mit); - return NULL; - } - rval = iter_subscript(it, mit->indexobj); - Py_DECREF(it); - Py_DECREF(mit); - return rval; + /* Error case when indexing 0-dim array with non-boolean. */ + else if (PyArray_NDIM(self) == 0) { + PyErr_SetString(PyExc_IndexError, + "0-dimensional arrays can't be indexed"); + return NULL; + } + + else { + fancy = fancy_indexing_check(op); + if (fancy != SOBJ_NOTFANCY) { + ret = array_subscript_fancy(self, op, fancy); } - if (PyArray_MapIterBind(mit, self) != 0) { - return NULL; + else { + ret = array_subscript_simple(self, op); } - other = (PyArrayObject *)PyArray_GetMap(mit); - Py_DECREF(mit); - return (PyObject *)other; } - - return array_subscript_simple(self, op); + if (ret == NULL) { + return NULL; + } + + if (PyArray_Check(ret) && PyArray_NDIM((PyArrayObject *)ret) == 0 + && !_check_ellipses(op)) { + return PyArray_Return((PyArrayObject *)ret); + } + return ret; } - /* * Another assignment hacked by using CopyObject. * This only works if subscript returns a standard view. @@ -1092,13 +1286,20 @@ array_ass_sub_simple(PyArrayObject *self, PyObject *ind, PyObject *op) npy_intp value; value = PyArray_PyIntAsIntp(ind); - if (!error_converting(value)) { - return array_ass_big_item(self, value, op); + if (value == -1 && PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + /* Operand is not an integer type */ + PyErr_Clear(); + } + else { + return -1; + } + } + else { + return array_ass_item_object(self, value, op); } - PyErr_Clear(); /* Rest of standard (view-based) indexing */ - if (PyArray_CheckExact(self)) { tmp = (PyArrayObject *)array_subscript_simple(self, ind); if (tmp == NULL) { @@ -1119,7 +1320,7 @@ array_ass_sub_simple(PyArrayObject *self, PyObject *ind, PyObject *op) } if (!PyArray_Check(tmp0)) { PyErr_SetString(PyExc_RuntimeError, - "Getitem not returning array."); + "Getitem not returning array"); Py_DECREF(tmp0); return -1; } @@ -1131,53 +1332,44 @@ array_ass_sub_simple(PyArrayObject *self, PyObject *ind, PyObject *op) return ret; } -/* Check if ind is a tuple and if it has as many elements as arr has axes. */ -static NPY_INLINE int -_is_full_index(PyObject *ind, PyArrayObject *arr) -{ - return PyTuple_Check(ind) && (PyTuple_GET_SIZE(ind) == PyArray_NDIM(arr)); -} - -/* - * Returns 0 if tuple-object seq is not a tuple of integers. - * If the return value is positive, vals will be filled with the elements - * from the tuple. - * Returns -1 on error. - */ static int -_tuple_of_integers(PyObject *seq, npy_intp *vals, int maxvals) +array_ass_sub_fancy(PyArrayObject *self, PyObject *ind, PyObject *op, int fancy) { - int i; - PyObject *obj; - npy_intp temp; + int oned, ret; + PyArrayMapIterObject *mit; + oned = ((PyArray_NDIM(self) == 1) && + !(PyTuple_Check(ind) && PyTuple_GET_SIZE(ind) > 1)); + mit = (PyArrayMapIterObject *) PyArray_MapIterNew(ind, oned, fancy); + if (mit == NULL) { + return -1; + } + if (oned) { + PyArrayIterObject *it; + int rval; - for(i=0; i<maxvals; i++) { - obj = PyTuple_GET_ITEM(seq, i); - if ((PyArray_Check(obj) && PyArray_NDIM((PyArrayObject *)obj) > 0) - || PyList_Check(obj)) { - return 0; - } - temp = PyArray_PyIntAsIntp(obj); - if (error_converting(temp)) { - return 0; - } - if (!PyIndex_Check_Or_Unsupported(obj)) { - if (DEPRECATE("non-integer scalar index. In a future numpy " - "release, this will raise an error.") < 0) { - return -1; - } + it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)self); + if (it == NULL) { + Py_DECREF(mit); + return -1; } - vals[i] = temp; + rval = iter_ass_subscript(it, mit->indexobj, op); + Py_DECREF(it); + Py_DECREF(mit); + return rval; } - return 1; + if (PyArray_MapIterBind(mit, self) != 0) { + return -1; + } + ret = PyArray_SetMap(mit, op); + Py_DECREF(mit); + return ret; } static int array_ass_sub(PyArrayObject *self, PyObject *ind, PyObject *op) { - int ret, oned, fancy; - PyArrayMapIterObject *mit; + int fancy; npy_intp vals[NPY_MAXDIMS]; if (op == NULL) { @@ -1189,19 +1381,22 @@ array_ass_sub(PyArrayObject *self, PyObject *ind, PyObject *op) return -1; } - if (PyInt_Check(ind) || PyArray_IsScalar(ind, Integer) || - PyLong_Check(ind) || (PyIndex_Check(ind) && - !PySequence_Check(ind))) { - npy_intp value; - value = PyArray_PyIntAsIntp(ind); - if (PyErr_Occurred()) { - PyErr_Clear(); + /* Integer index */ + if (PyArray_IsIntegerScalar(ind) || (PyIndex_Check(ind) && + !PySequence_Check(ind))) { + npy_intp value = PyArray_PyIntAsIntp(ind); + if (value == -1 && PyErr_Occurred()) { + /* fail on error */ + PyErr_SetString(PyExc_IndexError, + "cannot convert index to integer"); + return -1; } else { - return array_ass_big_item(self, value, op); + return array_ass_item_object(self, value, op); } } + /* Single field access */ if (PyString_Check(ind) || PyUnicode_Check(ind)) { if (PyDataType_HASFIELDS(PyArray_DESCR(self))) { PyObject *obj; @@ -1218,19 +1413,19 @@ array_ass_sub(PyArrayObject *self, PyObject *ind, PyObject *op) } } } - #if defined(NPY_PY3K) PyErr_Format(PyExc_ValueError, - "field named %S not found.", + "field named %S not found", ind); #else PyErr_Format(PyExc_ValueError, - "field named %s not found.", + "field named %s not found", PyString_AsString(ind)); #endif return -1; } + /* Ellipsis index */ if (ind == Py_Ellipsis) { /* * Doing "a[...] += 1" triggers assigning an array to itself, @@ -1253,29 +1448,33 @@ array_ass_sub(PyArrayObject *self, PyObject *ind, PyObject *op) * 3) Using newaxis (None) * 4) Boolean mask indexing */ + + /* Check for None or empty tuple */ if (ind == Py_None || (PyTuple_Check(ind) && (0 == PyTuple_GET_SIZE(ind) || count_new_axes_0d(ind) > 0))) { - return PyArray_DESCR(self)->f->setitem(op, PyArray_DATA(self), self); + return PyArray_SETITEM(self, PyArray_DATA(self), op); } + /* Check for boolean index */ if (PyBool_Check(ind) || PyArray_IsScalar(ind, Bool) || (PyArray_Check(ind) && - (PyArray_DIMS((PyArrayObject *)ind)==0) && + (PyArray_NDIM((PyArrayObject *)ind)==0) && PyArray_ISBOOL((PyArrayObject *)ind))) { if (PyObject_IsTrue(ind)) { return PyArray_CopyObject(self, op); } - else { /* don't do anything */ + else { /* False: don't do anything */ return 0; } } - PyErr_SetString(PyExc_IndexError, "0-d arrays can't be indexed."); + PyErr_SetString(PyExc_IndexError, + "0-dimensional arrays can't be indexed"); return -1; } - /* Integer-tuple */ + /* Integer-tuple index */ if (_is_full_index(ind, self)) { - ret = _tuple_of_integers(ind, vals, PyArray_NDIM(self)); + int ret = _tuple_of_integers(ind, vals, PyArray_NDIM(self)); /* In case an exception occurred (e.g. in PyErr_WarnEx) */ if (ret < 0) { return -1; @@ -1284,7 +1483,7 @@ array_ass_sub(PyArrayObject *self, PyObject *ind, PyObject *op) int idim, ndim = PyArray_NDIM(self); npy_intp *shape = PyArray_DIMS(self); npy_intp *strides = PyArray_STRIDES(self); - char *item = PyArray_DATA(self); + char *item = PyArray_BYTES(self); for (idim = 0; idim < ndim; idim++) { npy_intp v = vals[idim]; if (check_and_adjust_index(&v, shape[idim], idim) < 0) { @@ -1292,10 +1491,9 @@ array_ass_sub(PyArrayObject *self, PyObject *ind, PyObject *op) } item += v * strides[idim]; } - return PyArray_DESCR(self)->f->setitem(op, item, self); + return PyArray_SETITEM(self, item, op); } } - PyErr_Clear(); /* Boolean indexing special case */ if (PyArray_Check(ind) && @@ -1336,162 +1534,20 @@ array_ass_sub(PyArrayObject *self, PyObject *ind, PyObject *op) fancy = fancy_indexing_check(ind); if (fancy != SOBJ_NOTFANCY) { - - oned = ((PyArray_NDIM(self) == 1) && - !(PyTuple_Check(ind) && PyTuple_GET_SIZE(ind) > 1)); - mit = (PyArrayMapIterObject *) PyArray_MapIterNew(ind, oned, fancy); - if (mit == NULL) { - return -1; - } - if (oned) { - PyArrayIterObject *it; - int rval; - - it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)self); - if (it == NULL) { - Py_DECREF(mit); - return -1; - } - rval = iter_ass_subscript(it, mit->indexobj, op); - Py_DECREF(it); - Py_DECREF(mit); - return rval; - } - if (PyArray_MapIterBind(mit, self) != 0) { - return -1; - } - ret = PyArray_SetMap(mit, op); - Py_DECREF(mit); - return ret; - } - - return array_ass_sub_simple(self, ind, op); -} - - -/* - * There are places that require that array_subscript return a PyArrayObject - * and not possibly a scalar. Thus, this is the function exposed to - * Python so that 0-dim arrays are passed as scalars - */ - - -static PyObject * -array_subscript_nice(PyArrayObject *self, PyObject *op) -{ - - PyArrayObject *mp; - int ret; - npy_intp vals[NPY_MAXDIMS]; - - if (PyInt_Check(op) || PyArray_IsScalar(op, Integer) || - PyLong_Check(op) || (PyIndex_Check(op) && - !PySequence_Check(op))) { - npy_intp value; - value = PyArray_PyIntAsIntp(op); - if (PyErr_Occurred()) { - PyErr_Clear(); - } - else { - return array_item_nice(self, (Py_ssize_t) value); - } - } - /* - * Optimization for a tuple of integers that is the same size as the - * array's dimension. - */ - if (PyArray_NDIM(self) > 1 && _is_full_index(op, self)) { - ret = _tuple_of_integers(op, vals, PyArray_NDIM(self)); - /* In case an exception occurred (e.g. in PyErr_WarnEx) */ - if (ret < 0) { - return NULL; - } - else if (ret > 0) { - int idim, ndim = PyArray_NDIM(self); - npy_intp *shape = PyArray_DIMS(self); - npy_intp *strides = PyArray_STRIDES(self); - char *item = PyArray_DATA(self); - for (idim = 0; idim < ndim; idim++) { - npy_intp v = vals[idim]; - if (check_and_adjust_index(&v, shape[idim], idim) < 0) { - return NULL; - } - item += v * strides[idim]; - } - return PyArray_Scalar(item, PyArray_DESCR(self), (PyObject *)self); - } - } - PyErr_Clear(); - if ((PyNumber_Check(op) || PyArray_IsScalar(op, Number)) && - !PyIndex_Check_Or_Unsupported(op)) { - if (DEPRECATE("non-integer scalar index. In a future numpy " - "release, this will raise an error.") < 0) { - return NULL; - } - } - mp = (PyArrayObject *)array_subscript(self, op); - /* - * mp could be a scalar if op is not an Int, Scalar, Long or other Index - * object and still convertable to an integer (so that the code goes to - * array_subscript_simple). So, this cast is a bit dangerous.. - */ - - if (mp == NULL) { - return NULL; + return array_ass_sub_fancy(self, ind, op, fancy); } - - if (PyErr_Occurred()) { - Py_XDECREF(mp); - return NULL; - } - - /* - * The following adds some additional logic to avoid calling - * PyArray_Return if there is an ellipsis. - */ - - if (PyArray_Check(mp) && PyArray_NDIM(mp) == 0) { - npy_bool noellipses = NPY_TRUE; - if ((op == Py_Ellipsis) || PyString_Check(op) || PyUnicode_Check(op)) { - noellipses = NPY_FALSE; - } - else if (PyBool_Check(op) || PyArray_IsScalar(op, Bool) || - (PyArray_Check(op) && - (PyArray_DIMS((PyArrayObject *)op)==0) && - PyArray_ISBOOL((PyArrayObject *)op))) { - noellipses = NPY_FALSE; - } - else if (PySequence_Check(op)) { - Py_ssize_t n, i; - PyObject *temp; - - n = PySequence_Size(op); - i = 0; - while (i < n && noellipses) { - temp = PySequence_GetItem(op, i); - if (temp == Py_Ellipsis) { - noellipses = NPY_FALSE; - } - Py_DECREF(temp); - i++; - } - } - if (noellipses) { - return PyArray_Return(mp); - } + else { + return array_ass_sub_simple(self, ind, op); } - - return (PyObject *)mp; } - NPY_NO_EXPORT PyMappingMethods array_as_mapping = { #if PY_VERSION_HEX >= 0x02050000 (lenfunc)array_length, /*mp_length*/ #else (inquiry)array_length, /*mp_length*/ #endif - (binaryfunc)array_subscript_nice, /*mp_subscript*/ + (binaryfunc)array_subscript, /*mp_subscript*/ (objobjargproc)array_ass_sub, /*mp_ass_subscript*/ }; @@ -1528,6 +1584,14 @@ _nonzero_indices(PyObject *myBool, PyArrayIterObject **iters) return -1; } nd = PyArray_NDIM(ba); + + if (nd == 0) { + PyErr_SetString(PyExc_IndexError, + "only scalars can be indexed by 0-dimensional " + "boolean arrays"); + goto fail; + } + for (j = 0; j < nd; j++) { iters[j] = NULL; } @@ -1566,6 +1630,7 @@ _nonzero_indices(PyObject *myBool, PyArrayIterObject **iters) } /* + * Loop through the Boolean array and copy coordinates * for non-zero entries */ @@ -1920,20 +1985,14 @@ PyArray_MapIterNew(PyObject *indexobj, int oned, int fancy) } mit = (PyArrayMapIterObject *)PyArray_malloc(sizeof(PyArrayMapIterObject)); + /* set all attributes of mapiter to zero */ + memset(mit, 0, sizeof(PyArrayMapIterObject)); PyObject_Init((PyObject *)mit, &PyArrayMapIter_Type); if (mit == NULL) { return NULL; } - for (i = 0; i < NPY_MAXDIMS; i++) { - mit->iters[i] = NULL; - } - mit->index = 0; - mit->ait = NULL; - mit->subspace = NULL; - mit->numiter = 0; + /* initialize mapiter attributes */ mit->consec = 1; - Py_INCREF(indexobj); - mit->indexobj = indexobj; if (fancy == SOBJ_LISTTUP) { PyObject *newobj; @@ -1941,10 +2000,13 @@ PyArray_MapIterNew(PyObject *indexobj, int oned, int fancy) if (newobj == NULL) { goto fail; } - Py_DECREF(indexobj); indexobj = newobj; mit->indexobj = indexobj; } + else { + Py_INCREF(indexobj); + mit->indexobj = indexobj; + } if (oned) { @@ -1957,8 +2019,8 @@ PyArray_MapIterNew(PyObject *indexobj, int oned, int fancy) */ /* convert all inputs to iterators */ - if (PyArray_Check(indexobj) && - (PyArray_TYPE((PyArrayObject *)indexobj) == NPY_BOOL)) { + if (PyArray_Check(indexobj) && PyArray_ISBOOL(indexobj) + && !PyArray_IsZeroDim(indexobj)) { mit->numiter = _nonzero_indices(indexobj, mit->iters); if (mit->numiter < 0) { goto fail; diff --git a/numpy/core/src/multiarray/mapping.h b/numpy/core/src/multiarray/mapping.h index b00397436..816b3b8e6 100644 --- a/numpy/core/src/multiarray/mapping.h +++ b/numpy/core/src/multiarray/mapping.h @@ -7,14 +7,20 @@ extern NPY_NO_EXPORT PyMappingMethods array_as_mapping; NPY_NO_EXPORT PyMappingMethods array_as_mapping; #endif -NPY_NO_EXPORT PyObject * -array_big_item(PyArrayObject *self, npy_intp i); - NPY_NO_EXPORT Py_ssize_t array_length(PyArrayObject *self); NPY_NO_EXPORT PyObject * -array_item_nice(PyArrayObject *self, Py_ssize_t i); +array_item_asarray(PyArrayObject *self, npy_intp i); + +NPY_NO_EXPORT PyObject * +array_item_asscalar(PyArrayObject *self, npy_intp i); + +NPY_NO_EXPORT PyObject * +array_item(PyArrayObject *self, Py_ssize_t i); + +NPY_NO_EXPORT PyObject * +array_subscript_asarray(PyArrayObject *self, PyObject *op); NPY_NO_EXPORT PyObject * array_subscript(PyArrayObject *self, PyObject *op); diff --git a/numpy/core/src/multiarray/sequence.c b/numpy/core/src/multiarray/sequence.c index e5f74251e..6a9ecad97 100644 --- a/numpy/core/src/multiarray/sequence.c +++ b/numpy/core/src/multiarray/sequence.c @@ -134,7 +134,7 @@ NPY_NO_EXPORT PySequenceMethods array_as_sequence = { (lenfunc)array_length, /*sq_length*/ (binaryfunc)NULL, /*sq_concat is handled by nb_add*/ (ssizeargfunc)NULL, - (ssizeargfunc)array_item_nice, + (ssizeargfunc)array_item, (ssizessizeargfunc)array_slice, (ssizeobjargproc)array_ass_item, /*sq_ass_item*/ (ssizessizeobjargproc)array_ass_slice, /*sq_ass_slice*/ @@ -145,7 +145,7 @@ NPY_NO_EXPORT PySequenceMethods array_as_sequence = { (inquiry)array_length, /*sq_length*/ (binaryfunc)NULL, /*sq_concat is handled by nb_add*/ (intargfunc)NULL, /*sq_repeat is handled nb_multiply*/ - (intargfunc)array_item_nice, /*sq_item*/ + (intargfunc)array_item, /*sq_item*/ (intintargfunc)array_slice, /*sq_slice*/ (intobjargproc)array_ass_item, /*sq_ass_item*/ (intintobjargproc)array_ass_slice, /*sq_ass_slice*/ diff --git a/numpy/core/tests/test_indexing.py b/numpy/core/tests/test_indexing.py index 1a23da3c5..6906dcf6b 100644 --- a/numpy/core/tests/test_indexing.py +++ b/numpy/core/tests/test_indexing.py @@ -14,14 +14,108 @@ import sys, warnings # but hopefully NumPy indexing can be changed to be more systematic # at some point in the future. -def test_boolean_indexing(): - # Indexing a 2-dimensional array with a length-1 array of 'True' - a = np.array([[ 0., 0., 0.]]) - b = np.array([ True], dtype=bool) - assert_equal(a[b], a) - - a[b] = 1. - assert_equal(a, [[1., 1., 1.]]) +class TestIndexing(TestCase): + + def test_none_index(self): + # `None` index adds newaxis + a = np.array([1, 2, 3]) + assert_equal(a[None], a[np.newaxis]) + assert_equal(a[None].ndim, a.ndim + 1) + + def test_empty_tuple_index(self): + # Empty tuple index creates a view + a = np.array([1, 2, 3]) + assert_equal(a[()], a) + assert_(a[()].base is a) + + def test_empty_fancy_index(self): + # Empty list index creates an empty array + # with the same dtype (but with weird shape) + a = np.array([1, 2, 3]) + assert_equal(a[[]], []) + assert_equal(a[[]].dtype, a.dtype) + + b = np.array([], dtype=np.intp) + assert_equal(a[[]], []) + assert_equal(a[[]].dtype, a.dtype) + + b = np.array([]) + assert_raises(IndexError, a.__getitem__, b) + + def test_ellipsis_index(self): + # Ellipsis index does not create a view + a = np.array([[1, 2, 3], + [4 ,5, 6], + [7, 8, 9]]) + assert_equal(a[...], a) + assert_(a[...] is a) + + # Slicing with ellipsis can skip an + # arbitrary number of dimensions + assert_equal(a[0, ...], a[0]) + assert_equal(a[0, ...], a[0, :]) + assert_equal(a[..., 0], a[:, 0]) + + # Slicing with ellipsis always results + # in an array, not a scalar + assert_equal(a[0, ..., 1], np.array(2)) + + def test_single_int_index(self): + # Single integer index selects one row + a = np.array([[1, 2, 3], + [4 ,5, 6], + [7, 8, 9]]) + + assert_equal(a[0], [1, 2, 3]) + assert_equal(a[-1], [7, 8, 9]) + + # Index out of bounds produces IndexError + assert_raises(IndexError, a.__getitem__, 1<<30) + # Index overflow produces IndexError + assert_raises(IndexError, a.__getitem__, 1<<64) + + def test_single_bool_index(self): + # Single boolean index + a = np.array([[1, 2, 3], + [4 ,5, 6], + [7, 8, 9]]) + + # Python boolean converts to integer + assert_equal(a[True], a[1]) + assert_equal(a[False], a[0]) + + # Same with NumPy boolean scalar + assert_equal(a[np.array(True)], a[1]) + assert_equal(a[np.array(False)], a[0]) + + def test_boolean_indexing_onedim(self): + # Indexing a 2-dimensional array with + # boolean array of length one + a = np.array([[ 0., 0., 0.]]) + b = np.array([ True], dtype=bool) + assert_equal(a[b], a) + # boolean assignment + a[b] = 1. + assert_equal(a, [[1., 1., 1.]]) + + def test_boolean_indexing_twodim(self): + # Indexing a 2-dimensional array with + # 2-dimensional boolean array + a = np.array([[1, 2, 3], + [4 ,5, 6], + [7, 8, 9]]) + b = np.array([[ True, False, True], + [False, True, False], + [ True, False, True]]) + assert_equal(a[b], [1, 3, 5, 7, 9]) + assert_equal(a[b[1]], [[4, 5, 6]]) + assert_equal(a[b[0]], a[b[2]]) + + # boolean assignment + a[b] = 0 + assert_equal(a, [[0, 2, 0], + [4, 0, 6], + [0, 8, 0]]) if __name__ == "__main__": run_module_suite() |