summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorMark Wiebe <mwwiebe@gmail.com>2011-08-22 15:32:00 -0700
committerCharles Harris <charlesr.harris@gmail.com>2011-08-27 07:26:59 -0600
commitc8c262a1be42c3989994bdc557c7b25e22d80d83 (patch)
tree7c402605e5d1fbebb4afce77be4a9579aa9aa221 /numpy
parent4b79354bd51f7702794fd8c0793adc82440f4c6f (diff)
downloadnumpy-c8c262a1be42c3989994bdc557c7b25e22d80d83.tar.gz
ENH: ufunc: Remove CreateReduceResult and InitializeReduceResult from the API
Keep them internal longer, to let the details bake some more. PyArray_ReduceWrapper is in the API so both multiarray and umath can use it.
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/code_generators/numpy_api.py8
-rw-r--r--numpy/core/src/multiarray/reduction.c6
-rw-r--r--numpy/core/src/multiarray/reduction.h215
3 files changed, 90 insertions, 139 deletions
diff --git a/numpy/core/code_generators/numpy_api.py b/numpy/core/code_generators/numpy_api.py
index 08266d6ca..e21cc96b4 100644
--- a/numpy/core/code_generators/numpy_api.py
+++ b/numpy/core/code_generators/numpy_api.py
@@ -333,11 +333,9 @@ multiarray_funcs_api = {
'PyArray_AssignMaskNA': 294,
'PyArray_AssignRawScalar': 295,
'PyArray_AssignArray': 296,
- 'PyArray_CreateReduceResult': 297,
- 'PyArray_InitializeReduceResult': 298,
- 'PyArray_ReduceWrapper': 299,
- 'PyArray_RemoveAxesInPlace': 300,
- 'PyArray_DebugPrint': 301,
+ 'PyArray_ReduceWrapper': 297,
+ 'PyArray_RemoveAxesInPlace': 298,
+ 'PyArray_DebugPrint': 299,
}
ufunc_types_api = {
diff --git a/numpy/core/src/multiarray/reduction.c b/numpy/core/src/multiarray/reduction.c
index 0cc513a04..3034a8e2d 100644
--- a/numpy/core/src/multiarray/reduction.c
+++ b/numpy/core/src/multiarray/reduction.c
@@ -188,8 +188,7 @@ conform_reduce_result(int ndim, npy_bool *axis_flags,
return (PyArrayObject *)ret;
}
-/*NUMPY_API
- *
+/*
* Creates a result for reducing 'operand' along the axes specified
* in 'axis_flags'. If 'dtype' isn't NULL, this function steals a
* reference to 'dtype'.
@@ -270,8 +269,7 @@ check_nonreorderable_axes(int ndim, npy_bool *axis_flags, const char *funcname)
return 0;
}
-/*NUMPY_API
- *
+/*
* This function initializes a result array for a reduction operation
* which has no identity. This means it needs to copy the first element
* it sees along the reduction axes to result, then return a view of
diff --git a/numpy/core/src/multiarray/reduction.h b/numpy/core/src/multiarray/reduction.h
index 3a04fd118..95193ad28 100644
--- a/numpy/core/src/multiarray/reduction.h
+++ b/numpy/core/src/multiarray/reduction.h
@@ -2,136 +2,6 @@
#define _NPY_PRIVATE__REDUCTION_H_
/*
- * This is a function for assigning a reduction unit to the result,
- * before doing the reduction computation. If 'preservena' is True,
- * any masked NA values in 'result' should not be overwritten. The
- * value in 'data' is passed through from PyArray_ReduceWrapper.
- *
- * This function could, for example, simply be a call like
- * return PyArray_AssignZero(result, NULL, preservena, NULL);
- *
- * It should return -1 on failure, or 0 on success.
- */
-typedef int (PyArray_AssignReduceUnitFunc)(PyArrayObject *result,
- int preservena, void *data);
-
-/*
- * This is a function for the inner reduce loop. Both the unmasked and
- * masked variants have the same prototype, but should behave differently.
- *
- * The needs_api parameter indicates whether it's ok to release the GIL during
- * the inner loop, such as when the iternext() function never calls
- * a function which could raise a Python exception.
- *
- * Ths skip_first_count parameter indicates how many elements need to be
- * skipped based on NpyIter_IsFirstVisit checks. This can only be positive
- * when the 'assign_unit' parameter was NULL when calling
- * PyArray_ReduceWrapper.
- *
- * The unmasked inner loop gets two data pointers and two strides, and should
- * look roughly like this:
- * {
- * NPY_BEGIN_THREADS_DEF;
- * if (!needs_api) {
- * NPY_BEGIN_THREADS;
- * }
- * // This first-visit loop can be skipped if 'assign_unit' was non-NULL
- * if (skip_first_count > 0) {
- * do {
- * char *data0 = dataptr[0], *data1 = dataptr[1];
- * npy_intp stride0 = strideptr[0], stride1 = strideptr[1];
- * npy_intp count = *countptr;
- *
- * // Skip any first-visit elements
- * if (NpyIter_IsFirstVisit(iter, 0)) {
- * if (stride0 == 0) {
- * --count;
- * --skip_first_count;
- * data1 += stride1;
- * //data2 += stride2; // In masked loop
- * }
- * else {
- * skip_first_count -= count;
- * count = 0;
- * }
- * }
- *
- * while (count--) {
- * *(result_t *)data0 = my_reduce_op(*(result_t *)data0,
- * *(operand_t *)data1);
- * data0 += stride0;
- * data1 += stride1;
- * }
- *
- * // Jump to the faster loop when skipping is done
- * if (skip_first_count == 0) {
- * if (iternext(iter)) {
- * break;
- * }
- * else {
- * goto finish_loop;
- * }
- * }
- * } while (iternext(iter));
- * }
- * do {
- * char *data0 = dataptr[0], *data1 = dataptr[1];
- * npy_intp stride0 = strideptr[0], stride1 = strideptr[1];
- * npy_intp count = *countptr;
- *
- * while (count--) {
- * *(result_t *)data0 = my_reduce_op(*(result_t *)data0,
- * *(operand_t *)data1);
- * data0 += stride0;
- * data1 += stride1;
- * }
- * } while (iternext(iter));
- * finish_loop:
- * if (!needs_api) {
- * NPY_END_THREADS;
- * }
- * return (needs_api && PyErr_Occurred()) ? -1 : 0;
- * }
- *
- * The masked inner loop gets three data pointers and three strides, and
- * looks identical except for the iteration inner loops which should be
- * like this:
- * do {
- * char *data0 = dataptr[0], *data1 = dataptr[1], *data2 = dataptr[2];
- * npy_intp stride0 = strideptr[0], stride1 = strideptr[1],
- * stride2 = strideptr[2];
- * npy_intp count = *countptr;
- *
- * // Skipping first visits would go here
- *
- * while (count--) {
- * if (NpyMaskValue_IsExposed((npy_mask)*data2)) {
- * *(result_t *)data0 = my_reduce_op(*(result_t *)data0,
- * *(operand_t *)data1);
- * }
- * data0 += stride0;
- * data1 += stride1;
- * data2 += stride2;
- * }
- *
- * // Jumping to the faster loop would go here
- *
- * } while (iternext(iter));
- *
- * If needs_api is True, this function should call PyErr_Occurred()
- * to check if an error occurred during processing, and return -1 for
- * error, 0 for success.
- */
-typedef int (PyArray_ReduceInnerLoopFunc)(NpyIter *iter,
- char **dataptr,
- npy_intp *strideptr,
- npy_intp *countptr,
- NpyIter_IterNextFunc *iternext,
- int needs_api,
- npy_intp skip_first_count,
- void *data);
-
-/*
* This function counts the number of elements that a reduction
* will see along the reduction directions, given the provided options.
*
@@ -147,5 +17,90 @@ NPY_NO_EXPORT PyObject *
PyArray_CountReduceItems(PyArrayObject *operand,
npy_bool *axis_flags, int skipna, int keepdims);
+/*
+ * This function initializes a result array for a reduction operation
+ * which has no identity. This means it needs to copy the first element
+ * it sees along the reduction axes to result, then return a view of
+ * the operand which excludes that element.
+ *
+ * If a reduction has an identity, such as 0 or 1, the result should
+ * be initialized by calling PyArray_AssignZero(result, NULL, !skipna, NULL)
+ * or PyArray_AssignOne(result, NULL, !skipna, NULL), because this
+ * function raises an exception when there are no elements to reduce.
+ *
+ * For regular reduction, this means it copies the subarray indexed
+ * at zero along each reduction axis into 'result', then returns a view
+ * into 'operand' excluding those copied elements. If 'operand' has
+ * an NA mask in this case, the caller should have already done
+ * the reduction on the mask. This function copies the subarray with
+ * 'replacena' set to True, so that the already accumulated NA mask
+ * in result doesn't get overwritten.
+ *
+ * For 'skipna' reduction, this is more complicated. In the one dimensional
+ * case, it searches for the first non-NA element, copies that element
+ * to 'result', then returns a view into the rest of 'operand'. For
+ * multi-dimensional reductions, the initial elements may be scattered
+ * throughout the array.
+ *
+ * To deal with this, a view of 'operand' is taken, and given its own
+ * copy of the NA mask. Additionally, an array of flags is created,
+ * matching the shape of 'result', and initialized to all False.
+ * Then, the elements of the 'operand' view are gone through, and any time
+ * an exposed element is encounted which isn't already flagged in the
+ * auxiliary array, it is copied into 'result' and flagged as copied.
+ * The element is masked as an NA in the view of 'operand', so that the
+ * later reduction step will skip it during processing.
+ *
+ * result : The array into which the result is computed. This must have
+ * the same number of dimensions as 'operand', but for each
+ * axis i where 'axis_flags[i]' is True, it has a single element.
+ * operand : The array being reduced.
+ * axis_flags : An array of boolean flags, one for each axis of 'operand'.
+ * When a flag is True, it indicates to reduce along that axis.
+ * reorderable : If True, the reduction being done is reorderable, which
+ * means specifying multiple axes of reduction at once is ok,
+ * and the reduction code may calculate the reduction in an
+ * arbitrary order. The calculation may be reordered because
+ * of cache behavior or multithreading requirements.
+ * skipna : If True, indicates that the reduction is being calculated
+ * as if the NA values are being dropped from the computation
+ * instead of accumulating into an NA result.
+ * out_skip_first_count : This gets populated with the number of first-visit
+ * elements that should be skipped during the
+ * iteration loop.
+ * funcname : The name of the reduction operation, for the purpose of
+ * better quality error messages. For example, "numpy.max"
+ * would be a good name for NumPy's max function.
+ *
+ * Returns a view which contains the remaining elements on which to do
+ * the reduction.
+ */
+NPY_NO_EXPORT PyArrayObject *
+PyArray_InitializeReduceResult(
+ PyArrayObject *result, PyArrayObject *operand,
+ npy_bool *axis_flags, int reorderable, int skipna,
+ npy_intp *out_skip_first_count, const char *funcname);
+
+/*
+ * Creates a result for reducing 'operand' along the axes specified
+ * in 'axis_flags'. If 'dtype' isn't NULL, this function steals a
+ * reference to 'dtype'.
+ *
+ * If 'out' isn't NULL, this function creates a view conforming
+ * to the number of dimensions of 'operand', adding a singleton dimension
+ * for each reduction axis specified. In this case, 'dtype' is ignored
+ * (but its reference is still stolen), and the caller must handle any
+ * type conversion/validity check for 'out'. When 'need_namask' is true,
+ * raises an exception if 'out' doesn't have an NA mask.
+ *
+ * If 'out' is NULL, it allocates a new array whose shape matches
+ * that of 'operand', except for at the reduction axes. An NA mask
+ * is added if 'need_namask' is true. If 'dtype' is NULL, the dtype
+ * of 'operand' is used for the result.
+ */
+NPY_NO_EXPORT PyArrayObject *
+PyArray_CreateReduceResult(PyArrayObject *operand, PyArrayObject *out,
+ PyArray_Descr *dtype, npy_bool *axis_flags,
+ int need_namask, int keepdims, const char *funcname);
#endif