diff options
author | Mark Wiebe <mwwiebe@gmail.com> | 2010-12-21 10:54:10 -0800 |
---|---|---|
committer | Mark Wiebe <mwwiebe@gmail.com> | 2011-01-09 01:55:00 -0800 |
commit | f36566682043e9b2a27f3171c1fc94e63977d494 (patch) | |
tree | ab7ed943d2652781f4b6aacabcde1fc27bb8461f | |
parent | 516a38d1090ff3504a60da3da9205bd1fe0ff0d9 (diff) | |
download | numpy-f36566682043e9b2a27f3171c1fc94e63977d494.tar.gz |
ENH: iter: Move forcing the iterator order into an order= parameter
-rw-r--r-- | numpy/core/include/numpy/ndarraytypes.h | 3 | ||||
-rw-r--r-- | numpy/core/src/multiarray/new_iterator.c.src | 86 | ||||
-rw-r--r-- | numpy/core/src/multiarray/new_iterator.h | 22 | ||||
-rw-r--r-- | numpy/core/src/multiarray/new_iterator_pywrap.c | 71 | ||||
-rw-r--r-- | numpy/core/tests/test_new_iterator.py | 77 |
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 |