diff options
-rw-r--r-- | numpy/core/src/multiarray/na_mask.c | 36 | ||||
-rw-r--r-- | numpy/core/src/umath/ufunc_object.c | 627 |
2 files changed, 447 insertions, 216 deletions
diff --git a/numpy/core/src/multiarray/na_mask.c b/numpy/core/src/multiarray/na_mask.c index 6406dd82e..fdaaa6dab 100644 --- a/numpy/core/src/multiarray/na_mask.c +++ b/numpy/core/src/multiarray/na_mask.c @@ -498,11 +498,11 @@ PyArray_ReduceMaskArray(int ndim, npy_intp *shape, * by making it the first operand here */ if (PyArray_PrepareTwoRawArrayIter(ndim, shape, - src, src_strides, - dst, dst_strides, + src_data, src_strides, + dst_data, dst_strides, &ndim, shape_it, - &src, src_strides_it, - &dst, dst_strides_it) < 0) { + &src_data, src_strides_it, + &dst_data, dst_strides_it) < 0) { return NPY_FAIL; } @@ -512,40 +512,40 @@ PyArray_ReduceMaskArray(int ndim, npy_intp *shape, if (src_strides_it[0] == 1) { NPY_RAW_ITER_START(idim, ndim, coord, shape_it) { /* If there's a zero in src, set dst to zero */ - if (memchr(src, 0, shape_it[0]) != NULL) { - *dst = 0; + if (memchr(src_data, 0, shape_it[0]) != NULL) { + *dst_data = 0; } } NPY_RAW_ITER_TWO_NEXT(idim, ndim, coord, shape_it, - src, src_strides_it, - dst, dst_strides_it); + src_data, src_strides_it, + dst_data, dst_strides_it); } else { NPY_RAW_ITER_START(idim, ndim, coord, shape_it) { - char *src_d = src; + char *src_d = src_data; /* If there's a zero in src, set dst to zero */ for (i = 0; i < shape_it[0]; ++i) { if (*src_d == 0) { - *dst = 0; + *dst_data = 0; break; } - src_d += src_strides_it[0] + src_d += src_strides_it[0]; } } NPY_RAW_ITER_TWO_NEXT(idim, ndim, coord, shape_it, - src, src_strides_it, - dst, dst_strides_it); + src_data, src_strides_it, + dst_data, dst_strides_it); } } else { NPY_RAW_ITER_START(idim, ndim, coord, shape_it) { - char *src_d = src, dst_d = dst; + char *src_d = src_data, *dst_d = dst_data; for (i = 0; i < shape_it[0]; ++i) { *dst_d &= *src_d; - src_d += src_strides_it[0] - dst_d += dst_strides_it[0] + src_d += src_strides_it[0]; + dst_d += dst_strides_it[0]; } } NPY_RAW_ITER_TWO_NEXT(idim, ndim, coord, shape_it, - src, src_strides_it, - dst, dst_strides_it); + src_data, src_strides_it, + dst_data, dst_strides_it); } return 0; diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c index eb1c231d9..4cc9d7209 100644 --- a/numpy/core/src/umath/ufunc_object.c +++ b/numpy/core/src/umath/ufunc_object.c @@ -2448,10 +2448,8 @@ get_binary_op_function(PyUFuncObject *self, int *otype, * */ static PyObject * -PyUFunc_ReductionOp(PyUFuncObject *self, PyArrayObject *arr, - PyArrayObject *out, - int axis, int otype, int skipna, - int operation, char *opname) +PyUFunc_Reduce(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out, + int axis, int otype, int skipna) { PyArrayObject *op[2]; PyArray_Descr *op_dtypes[2] = {NULL, NULL}; @@ -2475,28 +2473,21 @@ PyUFunc_ReductionOp(PyUFuncObject *self, PyArrayObject *arr, NPY_BEGIN_THREADS_DEF; - NPY_UF_DBG_PRINT2("\nEvaluating ufunc %s.%s\n", ufunc_name, opname); + NPY_UF_DBG_PRINT1("\nEvaluating ufunc %s.reduce\n", ufunc_name); #if 0 - printf("Doing %s.%s on array with dtype : ", ufunc_name, opname); + printf("Doing %s.reduce on array with dtype : ", ufunc_name); PyObject_Print((PyObject *)PyArray_DESCR(arr), stdout, 0); printf("\n"); #endif use_maskna = PyArray_HASMASKNA(arr); - if (use_maskna) { - if (operation == UFUNC_ACCUMULATE) { - PyErr_SetString(PyExc_RuntimeError, - "ufunc accumulate doesn't support NA masked arrays yet"); - return NULL; - } - } /* If there's no NA mask, there are no NAs to skip */ - else { + if (!use_maskna) { skipna = 0; } - if (PyUFunc_GetPyValues(opname, &buffersize, &errormask, &errobj) < 0) { + if (PyUFunc_GetPyValues("reduce", &buffersize, &errormask, &errobj) < 0) { return NULL; } @@ -2508,9 +2499,9 @@ PyUFunc_ReductionOp(PyUFuncObject *self, PyArrayObject *arr, &innerloop, &innerloopdata) < 0) { PyArray_Descr *dtype = PyArray_DescrFromType(otype); PyErr_Format(PyExc_ValueError, - "could not find a matching type for %s.%s, " + "could not find a matching type for %s.reduce, " "requested type has type code '%c'", - ufunc_name, opname, dtype ? dtype->type : '-'); + ufunc_name, dtype ? dtype->type : '-'); Py_XDECREF(dtype); goto fail; } @@ -2540,32 +2531,19 @@ PyUFunc_ReductionOp(PyUFuncObject *self, PyArrayObject *arr, } #if NPY_UF_DBG_TRACING - printf("Found %s.%s inner loop with dtype : ", ufunc_name, opname); + printf("Found %s.reduce inner loop with dtype : ", ufunc_name); PyObject_Print((PyObject *)op_dtypes[0], stdout, 0); printf("\n"); #endif /* Set up the op_axes for the outer loop */ - if (operation == UFUNC_REDUCE) { - for (i = 0, idim = 0; idim < ndim; ++idim) { - if (idim != axis) { - op_axes_arrays[0][i] = i; - op_axes_arrays[1][i] = idim; - i++; - } - } - } - else if (operation == UFUNC_ACCUMULATE) { - for (idim = 0; idim < ndim; ++idim) { - op_axes_arrays[0][idim] = idim; - op_axes_arrays[1][idim] = idim; + for (i = 0, idim = 0; idim < ndim; ++idim) { + if (idim != axis) { + op_axes_arrays[0][i] = i; + op_axes_arrays[1][i] = idim; + i++; } } - else { - PyErr_Format(PyExc_RuntimeError, - "invalid reduction operation %s.%s", ufunc_name, opname); - goto fail; - } /* The per-operand flags for the outer loop */ op_flags[0] = NPY_ITER_READWRITE | @@ -2583,15 +2561,6 @@ PyUFunc_ReductionOp(PyUFuncObject *self, PyArrayObject *arr, op[1] = arr; need_outer_iterator = (ndim > 1); - if (operation == UFUNC_ACCUMULATE) { - /* We can't buffer, so must do UPDATEIFCOPY */ - if (!PyArray_ISALIGNED(arr) || (out && !PyArray_ISALIGNED(out)) || - !PyArray_EquivTypes(op_dtypes[0], PyArray_DESCR(arr)) || - (out && - !PyArray_EquivTypes(op_dtypes[0], PyArray_DESCR(out)))) { - need_outer_iterator = 1; - } - } if (need_outer_iterator) { int ndim_iter = 0; @@ -2599,24 +2568,9 @@ PyUFunc_ReductionOp(PyUFuncObject *self, PyArrayObject *arr, NPY_ITER_REFS_OK; PyArray_Descr **op_dtypes_param = NULL; - if (operation == UFUNC_REDUCE) { - ndim_iter = ndim - 1; - if (out == NULL) { - op_dtypes_param = op_dtypes; - } - } - else if (operation == UFUNC_ACCUMULATE) { - /* - * The way accumulate is set up, we can't do buffering, - * so make a copy instead when necessary. - */ - ndim_iter = ndim; - flags |= NPY_ITER_MULTI_INDEX; - /* Add some more flags */ - op_flags[0] |= NPY_ITER_UPDATEIFCOPY|NPY_ITER_ALIGNED; - op_flags[1] |= NPY_ITER_COPY|NPY_ITER_ALIGNED; + ndim_iter = ndim - 1; + if (out == NULL) { op_dtypes_param = op_dtypes; - op_dtypes[1] = op_dtypes[0]; } NPY_UF_DBG_PRINT("Allocating outer iterator\n"); iter = NpyIter_AdvancedNew(2, op, flags, @@ -2627,27 +2581,6 @@ PyUFunc_ReductionOp(PyUFuncObject *self, PyArrayObject *arr, if (iter == NULL) { goto fail; } - - if (operation == UFUNC_ACCUMULATE) { - /* In case COPY or UPDATEIFCOPY occurred */ - op[0] = NpyIter_GetOperandArray(iter)[0]; - op[1] = NpyIter_GetOperandArray(iter)[1]; - - if (PyArray_SIZE(op[0]) == 0) { - if (out == NULL) { - out = op[0]; - Py_INCREF(out); - } - goto finish; - } - - if (NpyIter_RemoveAxis(iter, axis) != NPY_SUCCEED) { - goto fail; - } - if (NpyIter_RemoveMultiIndex(iter) != NPY_SUCCEED) { - goto fail; - } - } } /* Get the output */ @@ -2659,18 +2592,10 @@ PyUFunc_ReductionOp(PyUFuncObject *self, PyArrayObject *arr, else { PyArray_Descr *dtype = op_dtypes[0]; Py_INCREF(dtype); - if (operation == UFUNC_REDUCE) { - op[0] = out = (PyArrayObject *)PyArray_NewFromDescr( - &PyArray_Type, dtype, - 0, NULL, NULL, NULL, - 0, NULL); - } - else if (operation == UFUNC_ACCUMULATE) { - op[0] = out = (PyArrayObject *)PyArray_NewFromDescr( - &PyArray_Type, dtype, - ndim, PyArray_DIMS(op[1]), NULL, NULL, - 0, NULL); - } + op[0] = out = (PyArrayObject *)PyArray_NewFromDescr( + &PyArray_Type, dtype, + 0, NULL, NULL, NULL, + 0, NULL); if (out == NULL) { goto fail; } @@ -2689,28 +2614,26 @@ PyUFunc_ReductionOp(PyUFuncObject *self, PyArrayObject *arr, * for UFUNC_ACCUMULATE. */ if (PyArray_DIM(op[1], axis) == 0) { - if (operation == UFUNC_REDUCE) { - if (self->identity == PyUFunc_None) { - PyErr_Format(PyExc_ValueError, - "zero-size array to %s.%s " - "without identity", ufunc_name, opname); + if (self->identity == PyUFunc_None) { + PyErr_Format(PyExc_ValueError, + "zero-size array to %s.reduce " + "without identity", ufunc_name); + goto fail; + } + if (self->identity == PyUFunc_One) { + PyObject *obj = PyInt_FromLong((long) 1); + if (obj == NULL) { goto fail; } - if (self->identity == PyUFunc_One) { - PyObject *obj = PyInt_FromLong((long) 1); - if (obj == NULL) { - goto fail; - } - PyArray_FillWithScalar(op[0], obj); - Py_DECREF(obj); - } else { - PyObject *obj = PyInt_FromLong((long) 0); - if (obj == NULL) { - goto fail; - } - PyArray_FillWithScalar(op[0], obj); - Py_DECREF(obj); + PyArray_FillWithScalar(op[0], obj); + Py_DECREF(obj); + } else { + PyObject *obj = PyInt_FromLong((long) 0); + if (obj == NULL) { + goto fail; } + PyArray_FillWithScalar(op[0], obj); + Py_DECREF(obj); } goto finish; @@ -2727,38 +2650,30 @@ PyUFunc_ReductionOp(PyUFuncObject *self, PyArrayObject *arr, op_dtypes[1] = op_dtypes[0]; NPY_UF_DBG_PRINT("Allocating inner iterator\n"); - if (operation == UFUNC_REDUCE) { - /* The per-operand flags for the inner loop */ - op_flags[0] = NPY_ITER_READWRITE| - NPY_ITER_ALIGNED; - op_flags[1] = NPY_ITER_READONLY| - NPY_ITER_ALIGNED; - - if (use_maskna) { - op_flags[0] |= NPY_ITER_USE_MASKNA; - op_flags[1] |= NPY_ITER_USE_MASKNA; - } + /* The per-operand flags for the inner loop */ + op_flags[0] = NPY_ITER_READWRITE| + NPY_ITER_ALIGNED; + op_flags[1] = NPY_ITER_READONLY| + NPY_ITER_ALIGNED; - op_axes[0][0] = -1; - op_axes[1][0] = axis; - - iter_inner = NpyIter_AdvancedNew(2, op, - NPY_ITER_EXTERNAL_LOOP | - NPY_ITER_BUFFERED | - NPY_ITER_DELAY_BUFALLOC | - NPY_ITER_GROWINNER | - NPY_ITER_REDUCE_OK | - NPY_ITER_REFS_OK, - NPY_CORDER, NPY_UNSAFE_CASTING, - op_flags, op_dtypes, - 1, op_axes, NULL, buffersize); - } - /* Should never get an inner iterator for ACCUMULATE */ - else { - PyErr_SetString(PyExc_RuntimeError, - "internal ufunc reduce error, should not need inner iterator"); - goto fail; + if (use_maskna) { + op_flags[0] |= NPY_ITER_USE_MASKNA; + op_flags[1] |= NPY_ITER_USE_MASKNA; } + + op_axes[0][0] = -1; + op_axes[1][0] = axis; + + iter_inner = NpyIter_AdvancedNew(2, op, + NPY_ITER_EXTERNAL_LOOP | + NPY_ITER_BUFFERED | + NPY_ITER_DELAY_BUFALLOC | + NPY_ITER_GROWINNER | + NPY_ITER_REDUCE_OK | + NPY_ITER_REFS_OK, + NPY_CORDER, NPY_UNSAFE_CASTING, + op_flags, op_dtypes, + 1, op_axes, NULL, buffersize); if (iter_inner == NULL) { goto fail; } @@ -2856,10 +2771,6 @@ PyUFunc_ReductionOp(PyUFuncObject *self, PyArrayObject *arr, NPY_UF_DBG_PRINT("UFunc: Reduce loop with just outer iterator\n"); - if (operation == UFUNC_ACCUMULATE) { - stride0 = PyArray_STRIDE(op[0], axis); - } - stride_copy[0] = stride0; stride_copy[1] = stride1; stride_copy[2] = stride0; @@ -2889,13 +2800,7 @@ PyUFunc_ReductionOp(PyUFuncObject *self, PyArrayObject *arr, if (count_m1 > 0) { /* Turn the two items into three for the inner loop */ - if (operation == UFUNC_REDUCE) { - dataptr_copy[1] += stride1; - } - else if (operation == UFUNC_ACCUMULATE) { - dataptr_copy[1] += stride1; - dataptr_copy[2] += stride0; - } + dataptr_copy[1] += stride1; NPY_UF_DBG_PRINT1("iterator loop count %d\n", (int)count_m1); innerloop(dataptr_copy, &count_m1, @@ -2985,25 +2890,11 @@ PyUFunc_ReductionOp(PyUFuncObject *self, PyArrayObject *arr, NPY_UF_DBG_PRINT("UFunc: Reduce loop with no iterators\n"); - if (operation == UFUNC_REDUCE) { - if (PyArray_NDIM(op[0]) != 0) { - PyErr_SetString(PyExc_ValueError, - "provided out is the wrong size " - "for the reduction"); - goto fail; - } - } - else if (operation == UFUNC_ACCUMULATE) { - if (PyArray_NDIM(op[0]) != PyArray_NDIM(op[1]) || - !PyArray_CompareLists(PyArray_DIMS(op[0]), - PyArray_DIMS(op[1]), - PyArray_NDIM(op[0]))) { - PyErr_SetString(PyExc_ValueError, - "provided out is the wrong size " - "for the reduction"); - goto fail; - } - stride0 = PyArray_STRIDE(op[0], axis); + if (PyArray_NDIM(op[0]) != 0) { + PyErr_SetString(PyExc_ValueError, + "provided out is the wrong size " + "for the reduction"); + goto fail; } stride_copy[0] = stride0; @@ -3028,13 +2919,7 @@ PyUFunc_ReductionOp(PyUFuncObject *self, PyArrayObject *arr, if (count > 1) { --count; - if (operation == UFUNC_REDUCE) { - dataptr_copy[1] += stride1; - } - else if (operation == UFUNC_ACCUMULATE) { - dataptr_copy[1] += stride1; - dataptr_copy[2] += stride0; - } + dataptr_copy[1] += stride1; NPY_UF_DBG_PRINT1("iterator loop count %d\n", (int)count); @@ -3083,28 +2968,374 @@ fail: return NULL; } -/* - * We have two basic kinds of loops. One is used when arr is not-swapped - * and aligned and output type is the same as input type. The other uses - * buffers when one of these is not satisfied. - * - * Zero-length and one-length axes-to-be-reduced are handled separately. - */ -static PyObject * -PyUFunc_Reduce(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out, - int axis, int otype, int skipna) -{ - return PyUFunc_ReductionOp(self, arr, out, axis, otype, skipna, - UFUNC_REDUCE, "reduce"); -} - static PyObject * PyUFunc_Accumulate(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out, int axis, int otype, int skipna) { - return PyUFunc_ReductionOp(self, arr, out, axis, otype, skipna, - UFUNC_ACCUMULATE, "accumulate"); + PyArrayObject *op[2]; + PyArray_Descr *op_dtypes[2] = {NULL, NULL}; + int op_axes_arrays[2][NPY_MAXDIMS]; + int *op_axes[2] = {op_axes_arrays[0], op_axes_arrays[1]}; + npy_uint32 op_flags[2]; + int i, idim, ndim, otype_final; + int needs_api, need_outer_iterator, use_maskna = 0; + + NpyIter *iter = NULL, *iter_inner = NULL; + + /* The selected inner loop */ + PyUFuncGenericFunction innerloop = NULL; + void *innerloopdata = NULL; + + char *ufunc_name = self->name ? self->name : "(unknown)"; + + /* These parameters come from extobj= or from a TLS global */ + int buffersize = 0, errormask = 0; + PyObject *errobj = NULL; + + NPY_BEGIN_THREADS_DEF; + + NPY_UF_DBG_PRINT1("\nEvaluating ufunc %s.accumulate\n", ufunc_name); + +#if 0 + printf("Doing %s.accumulate on array with dtype : ", ufunc_name); + PyObject_Print((PyObject *)PyArray_DESCR(arr), stdout, 0); + printf("\n"); +#endif + + use_maskna = PyArray_HASMASKNA(arr); + if (use_maskna) { + PyErr_SetString(PyExc_RuntimeError, + "ufunc accumulate doesn't support NA masked arrays yet"); + return NULL; + } + /* If there's no NA mask, there are no NAs to skip */ + else { + skipna = 0; + } + + if (PyUFunc_GetPyValues("accumulate", &buffersize, &errormask, &errobj) < 0) { + return NULL; + } + + /* Take a reference to out for later returning */ + Py_XINCREF(out); + + otype_final = otype; + if (get_binary_op_function(self, &otype_final, + &innerloop, &innerloopdata) < 0) { + PyArray_Descr *dtype = PyArray_DescrFromType(otype); + PyErr_Format(PyExc_ValueError, + "could not find a matching type for %s.accumulate, " + "requested type has type code '%c'", + ufunc_name, dtype ? dtype->type : '-'); + Py_XDECREF(dtype); + goto fail; + } + + ndim = PyArray_NDIM(arr); + + /* + * Set up the output data type, using the input's exact + * data type if the type number didn't change to preserve + * metadata + */ + if (PyArray_DESCR(arr)->type_num == otype_final) { + if (PyArray_ISNBO(PyArray_DESCR(arr)->byteorder)) { + op_dtypes[0] = PyArray_DESCR(arr); + Py_INCREF(op_dtypes[0]); + } + else { + op_dtypes[0] = PyArray_DescrNewByteorder(PyArray_DESCR(arr), + NPY_NATIVE); + } + } + else { + op_dtypes[0] = PyArray_DescrFromType(otype_final); + } + if (op_dtypes[0] == NULL) { + goto fail; + } + +#if NPY_UF_DBG_TRACING + printf("Found %s.%s inner loop with dtype : ", ufunc_name, opname); + PyObject_Print((PyObject *)op_dtypes[0], stdout, 0); + printf("\n"); +#endif + + /* Set up the op_axes for the outer loop */ + for (idim = 0; idim < ndim; ++idim) { + op_axes_arrays[0][idim] = idim; + op_axes_arrays[1][idim] = idim; + } + + /* The per-operand flags for the outer loop */ + op_flags[0] = NPY_ITER_READWRITE | + NPY_ITER_NO_BROADCAST | + NPY_ITER_ALLOCATE | + NPY_ITER_NO_SUBTYPE; + op_flags[1] = NPY_ITER_READONLY; + + if (use_maskna) { + op_flags[0] |= NPY_ITER_USE_MASKNA; + op_flags[1] |= NPY_ITER_USE_MASKNA; + } + + op[0] = out; + op[1] = arr; + + need_outer_iterator = (ndim > 1); + /* We can't buffer, so must do UPDATEIFCOPY */ + if (!PyArray_ISALIGNED(arr) || (out && !PyArray_ISALIGNED(out)) || + !PyArray_EquivTypes(op_dtypes[0], PyArray_DESCR(arr)) || + (out && + !PyArray_EquivTypes(op_dtypes[0], PyArray_DESCR(out)))) { + need_outer_iterator = 1; + } + + if (need_outer_iterator) { + int ndim_iter = 0; + npy_uint32 flags = NPY_ITER_ZEROSIZE_OK| + NPY_ITER_REFS_OK; + PyArray_Descr **op_dtypes_param = NULL; + + /* + * The way accumulate is set up, we can't do buffering, + * so make a copy instead when necessary. + */ + ndim_iter = ndim; + flags |= NPY_ITER_MULTI_INDEX; + /* Add some more flags */ + op_flags[0] |= NPY_ITER_UPDATEIFCOPY|NPY_ITER_ALIGNED; + op_flags[1] |= NPY_ITER_COPY|NPY_ITER_ALIGNED; + op_dtypes_param = op_dtypes; + op_dtypes[1] = op_dtypes[0]; + NPY_UF_DBG_PRINT("Allocating outer iterator\n"); + iter = NpyIter_AdvancedNew(2, op, flags, + NPY_KEEPORDER, NPY_UNSAFE_CASTING, + op_flags, + op_dtypes_param, + ndim_iter, op_axes, NULL, 0); + if (iter == NULL) { + goto fail; + } + + /* In case COPY or UPDATEIFCOPY occurred */ + op[0] = NpyIter_GetOperandArray(iter)[0]; + op[1] = NpyIter_GetOperandArray(iter)[1]; + + if (PyArray_SIZE(op[0]) == 0) { + if (out == NULL) { + out = op[0]; + Py_INCREF(out); + } + goto finish; + } + + if (NpyIter_RemoveAxis(iter, axis) != NPY_SUCCEED) { + goto fail; + } + if (NpyIter_RemoveMultiIndex(iter) != NPY_SUCCEED) { + goto fail; + } + } + + /* Get the output */ + if (out == NULL) { + if (iter) { + op[0] = out = NpyIter_GetOperandArray(iter)[0]; + Py_INCREF(out); + } + else { + PyArray_Descr *dtype = op_dtypes[0]; + Py_INCREF(dtype); + op[0] = out = (PyArrayObject *)PyArray_NewFromDescr( + &PyArray_Type, dtype, + ndim, PyArray_DIMS(op[1]), NULL, NULL, + 0, NULL); + if (out == NULL) { + goto fail; + } + + if (use_maskna) { + if (PyArray_AllocateMaskNA(out, 1, 0, 1) < 0) { + goto fail; + } + } + } + } + + /* + * If the reduction axis has size zero, either return the reduction + * unit for UFUNC_REDUCE, or return the zero-sized output array + * for UFUNC_ACCUMULATE. + */ + if (PyArray_DIM(op[1], axis) == 0) { + goto finish; + } + else if (PyArray_SIZE(op[0]) == 0) { + goto finish; + } + + if (iter && NpyIter_GetIterSize(iter) != 0) { + char *dataptr_copy[3]; + npy_intp stride_copy[3]; + + NpyIter_IterNextFunc *iternext; + char **dataptr; + + int itemsize = op_dtypes[0]->elsize; + + /* Get the variables needed for the loop */ + iternext = NpyIter_GetIterNext(iter, NULL); + if (iternext == NULL) { + goto fail; + } + dataptr = NpyIter_GetDataPtrArray(iter); + + + /* Execute the loop with just the outer iterator */ + npy_intp count_m1 = PyArray_DIM(op[1], axis)-1; + npy_intp stride0 = 0, stride1 = PyArray_STRIDE(op[1], axis); + + NPY_UF_DBG_PRINT("UFunc: Reduce loop with just outer iterator\n"); + + stride0 = PyArray_STRIDE(op[0], axis); + + stride_copy[0] = stride0; + stride_copy[1] = stride1; + stride_copy[2] = stride0; + + needs_api = NpyIter_IterationNeedsAPI(iter); + + if (!needs_api) { + NPY_BEGIN_THREADS; + } + + do { + + dataptr_copy[0] = dataptr[0]; + dataptr_copy[1] = dataptr[1]; + dataptr_copy[2] = dataptr[0]; + + /* Copy the first element to start the reduction */ + if (otype == NPY_OBJECT) { + Py_XDECREF(*(PyObject **)dataptr_copy[0]); + *(PyObject **)dataptr_copy[0] = + *(PyObject **)dataptr_copy[1]; + Py_XINCREF(*(PyObject **)dataptr_copy[0]); + } + else { + memcpy(dataptr_copy[0], dataptr_copy[1], itemsize); + } + + if (count_m1 > 0) { + /* Turn the two items into three for the inner loop */ + dataptr_copy[1] += stride1; + dataptr_copy[2] += stride0; + NPY_UF_DBG_PRINT1("iterator loop count %d\n", + (int)count_m1); + innerloop(dataptr_copy, &count_m1, + stride_copy, innerloopdata); + } + } while (iternext(iter)); + + if (!needs_api) { + NPY_END_THREADS; + } + } + else if (iter == NULL) { + char *dataptr_copy[3]; + npy_intp stride_copy[3]; + + int itemsize = op_dtypes[0]->elsize; + + /* Execute the loop with no iterators */ + npy_intp count = PyArray_DIM(op[1], axis); + npy_intp stride0 = 0, stride1 = PyArray_STRIDE(op[1], axis); + + NPY_UF_DBG_PRINT("UFunc: Reduce loop with no iterators\n"); + + if (PyArray_NDIM(op[0]) != PyArray_NDIM(op[1]) || + !PyArray_CompareLists(PyArray_DIMS(op[0]), + PyArray_DIMS(op[1]), + PyArray_NDIM(op[0]))) { + PyErr_SetString(PyExc_ValueError, + "provided out is the wrong size " + "for the reduction"); + goto fail; + } + stride0 = PyArray_STRIDE(op[0], axis); + + stride_copy[0] = stride0; + stride_copy[1] = stride1; + stride_copy[2] = stride0; + + /* Turn the two items into three for the inner loop */ + dataptr_copy[0] = PyArray_BYTES(op[0]); + dataptr_copy[1] = PyArray_BYTES(op[1]); + dataptr_copy[2] = PyArray_BYTES(op[0]); + + /* Copy the first element to start the reduction */ + if (otype == NPY_OBJECT) { + Py_XDECREF(*(PyObject **)dataptr_copy[0]); + *(PyObject **)dataptr_copy[0] = + *(PyObject **)dataptr_copy[1]; + Py_XINCREF(*(PyObject **)dataptr_copy[0]); + } + else { + memcpy(dataptr_copy[0], dataptr_copy[1], itemsize); + } + + if (count > 1) { + --count; + dataptr_copy[1] += stride1; + dataptr_copy[2] += stride0; + + NPY_UF_DBG_PRINT1("iterator loop count %d\n", (int)count); + + needs_api = PyDataType_REFCHK(op_dtypes[0]); + + if (!needs_api) { + NPY_BEGIN_THREADS; + } + + innerloop(dataptr_copy, &count, + stride_copy, innerloopdata); + + if (!needs_api) { + NPY_END_THREADS; + } + } + } + +finish: + Py_XDECREF(op_dtypes[0]); + if (iter != NULL) { + NpyIter_Deallocate(iter); + } + if (iter_inner != NULL) { + NpyIter_Deallocate(iter_inner); + } + + Py_XDECREF(errobj); + + return (PyObject *)out; + +fail: + Py_XDECREF(out); + Py_XDECREF(op_dtypes[0]); + + if (iter != NULL) { + NpyIter_Deallocate(iter); + } + if (iter_inner != NULL) { + NpyIter_Deallocate(iter_inner); + } + + Py_XDECREF(errobj); + + return NULL; } /* |