summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/fromnumeric.py38
-rw-r--r--numpy/core/include/numpy/ndarraytypes.h2
-rw-r--r--numpy/core/src/multiarray/convert.c2
-rw-r--r--numpy/core/src/multiarray/ctors.c67
-rw-r--r--numpy/core/src/multiarray/new_iterator.c.src2
-rw-r--r--numpy/core/src/multiarray/shape.c129
-rw-r--r--numpy/core/src/multiarray/shape.h14
-rw-r--r--numpy/core/tests/test_multiarray.py16
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