diff options
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/fromnumeric.py | 38 | ||||
-rw-r--r-- | numpy/core/include/numpy/ndarraytypes.h | 2 | ||||
-rw-r--r-- | numpy/core/src/multiarray/convert.c | 2 | ||||
-rw-r--r-- | numpy/core/src/multiarray/ctors.c | 67 | ||||
-rw-r--r-- | numpy/core/src/multiarray/new_iterator.c.src | 2 | ||||
-rw-r--r-- | numpy/core/src/multiarray/shape.c | 129 | ||||
-rw-r--r-- | numpy/core/src/multiarray/shape.h | 14 | ||||
-rw-r--r-- | numpy/core/tests/test_multiarray.py | 16 |
8 files changed, 198 insertions, 72 deletions
diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py index 76b74e223..d66e5cb68 100644 --- a/numpy/core/fromnumeric.py +++ b/numpy/core/fromnumeric.py @@ -1051,13 +1051,16 @@ def ravel(a, order='C'): Parameters ---------- a : array_like - Input array. The elements in `a` are read in the order specified by + Input array. The elements in ``a`` are read in the order specified by `order`, and packed as a 1-D array. - order : {'C','F', 'A'}, optional - The elements of `a` are read in this order. It can be - 'C' for row-major order, `F` for column-major order, or - 'A' to preserve the order of `a` when possible. - By default, row-major order is used. + order : {'C','F', 'A', 'K'}, optional + The elements of ``a`` are read in this order. 'C' means to view + the elements in C (row-major) order. 'F' means to view the elements + in Fortran (column-major) order. 'A' means to view the elements + in 'F' order if a is Fortran contiguous, 'C' order otherwise. + 'K' means to view the elements in the order they occur in memory, + except for reversing the data when strides are negative. + By default, 'C' order is used. Returns ------- @@ -1092,12 +1095,33 @@ def ravel(a, order='C'): >>> print np.ravel(x, order='F') [1 4 2 5 3 6] - When `order` is 'A', it will preserve the array's 'C' or 'F' ordering: + When ``order`` is 'A', it will preserve the array's 'C' or 'F' ordering: >>> print np.ravel(x.T) [1 4 2 5 3 6] >>> print np.ravel(x.T, order='A') [1 2 3 4 5 6] + + When ``order`` is 'K', it will preserve orderings that are neither 'C' + nor 'F', but won't reverse axes: + + >>> a = np.arange(3)[::-1]; a + array([2, 1, 0]) + >>> a.ravel(order='C') + array([2, 1, 0]) + >>> a.ravel(order='K') + array([2, 1, 0]) + + >>> a = np.arange(12).reshape(2,3,2).swapaxes(1,2); a + array([[[ 0, 2, 4], + [ 1, 3, 5]], + [[ 6, 8, 10], + [ 7, 9, 11]]]) + >>> a.ravel(order='C') + array([ 0, 2, 4, 1, 3, 5, 6, 8, 10, 7, 9, 11]) + >>> a.ravel(order='K') + array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) + """ return asarray(a).ravel(order) diff --git a/numpy/core/include/numpy/ndarraytypes.h b/numpy/core/include/numpy/ndarraytypes.h index 13d5650b5..4ca1b2cae 100644 --- a/numpy/core/include/numpy/ndarraytypes.h +++ b/numpy/core/include/numpy/ndarraytypes.h @@ -897,6 +897,8 @@ typedef void (*NpyIter_GetCoords_Fn )(NpyIter *iter, #define NPY_ITER_GROWINNER 0x00000400 /* Delay allocation of buffers until first Reset* call */ #define NPY_ITER_DELAY_BUFALLOC 0x00000800 +/* When NPY_KEEPORDER is specified, disable reversing negative-stride axes */ +#define NPY_ITER_DONT_REVERSE_AXES 0x00001000 /*** Per-operand flags that may be passed to the iterator constructors ***/ diff --git a/numpy/core/src/multiarray/convert.c b/numpy/core/src/multiarray/convert.c index 04bcdfc4d..f63204e65 100644 --- a/numpy/core/src/multiarray/convert.c +++ b/numpy/core/src/multiarray/convert.c @@ -431,7 +431,7 @@ PyArray_FillWithZero(PyArrayObject *a) NPY_NO_EXPORT PyObject * PyArray_NewCopy(PyArrayObject *m1, NPY_ORDER order) { - PyArrayObject *ret = PyArray_NewLikeArray(m1, order, NULL); + PyArrayObject *ret = (PyArrayObject *)PyArray_NewLikeArray(m1, order, NULL); if (ret == NULL) { return NULL; } diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c index 573e75198..495fe1fef 100644 --- a/numpy/core/src/multiarray/ctors.c +++ b/numpy/core/src/multiarray/ctors.c @@ -17,6 +17,8 @@ #include "ctors.h" +#include "shape.h" + #include "buffer.h" #include "numpymemoryview.h" @@ -1229,36 +1231,6 @@ PyArray_NewFromDescr(PyTypeObject *subtype, PyArray_Descr *descr, int nd, return NULL; } -typedef struct { - npy_intp perm, stride; -} _npy_stride_sort_item; - -/* - * Sorts items so stride is descending, because C-order - * is the default in the face of ambiguity. - */ -int _npy_stride_sort_item_comparator(const void *a, const void *b) -{ - npy_intp astride = ((_npy_stride_sort_item *)a)->stride, - bstride = ((_npy_stride_sort_item *)b)->stride; - - if (astride > bstride) { - return -1; - } - else if (astride == bstride) { - /* - * Make the qsort stable by next comparing the perm order. - * (Note that two perm entries will never be equal) - */ - npy_intp aperm = ((_npy_stride_sort_item *)a)->perm, - bperm = ((_npy_stride_sort_item *)b)->perm; - return (aperm < bperm) ? -1 : 1; - } - else { - return 1; - } -} - /*NUMPY_API * Creates a new array with the same shape as the provided one, * with possible memory layout order and data type changes. @@ -1320,26 +1292,15 @@ PyArray_NewLikeArray(PyArrayObject *prototype, NPY_ORDER order, else { npy_intp strides[NPY_MAXDIMS], stride; npy_intp *shape = PyArray_DIMS(prototype); - _npy_stride_sort_item sortstrides[NPY_MAXDIMS]; - int i, ndim = PyArray_NDIM(prototype); - - /* Set up the permutation and absolute value of strides */ - for (i = 0; i < ndim; ++i) { - sortstrides[i].perm = i; - sortstrides[i].stride = PyArray_STRIDE(prototype, i); - if (sortstrides[i].stride < 0) { - sortstrides[i].stride = -sortstrides[i].stride; - } - } + _npy_stride_sort_item strideperm[NPY_MAXDIMS]; + int i; - /* Sort them */ - qsort(sortstrides, ndim, sizeof(_npy_stride_sort_item), - &_npy_stride_sort_item_comparator); + PyArray_CreateSortedStridePerm(prototype, strideperm); /* Build the new strides */ stride = dtype->elsize; for (i = ndim-1; i >= 0; --i) { - npy_intp i_perm = sortstrides[i].perm; + npy_intp i_perm = strideperm[i].perm; strides[i_perm] = stride; stride *= shape[i_perm]; } @@ -2292,14 +2253,12 @@ PyArray_CopyAnyIntoOrdered(PyArrayObject *dst, PyArrayObject *src, return -1; } - if (!(order == NPY_CORDER || order == NPY_FORTRANORDER)) { - PyErr_SetString(PyExc_RuntimeError, - "CopyAnyIntoOrdered requires either C or Fortran order"); - return -1; - } - - /* If the shapes match, use the more efficient CopyInto */ - if (PyArray_NDIM(dst) == PyArray_NDIM(src) && + /* + * If the shapes match and a particular order is forced + * for both, use the more efficient CopyInto + */ + if (order != NPY_ANYORDER && order != NPY_KEEPORDER && + PyArray_NDIM(dst) == PyArray_NDIM(src) && PyArray_CompareLists(PyArray_DIMS(dst), PyArray_DIMS(src), PyArray_NDIM(dst))) { return PyArray_CopyInto(dst, src); @@ -2327,6 +2286,7 @@ PyArray_CopyAnyIntoOrdered(PyArrayObject *dst, PyArrayObject *src, */ dst_iter = NpyIter_New(dst, NPY_ITER_WRITEONLY| NPY_ITER_NO_INNER_ITERATION| + NPY_ITER_DONT_REVERSE_AXES| NPY_ITER_REFS_OK, order, NPY_NO_CASTING, @@ -2336,6 +2296,7 @@ PyArray_CopyAnyIntoOrdered(PyArrayObject *dst, PyArrayObject *src, } src_iter = NpyIter_New(src, NPY_ITER_READONLY| NPY_ITER_NO_INNER_ITERATION| + NPY_ITER_DONT_REVERSE_AXES| NPY_ITER_REFS_OK, order, NPY_NO_CASTING, diff --git a/numpy/core/src/multiarray/new_iterator.c.src b/numpy/core/src/multiarray/new_iterator.c.src index 018ef1a9f..89de0821b 100644 --- a/numpy/core/src/multiarray/new_iterator.c.src +++ b/numpy/core/src/multiarray/new_iterator.c.src @@ -508,7 +508,7 @@ NpyIter_MultiNew(npy_intp niter, PyArrayObject **op_in, npy_uint32 flags, * If there's an output being allocated, we must not negate * any strides. */ - if (!any_allocate) { + if (!any_allocate && !(flags&NPY_ITER_DONT_REVERSE_AXES)) { npyiter_flip_negative_strides(iter); } itflags = NIT_ITFLAGS(iter); diff --git a/numpy/core/src/multiarray/shape.c b/numpy/core/src/multiarray/shape.c index fcc3fd589..f42860f78 100644 --- a/numpy/core/src/multiarray/shape.c +++ b/numpy/core/src/multiarray/shape.c @@ -760,29 +760,137 @@ PyArray_Transpose(PyArrayObject *ap, PyArray_Dims *permute) return (PyObject *)ret; } +/* + * Sorts items so stride is descending, because C-order + * is the default in the face of ambiguity. + */ +int _npy_stride_sort_item_comparator(const void *a, const void *b) +{ + npy_intp astride = ((_npy_stride_sort_item *)a)->stride, + bstride = ((_npy_stride_sort_item *)b)->stride; + + /* Sort the absolute value of the strides */ + if (astride < 0) { + astride = -astride; + } + if (bstride < 0) { + bstride = -bstride; + } + + if (astride > bstride) { + return -1; + } + else if (astride == bstride) { + /* + * Make the qsort stable by next comparing the perm order. + * (Note that two perm entries will never be equal) + */ + npy_intp aperm = ((_npy_stride_sort_item *)a)->perm, + bperm = ((_npy_stride_sort_item *)b)->perm; + return (aperm < bperm) ? -1 : 1; + } + else { + return 1; + } +} + +/* + * This function populates the first PyArray_NDIM(arr) elements + * of strideperm with sorted descending by their absolute values. + * For example, the stride array (4, -2, 12) becomes + * [(2, 12), (0, 4), (1, -2)]. + */ +NPY_NO_EXPORT void +PyArray_CreateSortedStridePerm(PyArrayObject *arr, + _npy_stride_sort_item *strideperm) +{ + int i, ndim = PyArray_NDIM(arr); + + /* Set up the strideperm values */ + for (i = 0; i < ndim; ++i) { + strideperm[i].perm = i; + strideperm[i].stride = PyArray_STRIDE(arr, i); + } + + /* Sort them */ + qsort(strideperm, ndim, sizeof(_npy_stride_sort_item), + &_npy_stride_sort_item_comparator); +} + /*NUMPY_API * Ravel * Returns a contiguous array */ NPY_NO_EXPORT PyObject * -PyArray_Ravel(PyArrayObject *a, NPY_ORDER fortran) +PyArray_Ravel(PyArrayObject *a, NPY_ORDER order) { PyArray_Dims newdim = {NULL,1}; intp val[1] = {-1}; - if (fortran == PyArray_ANYORDER) { - fortran = PyArray_ISFORTRAN(a); - } newdim.ptr = val; - if (!fortran && PyArray_ISCONTIGUOUS(a)) { - return PyArray_Newshape(a, &newdim, PyArray_CORDER); + + if (order == NPY_ANYORDER) { + order = PyArray_ISFORTRAN(a) ? NPY_FORTRANORDER : NPY_CORDER; } - else if (fortran && PyArray_ISFORTRAN(a)) { - return PyArray_Newshape(a, &newdim, PyArray_FORTRANORDER); + else if (order == NPY_KEEPORDER) { + if (PyArray_IS_C_CONTIGUOUS(a)) { + order = NPY_CORDER; + } + else if (PyArray_IS_F_CONTIGUOUS(a)) { + order = NPY_FORTRANORDER; + } } - else { - return PyArray_Flatten(a, fortran); + + if (order == NPY_CORDER && PyArray_ISCONTIGUOUS(a)) { + return PyArray_Newshape(a, &newdim, NPY_CORDER); } + else if (order == NPY_FORTRANORDER && PyArray_ISFORTRAN(a)) { + return PyArray_Newshape(a, &newdim, NPY_FORTRANORDER); + } + /* For KEEPORDER, check if we can make a flattened view */ + else if (order == NPY_KEEPORDER) { + _npy_stride_sort_item strideperm[NPY_MAXDIMS]; + npy_intp stride; + int i, ndim = PyArray_NDIM(a); + + PyArray_CreateSortedStridePerm(a, strideperm); + + stride = PyArray_DESCR(a)->elsize; + for (i = ndim-1; i >= 0; --i) { + if (strideperm[i].stride != stride) { + break; + } + stride *= PyArray_DIM(a, strideperm[i].perm); + } + + /* If all the strides matched a contiguous layout, return a view */ + if (i < 0) { + PyObject *ret; + npy_intp stride = PyArray_DESCR(a)->elsize; + + val[0] = PyArray_SIZE(a); + + Py_INCREF(PyArray_DESCR(a)); + ret = PyArray_NewFromDescr(Py_TYPE(a), + PyArray_DESCR(a), + 1, val, + &stride, + PyArray_BYTES(a), + PyArray_FLAGS(a), + (PyObject *)a); + + if (ret != NULL) { + PyArray_UpdateFlags((PyArrayObject *)ret, + NPY_CONTIGUOUS|NPY_FORTRAN); + Py_INCREF(a); + PyArray_BASE(ret) = (PyObject *)a; + } + return ret; + } + + } + + return PyArray_Flatten(a, order); } /*NUMPY_API @@ -797,6 +905,7 @@ PyArray_Flatten(PyArrayObject *a, NPY_ORDER order) if (order == NPY_ANYORDER) { order = PyArray_ISFORTRAN(a) ? NPY_FORTRANORDER : NPY_CORDER; } + size = PyArray_SIZE(a); Py_INCREF(a->descr); ret = (PyArrayObject *)PyArray_NewFromDescr(Py_TYPE(a), diff --git a/numpy/core/src/multiarray/shape.h b/numpy/core/src/multiarray/shape.h index 1a5991a50..8038a9f25 100644 --- a/numpy/core/src/multiarray/shape.h +++ b/numpy/core/src/multiarray/shape.h @@ -1,4 +1,18 @@ #ifndef _NPY_ARRAY_SHAPE_H_ #define _NPY_ARRAY_SHAPE_H_ +typedef struct { + npy_intp perm, stride; +} _npy_stride_sort_item; + +/* + * This function populates the first PyArray_NDIM(arr) elements + * of strideperm with sorted descending by their absolute values. + * For example, the stride array (4, -2, 12) becomes + * [(2, 12), (0, 4), (1, -2)]. + */ +NPY_NO_EXPORT void +PyArray_CreateSortedStridePerm(PyArrayObject *arr, + _npy_stride_sort_item *strideperm); + #endif diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index 1c5e84537..21bd44299 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -667,18 +667,34 @@ class TestMethods(TestCase): def test_ravel(self): a = np.array([[0,1],[2,3]]) assert_equal(a.ravel(), [0,1,2,3]) + assert_(not a.ravel().flags.owndata) assert_equal(a.ravel('F'), [0,2,1,3]) assert_equal(a.ravel(order='C'), [0,1,2,3]) assert_equal(a.ravel(order='F'), [0,2,1,3]) assert_equal(a.ravel(order='A'), [0,1,2,3]) + assert_(not a.ravel(order='A').flags.owndata) + assert_equal(a.ravel(order='K'), [0,1,2,3]) + assert_(not a.ravel(order='K').flags.owndata) assert_equal(a.ravel(), a.reshape(-1)) a = np.array([[0,1],[2,3]], order='F') assert_equal(a.ravel(), [0,1,2,3]) assert_equal(a.ravel(order='A'), [0,2,1,3]) + assert_equal(a.ravel(order='K'), [0,2,1,3]) + assert_(not a.ravel(order='A').flags.owndata) + assert_(not a.ravel(order='K').flags.owndata) assert_equal(a.ravel(), a.reshape(-1)) assert_equal(a.ravel(order='A'), a.reshape(-1, order='A')) + a = np.array([[0,1],[2,3]])[::-1,:] + assert_equal(a.ravel(), [2,3,0,1]) + assert_equal(a.ravel(order='C'), [2,3,0,1]) + assert_equal(a.ravel(order='F'), [2,0,3,1]) + assert_equal(a.ravel(order='A'), [2,3,0,1]) + # 'K' doesn't reverse the axes of negative strides + assert_equal(a.ravel(order='K'), [2,3,0,1]) + assert_(a.ravel(order='K').flags.owndata) + def test_setasflat(self): # In this case, setasflat can treat a as a flat array, # and must treat b in chunks of 3 |