summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Wiebe <mwwiebe@gmail.com>2010-12-21 10:54:10 -0800
committerMark Wiebe <mwwiebe@gmail.com>2011-01-09 01:55:00 -0800
commitf36566682043e9b2a27f3171c1fc94e63977d494 (patch)
treeab7ed943d2652781f4b6aacabcde1fc27bb8461f
parent516a38d1090ff3504a60da3da9205bd1fe0ff0d9 (diff)
downloadnumpy-f36566682043e9b2a27f3171c1fc94e63977d494.tar.gz
ENH: iter: Move forcing the iterator order into an order= parameter
-rw-r--r--numpy/core/include/numpy/ndarraytypes.h3
-rw-r--r--numpy/core/src/multiarray/new_iterator.c.src86
-rw-r--r--numpy/core/src/multiarray/new_iterator.h22
-rw-r--r--numpy/core/src/multiarray/new_iterator_pywrap.c71
-rw-r--r--numpy/core/tests/test_new_iterator.py77
5 files changed, 128 insertions, 131 deletions
diff --git a/numpy/core/include/numpy/ndarraytypes.h b/numpy/core/include/numpy/ndarraytypes.h
index 98d02287a..a0ae16990 100644
--- a/numpy/core/include/numpy/ndarraytypes.h
+++ b/numpy/core/include/numpy/ndarraytypes.h
@@ -177,7 +177,8 @@ typedef enum {
typedef enum {
NPY_ANYORDER=-1,
NPY_CORDER=0,
- NPY_FORTRANORDER=1
+ NPY_FORTRANORDER=1,
+ NPY_KEEPORDER=2
} NPY_ORDER;
diff --git a/numpy/core/src/multiarray/new_iterator.c.src b/numpy/core/src/multiarray/new_iterator.c.src
index 91dba7bdd..e853d9df0 100644
--- a/numpy/core/src/multiarray/new_iterator.c.src
+++ b/numpy/core/src/multiarray/new_iterator.c.src
@@ -203,7 +203,7 @@ npyiter_replace_axisdata(NpyIter *iter, npy_intp iiter,
static void
npyiter_compute_index_strides(NpyIter *iter, npy_uint32 flags);
static void
-npyiter_apply_forced_iteration_order(npy_uint32 flags, NpyIter *iter);
+npyiter_apply_forced_iteration_order(NpyIter *iter, NPY_ORDER order);
static void
npyiter_flip_negative_strides(NpyIter *iter);
@@ -240,7 +240,8 @@ npyiter_copy_to_buffers(NpyIter *iter);
/* The constructor for an iterator over multiple objects */
NpyIter*
NpyIter_MultiNew(npy_intp niter, PyArrayObject **op_in, npy_uint32 flags,
- npy_uint32 *op_flags, PyArray_Descr **op_request_dtypes,
+ NPY_ORDER order, npy_uint32 *op_flags,
+ PyArray_Descr **op_request_dtypes,
npy_intp oa_ndim, npy_intp **op_axes, npy_intp buffersize)
{
npy_uint32 itflags = NPY_ITFLAG_IDENTPERM;
@@ -528,7 +529,7 @@ NpyIter_MultiNew(npy_intp niter, PyArrayObject **op_in, npy_uint32 flags,
/*
* If an iteration order is being forced, apply it.
*/
- npyiter_apply_forced_iteration_order(flags, iter);
+ npyiter_apply_forced_iteration_order(iter, order);
itflags = NIT_ITFLAGS(iter);
/*
@@ -846,7 +847,8 @@ NpyIter_MultiNew(npy_intp niter, PyArrayObject **op_in, npy_uint32 flags,
/* The constructor for an iterator over one object */
NpyIter*
-NpyIter_New(PyArrayObject *op, npy_uint32 flags, PyArray_Descr* dtype,
+NpyIter_New(PyArrayObject *op, npy_uint32 flags,
+ NPY_ORDER order, PyArray_Descr* dtype,
npy_intp a_ndim, npy_intp *axes, npy_intp buffersize)
{
/* Split the flags into separate global and op flags */
@@ -854,11 +856,11 @@ NpyIter_New(PyArrayObject *op, npy_uint32 flags, PyArray_Descr* dtype,
flags &= NPY_ITER_GLOBAL_FLAGS;
if (a_ndim > 0) {
- return NpyIter_MultiNew(1, &op, flags, &op_flags, &dtype,
+ return NpyIter_MultiNew(1, &op, flags, order, &op_flags, &dtype,
a_ndim, &axes, buffersize);
}
else {
- return NpyIter_MultiNew(1, &op, flags, &op_flags, &dtype,
+ return NpyIter_MultiNew(1, &op, flags, order, &op_flags, &dtype,
0, NULL, buffersize);
}
}
@@ -1867,24 +1869,6 @@ pyiter_check_global_flags(npy_uint32 flags, npy_uint32* itflags)
*/
(*itflags) |= NPY_ITFLAG_HASCOORDS;
}
- /* Check that at most one forced ordering is specified */
- if (flags&(NPY_ITER_FORCE_C_ORDER|
- NPY_ITER_FORCE_F_ORDER|
- NPY_ITER_FORCE_C_OR_F_ORDER)) {
- if (((flags&NPY_ITER_FORCE_C_ORDER) &&
- (flags&(NPY_ITER_FORCE_F_ORDER|
- NPY_ITER_FORCE_C_OR_F_ORDER))) ||
- ((flags&(NPY_ITER_FORCE_F_ORDER|
- NPY_ITER_FORCE_C_OR_F_ORDER)) ==
- (NPY_ITER_FORCE_F_ORDER|
- NPY_ITER_FORCE_C_OR_F_ORDER))) {
- PyErr_SetString(PyExc_ValueError,
- "Only one of the iterator flags FORCE_C_ORDER, "
- "FORCE_F_ORDER, and "
- "FORCE_C_OR_F_ORDER may be specified");
- return 0;
- }
- }
/* Check if offsets were requested instead of pointers */
if (flags&NPY_ITER_OFFSETS) {
/* Buffering is incompatible with offsets */
@@ -2613,43 +2597,51 @@ npyiter_compute_index_strides(NpyIter *iter, npy_uint32 flags)
}
/*
- * If the flags specify a forced iteration order, applies it
- * to the iterator, and indicates in its itflags that the iteration
- * order was forced.
+ * If the order is NPY_KEEPORDER, lets the iterator find the best
+ * iteration order, otherwise forces it. Indicates in the itflags that
+ * whether the iteration order was forced.
*/
static void
-npyiter_apply_forced_iteration_order(npy_uint32 flags, NpyIter *iter)
+npyiter_apply_forced_iteration_order(NpyIter *iter, NPY_ORDER order)
{
/*npy_uint32 itflags = NIT_ITFLAGS(iter);*/
npy_intp ndim = NIT_NDIM(iter);
npy_intp iiter, niter = NIT_NITER(iter);
- if (flags&(NPY_ITER_FORCE_C_ORDER|
- NPY_ITER_FORCE_F_ORDER|
- NPY_ITER_FORCE_C_OR_F_ORDER)) {
+ switch (order) {
+ case NPY_CORDER:
+ NIT_ITFLAGS(iter) |= NPY_ITFLAG_FORCEDORDER;
+ break;
+ case NPY_FORTRANORDER:
NIT_ITFLAGS(iter) |= NPY_ITFLAG_FORCEDORDER;
/* Only need to actually do something if there is more than 1 dim */
if (ndim > 1) {
- if (flags&NPY_ITER_FORCE_F_ORDER) {
- npyiter_reverse_axis_ordering(iter);
- }
- else if(flags&NPY_ITER_FORCE_C_OR_F_ORDER) {
- PyArrayObject **op = NIT_OBJECTS(iter);
- int forder = 1;
-
- /* Check that all the array inputs are fortran order */
- for (iiter = 0; iiter < niter; ++iiter, ++op) {
- if (*op && !PyArray_CHKFLAGS(*op, NPY_F_CONTIGUOUS)) {
- forder = 0;
- break;
- }
+ npyiter_reverse_axis_ordering(iter);
+ }
+ break;
+ case NPY_ANYORDER:
+ NIT_ITFLAGS(iter) |= NPY_ITFLAG_FORCEDORDER;
+ /* Only need to actually do something if there is more than 1 dim */
+ if (ndim > 1) {
+ PyArrayObject **op = NIT_OBJECTS(iter);
+ int forder = 1;
+
+ /* Check that all the array inputs are fortran order */
+ for (iiter = 0; iiter < niter; ++iiter, ++op) {
+ if (*op && !PyArray_CHKFLAGS(*op, NPY_F_CONTIGUOUS)) {
+ forder = 0;
+ break;
}
+ }
- if (forder) {
- npyiter_reverse_axis_ordering(iter);
- }
+ if (forder) {
+ npyiter_reverse_axis_ordering(iter);
}
}
+ break;
+ case NPY_KEEPORDER:
+ /* Don't set the forced order flag here... */
+ break;
}
}
diff --git a/numpy/core/src/multiarray/new_iterator.h b/numpy/core/src/multiarray/new_iterator.h
index 1c79d57f4..1e551952d 100644
--- a/numpy/core/src/multiarray/new_iterator.h
+++ b/numpy/core/src/multiarray/new_iterator.h
@@ -15,13 +15,15 @@ typedef void (*NpyIter_GetCoords_Fn )(NpyIter *iter,
/* Allocate a new iterator over one array object */
NpyIter*
-NpyIter_New(PyArrayObject* op, npy_uint32 flags, PyArray_Descr* dtype,
+NpyIter_New(PyArrayObject* op, npy_uint32 flags,
+ NPY_ORDER order, PyArray_Descr* dtype,
npy_intp a_ndim, npy_intp *axes, npy_intp buffersize);
/* Allocate a new iterator over multiple array objects */
NpyIter*
NpyIter_MultiNew(npy_intp niter, PyArrayObject **op_in, npy_uint32 flags,
- npy_uint32 *op_flags, PyArray_Descr **op_request_dtypes,
+ NPY_ORDER order, npy_uint32 *op_flags,
+ PyArray_Descr **op_request_dtypes,
npy_intp oa_ndim, npy_intp **op_axes, npy_intp buffersize);
/* Removes coords support from an iterator */
@@ -95,22 +97,16 @@ NPY_NO_EXPORT void NpyIter_DebugPrint(NpyIter *iter);
#define NPY_ITER_F_ORDER_INDEX 0x00000002
/* Track coordinates */
#define NPY_ITER_COORDS 0x00000004
-/* Iterate in C order */
-#define NPY_ITER_FORCE_C_ORDER 0x00000008
-/* Iterate in F order */
-#define NPY_ITER_FORCE_F_ORDER 0x00000010
-/* Iterate in F order if all operands are F contiguous, C order otherwise */
-#define NPY_ITER_FORCE_C_OR_F_ORDER 0x00000020
/* Let the caller handle the inner loop of iteration */
-#define NPY_ITER_NO_INNER_ITERATION 0x00000040
+#define NPY_ITER_NO_INNER_ITERATION 0x00000008
/* Convert all the operands to a common data type */
-#define NPY_ITER_COMMON_DATA_TYPE 0x00000080
+#define NPY_ITER_COMMON_DATA_TYPE 0x00000010
/* Produce offsets instead of pointers into the data */
-#define NPY_ITER_OFFSETS 0x00000100
+#define NPY_ITER_OFFSETS 0x00000020
/* Enables buffering */
-#define NPY_ITER_BUFFERED 0x00000200
+#define NPY_ITER_BUFFERED 0x00000040
/* Enables buffering, and grows the inner loop when possible */
-#define NPY_ITER_BUFFERED_GROWINNER 0x00000400
+#define NPY_ITER_BUFFERED_GROWINNER 0x00000080
/*** Per-operand flags that may be passed to the iterator constructors ***/
diff --git a/numpy/core/src/multiarray/new_iterator_pywrap.c b/numpy/core/src/multiarray/new_iterator_pywrap.c
index cf2253754..e37b95417 100644
--- a/numpy/core/src/multiarray/new_iterator_pywrap.c
+++ b/numpy/core/src/multiarray/new_iterator_pywrap.c
@@ -79,9 +79,9 @@ static int
npyiter_init(NewNpyArrayIterObject *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"op", "flags", "op_flags", "op_dtypes",
- "op_axes", "buffersize"};
+ "order", "op_axes", "buffersize"};
- PyObject *op_in, *flags_in = NULL, *op_flags_in = NULL,
+ PyObject *op_in, *flags_in = NULL, *order_in = NULL, *op_flags_in = NULL,
*op_dtypes_in = NULL, *op_axes_in = NULL;
npy_intp iiter, buffersize = 0;
@@ -89,6 +89,7 @@ npyiter_init(NewNpyArrayIterObject *self, PyObject *args, PyObject *kwds)
npy_intp niter = 0;
PyArrayObject *op[NPY_MAXARGS];
npy_uint32 flags = 0;
+ NPY_ORDER order = NPY_KEEPORDER;
npy_uint32 op_flags[NPY_MAXARGS];
PyArray_Descr *op_request_dtypes[NPY_MAXARGS];
npy_intp oa_ndim = 0;
@@ -101,9 +102,9 @@ npyiter_init(NewNpyArrayIterObject *self, PyObject *args, PyObject *kwds)
return -1;
}
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOOOi", kwlist,
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOOOOi", kwlist,
&op_in, &flags_in, &op_flags_in, &op_dtypes_in,
- &op_axes_in, &buffersize)) {
+ &order_in, &op_axes_in, &buffersize)) {
return -1;
}
@@ -145,10 +146,7 @@ npyiter_init(NewNpyArrayIterObject *self, PyObject *args, PyObject *kwds)
}
/* flags */
if (flags_in == NULL) {
- /* Default to behaving similarly to the old iterator */
- flags = NPY_ITER_C_ORDER_INDEX |
- NPY_ITER_COORDS |
- NPY_ITER_FORCE_C_ORDER;
+ flags = 0;
}
else if (PyTuple_Check(flags_in) || PyList_Check(flags_in)) {
int iflags, nflags = PySequence_Size(flags_in);
@@ -198,26 +196,8 @@ npyiter_init(NewNpyArrayIterObject *self, PyObject *args, PyObject *kwds)
}
break;
case 'f':
- if (length >= 7) switch (str[6]) {
- case 'r':
- if (strcmp(str, "f_order_index") == 0) {
- flag = NPY_ITER_F_ORDER_INDEX;
- }
- break;
- case 'c':
- if (strcmp(str, "force_c_order") == 0) {
- flag = NPY_ITER_FORCE_C_ORDER;
- }
- else if (strcmp(str, "force_c_or_f_order") == 0) {
- flag = NPY_ITER_FORCE_C_OR_F_ORDER;
- }
- break;
- case 'f':
- if (strcmp(str, "force_f_order") == 0) {
- flag = NPY_ITER_FORCE_F_ORDER;
- }
- break;
- break;
+ if (strcmp(str, "f_order_index") == 0) {
+ flag = NPY_ITER_F_ORDER_INDEX;
}
break;
case 'n':
@@ -248,6 +228,39 @@ npyiter_init(NewNpyArrayIterObject *self, PyObject *args, PyObject *kwds)
"Parameter 2 must be a tuple of flags");
goto fail;
}
+ /* order */
+ if (order_in != NULL) {
+ char *str = NULL;
+ Py_ssize_t length = 0;
+
+ if (PyString_AsStringAndSize(order_in, &str, &length) == -1) {
+ goto fail;
+ }
+
+ if (length != 1) {
+ PyErr_SetString(PyExc_ValueError,
+ "order must be 'C', 'F', 'A', or 'K'");
+ goto fail;
+ }
+ switch (str[0]) {
+ case 'C':
+ order = NPY_CORDER;
+ break;
+ case 'F':
+ order = NPY_FORTRANORDER;
+ break;
+ case 'A':
+ order = NPY_ANYORDER;
+ break;
+ case 'K':
+ order = NPY_KEEPORDER;
+ break;
+ default:
+ PyErr_SetString(PyExc_ValueError,
+ "order must be 'C', 'F', 'A', or 'K'");
+ goto fail;
+ }
+ }
/* op_flags */
if (op_flags_in == NULL) {
for (iiter = 0; iiter < niter; ++iiter) {
@@ -540,7 +553,7 @@ npyiter_init(NewNpyArrayIterObject *self, PyObject *args, PyObject *kwds)
}
}
- self->iter = NpyIter_MultiNew(niter, op, flags, op_flags,
+ self->iter = NpyIter_MultiNew(niter, op, flags, order, op_flags,
(PyArray_Descr**)op_request_dtypes,
oa_ndim, oa_ndim > 0 ? op_axes : NULL,
buffersize);
diff --git a/numpy/core/tests/test_new_iterator.py b/numpy/core/tests/test_new_iterator.py
index 076c9e8a6..50596fd73 100644
--- a/numpy/core/tests/test_new_iterator.py
+++ b/numpy/core/tests/test_new_iterator.py
@@ -78,15 +78,14 @@ def test_iter_c_order():
aview = a.reshape(shape)[dirs_index]
# C-order
- i = newiter(aview, ['force_c_order'], [['readonly']])
+ i = newiter(aview, order='C')
assert_equal([x for x in i], aview.ravel(order='C'))
# Fortran-order
- i = newiter(aview.T, ['force_c_order'], [['readonly']])
+ i = newiter(aview.T, order='C')
assert_equal([x for x in i], aview.T.ravel(order='C'))
# Other order
if len(shape) > 2:
- i = newiter(aview.swapaxes(0,1),
- ['force_c_order'], [['readonly']])
+ i = newiter(aview.swapaxes(0,1), order='C')
assert_equal([x for x in i],
aview.swapaxes(0,1).ravel(order='C'))
@@ -106,15 +105,14 @@ def test_iter_f_order():
aview = a.reshape(shape)[dirs_index]
# C-order
- i = newiter(aview, ['force_f_order'], [['readonly']])
+ i = newiter(aview, order='F')
assert_equal([x for x in i], aview.ravel(order='F'))
# Fortran-order
- i = newiter(aview.T, ['force_f_order'], [['readonly']])
+ i = newiter(aview.T, order='F')
assert_equal([x for x in i], aview.T.ravel(order='F'))
# Other order
if len(shape) > 2:
- i = newiter(aview.swapaxes(0,1),
- ['force_f_order'], [['readonly']])
+ i = newiter(aview.swapaxes(0,1), order='F')
assert_equal([x for x in i],
aview.swapaxes(0,1).ravel(order='F'))
@@ -134,15 +132,14 @@ def test_iter_c_or_f_order():
aview = a.reshape(shape)[dirs_index]
# C-order
- i = newiter(aview, ['force_c_or_f_order'], [['readonly']])
+ i = newiter(aview, order='A')
assert_equal([x for x in i], aview.ravel(order='A'))
# Fortran-order
- i = newiter(aview.T, ['force_c_or_f_order'], [['readonly']])
+ i = newiter(aview.T, order='A')
assert_equal([x for x in i], aview.T.ravel(order='A'))
# Other order
if len(shape) > 2:
- i = newiter(aview.swapaxes(0,1),
- ['force_c_or_f_order'], [['readonly']])
+ i = newiter(aview.swapaxes(0,1), order='A')
assert_equal([x for x in i],
aview.swapaxes(0,1).ravel(order='A'))
@@ -458,17 +455,17 @@ def test_iter_dim_coalescing():
# When C or F order is forced, coalescing may still occur
a3d = arange(24).reshape(2,3,4)
- i = newiter(a3d, ['force_c_order'], [['readonly']])
+ i = newiter(a3d, order='C')
assert_equal(i.ndim, 1)
- i = newiter(a3d.T, ['force_c_order'], [['readonly']])
+ i = newiter(a3d.T, order='C')
assert_equal(i.ndim, 3)
- i = newiter(a3d, ['force_f_order'], [['readonly']])
+ i = newiter(a3d, order='F')
assert_equal(i.ndim, 3)
- i = newiter(a3d.T, ['force_f_order'], [['readonly']])
+ i = newiter(a3d.T, order='F')
assert_equal(i.ndim, 1)
- i = newiter(a3d, ['force_c_or_f_order'], [['readonly']])
+ i = newiter(a3d, order='A')
assert_equal(i.ndim, 1)
- i = newiter(a3d.T, ['force_c_or_f_order'], [['readonly']])
+ i = newiter(a3d.T, order='A')
assert_equal(i.ndim, 1)
def test_iter_broadcasting():
@@ -578,16 +575,6 @@ def test_iter_flags_errors():
assert_raises(ValueError, newiter, [a]*100, [], [['readonly']]*100)
# op_flags must match ops
assert_raises(ValueError, newiter, [a]*3, [], [['readonly']]*2)
- # Can only force one order
- assert_raises(ValueError, newiter, a,
- ['force_c_order','force_f_order'], [['readonly']])
- assert_raises(ValueError, newiter, a,
- ['force_c_order','force_c_or_f_order'], [['readonly']])
- assert_raises(ValueError, newiter, a,
- ['force_f_order','force_c_or_f_order'], [['readonly']])
- assert_raises(ValueError, newiter, a,
- ['force_c_order','force_f_order','force_c_or_f_order'],
- [['readonly']])
# Cannot track both a C and an F index
assert_raises(ValueError, newiter, a,
['c_order_index','f_order_index'], [['readonly']])
@@ -954,8 +941,9 @@ def test_iter_allocate_output_itorder():
assert_equal(i.operands[1].dtype, np.dtype('f4'))
# Non-contiguous input, C iteration order
a = arange(24, dtype='i4').reshape(2,3,4).swapaxes(0,1)
- i = newiter([a,None], ['force_c_order'],
+ i = newiter([a,None], [],
[['readonly'],['writeonly','allocate']],
+ order='C',
op_dtypes=[None,np.dtype('f4')])
assert_equal(i.operands[1].shape, a.shape)
assert_equal(i.operands[1].strides, (32,16,4))
@@ -1109,8 +1097,9 @@ def test_iter_buffering():
for a in arrays:
for buffersize in (1,2,3,5,8,11,16,1024):
vals = []
- i = np.newiter(a, ['buffered','no_inner_iteration','force_c_order'],
+ i = np.newiter(a, ['buffered','no_inner_iteration'],
[['readonly','nbo_aligned']],
+ order='C',
buffersize=buffersize)
while not i.finished:
assert_(i[0].size <= buffersize)
@@ -1123,8 +1112,9 @@ def test_iter_write_buffering():
# F-order swapped array
a = np.arange(24).reshape(2,3,4).T.newbyteorder().byteswap()
- i = np.newiter(a, ['buffered','force_c_order'],
+ i = np.newiter(a, ['buffered'],
[['readwrite','nbo_aligned']],
+ order='C',
buffersize=16)
x = 0
while not i.finished:
@@ -1218,27 +1208,32 @@ def test_iter_buffering_badwriteback():
a = np.arange(6).reshape(2,3,1)
b = np.arange(12).reshape(2,3,2)
assert_raises(ValueError,np.newiter,[a,b],
- ['buffered','no_inner_iteration','force_c_order'],
- [['readwrite'],['writeonly']])
+ ['buffered','no_inner_iteration'],
+ [['readwrite'],['writeonly']],
+ order='C')
# But if a is readonly, it's fine
- i = np.newiter([a,b],['buffered','no_inner_iteration','force_c_order'],
- [['readonly'],['writeonly']])
+ i = np.newiter([a,b],['buffered','no_inner_iteration'],
+ [['readonly'],['writeonly']],
+ order='C')
# If a has just one element, it's fine too (constant 0 stride)
a = np.arange(1).reshape(1,1,1)
- i = np.newiter([a,b],['buffered','no_inner_iteration','force_c_order'],
- [['readwrite'],['writeonly']])
+ i = np.newiter([a,b],['buffered','no_inner_iteration'],
+ [['readwrite'],['writeonly']],
+ order='C')
# check that it fails on other dimensions too
a = np.arange(6).reshape(1,3,2)
assert_raises(ValueError,np.newiter,[a,b],
- ['buffered','no_inner_iteration','force_c_order'],
- [['readwrite'],['writeonly']])
+ ['buffered','no_inner_iteration'],
+ [['readwrite'],['writeonly']],
+ order='C')
a = np.arange(4).reshape(2,1,2)
assert_raises(ValueError,np.newiter,[a,b],
- ['buffered','no_inner_iteration','force_c_order'],
- [['readwrite'],['writeonly']])
+ ['buffered','no_inner_iteration'],
+ [['readwrite'],['writeonly']],
+ order='C')
def test_iter_buffering_growinner():
# Test that the inner loop grows when no buffering is needed