numpy  2.0.0
src/umath/reduction.h
Go to the documentation of this file.
00001 #ifndef _NPY_PRIVATE__REDUCTION_H_
00002 #define _NPY_PRIVATE__REDUCTION_H_
00003 
00004 /************************************************************
00005  * Typedefs used by PyArray_ReduceWrapper, new in 1.7.
00006  ************************************************************/
00007 
00008 /*
00009  * This is a function for assigning a reduction identity to the result,
00010  * before doing the reduction computation. The
00011  * value in 'data' is passed through from PyArray_ReduceWrapper.
00012  *
00013  * This function could, for example, simply be a call like
00014  *      return PyArray_AssignZero(result, NULL, NULL);
00015  *
00016  * It should return -1 on failure, or 0 on success.
00017  */
00018 typedef int (PyArray_AssignReduceIdentityFunc)(PyArrayObject *result,
00019                                                void *data);
00020 
00021 /*
00022  * This is a function for the reduce loop.
00023  *
00024  * The needs_api parameter indicates whether it's ok to release the GIL during
00025  * the loop, such as when the iternext() function never calls
00026  * a function which could raise a Python exception.
00027  *
00028  * Ths skip_first_count parameter indicates how many elements need to be
00029  * skipped based on NpyIter_IsFirstVisit checks. This can only be positive
00030  * when the 'assign_identity' parameter was NULL when calling
00031  * PyArray_ReduceWrapper.
00032  *
00033  * The loop gets two data pointers and two strides, and should
00034  * look roughly like this:
00035  *  {
00036  *      NPY_BEGIN_THREADS_DEF;
00037  *      if (!needs_api) {
00038  *          NPY_BEGIN_THREADS;
00039  *      }
00040  *      // This first-visit loop can be skipped if 'assign_identity' was non-NULL
00041  *      if (skip_first_count > 0) {
00042  *          do {
00043  *              char *data0 = dataptr[0], *data1 = dataptr[1];
00044  *              npy_intp stride0 = strideptr[0], stride1 = strideptr[1];
00045  *              npy_intp count = *countptr;
00046  *
00047  *              // Skip any first-visit elements
00048  *              if (NpyIter_IsFirstVisit(iter, 0)) {
00049  *                  if (stride0 == 0) {
00050  *                      --count;
00051  *                      --skip_first_count;
00052  *                      data1 += stride1;
00053  *                  }
00054  *                  else {
00055  *                      skip_first_count -= count;
00056  *                      count = 0;
00057  *                  }
00058  *              }
00059  *
00060  *              while (count--) {
00061  *                  *(result_t *)data0 = my_reduce_op(*(result_t *)data0,
00062  *                                                    *(operand_t *)data1);
00063  *                  data0 += stride0;
00064  *                  data1 += stride1;
00065  *              }
00066  *
00067  *              // Jump to the faster loop when skipping is done
00068  *              if (skip_first_count == 0) {
00069  *                  if (iternext(iter)) {
00070  *                      break;
00071  *                  }
00072  *                  else {
00073  *                      goto finish_loop;
00074  *                  }
00075  *              }
00076  *          } while (iternext(iter));
00077  *      }
00078  *      do {
00079  *          char *data0 = dataptr[0], *data1 = dataptr[1];
00080  *          npy_intp stride0 = strideptr[0], stride1 = strideptr[1];
00081  *          npy_intp count = *countptr;
00082  *
00083  *          while (count--) {
00084  *              *(result_t *)data0 = my_reduce_op(*(result_t *)data0,
00085  *                                                *(operand_t *)data1);
00086  *              data0 += stride0;
00087  *              data1 += stride1;
00088  *          }
00089  *      } while (iternext(iter));
00090  *  finish_loop:
00091  *      if (!needs_api) {
00092  *          NPY_END_THREADS;
00093  *      }
00094  *      return (needs_api && PyErr_Occurred()) ? -1 : 0;
00095  *  }
00096  *
00097  * If needs_api is True, this function should call PyErr_Occurred()
00098  * to check if an error occurred during processing, and return -1 for
00099  * error, 0 for success.
00100  */
00101 typedef int (PyArray_ReduceLoopFunc)(NpyIter *iter,
00102                                             char **dataptr,
00103                                             npy_intp *strideptr,
00104                                             npy_intp *countptr,
00105                                             NpyIter_IterNextFunc *iternext,
00106                                             int needs_api,
00107                                             npy_intp skip_first_count,
00108                                             void *data);
00109 
00110 /*
00111  * This function executes all the standard NumPy reduction function
00112  * boilerplate code, just calling assign_identity and the appropriate
00113  * inner loop function where necessary.
00114  *
00115  * operand     : The array to be reduced.
00116  * out         : NULL, or the array into which to place the result.
00117  * wheremask   : NOT YET SUPPORTED, but this parameter is placed here
00118  *               so that support can be added in the future without breaking
00119  *               API compatibility. Pass in NULL.
00120  * operand_dtype : The dtype the inner loop expects for the operand.
00121  * result_dtype : The dtype the inner loop expects for the result.
00122  * casting     : The casting rule to apply to the operands.
00123  * axis_flags  : Flags indicating the reduction axes of 'operand'.
00124  * reorderable : If True, the reduction being done is reorderable, which
00125  *               means specifying multiple axes of reduction at once is ok,
00126  *               and the reduction code may calculate the reduction in an
00127  *               arbitrary order. The calculation may be reordered because
00128  *               of cache behavior or multithreading requirements.
00129  * keepdims    : If true, leaves the reduction dimensions in the result
00130  *               with size one.
00131  * subok       : If true, the result uses the subclass of operand, otherwise
00132  *               it is always a base class ndarray.
00133  * assign_identity : If NULL, PyArray_InitializeReduceResult is used, otherwise
00134  *               this function is called to initialize the result to
00135  *               the reduction's unit.
00136  * loop        : The loop which does the reduction.
00137  * data        : Data which is passed to assign_identity and the inner loop.
00138  * buffersize  : Buffer size for the iterator. For the default, pass in 0.
00139  * funcname    : The name of the reduction function, for error messages.
00140  */
00141 NPY_NO_EXPORT PyArrayObject *
00142 PyUFunc_ReduceWrapper(PyArrayObject *operand, PyArrayObject *out,
00143                       PyArrayObject *wheremask,
00144                       PyArray_Descr *operand_dtype,
00145                       PyArray_Descr *result_dtype,
00146                       NPY_CASTING casting,
00147                       npy_bool *axis_flags, int reorderable,
00148                       int keepdims,
00149                       int subok,
00150                       PyArray_AssignReduceIdentityFunc *assign_identity,
00151                       PyArray_ReduceLoopFunc *loop,
00152                       void *data, npy_intp buffersize, const char *funcname);
00153 
00154 #endif