numpy 2.0.0
src/multiarray/nditer.c.src File Reference
#include "Python.h"
#include "structmember.h"
#include <numpy/ndarrayobject.h>
#include <numpy/npy_3kcompat.h>
#include "convert_datatype.h"
#include "lowlevel_strided_loops.h"

Data Structures

struct  NpyIter_InternalOnly
struct  NpyIter_BD
struct  NpyIter_AD

Defines

#define PY_SSIZE_T_CLEAN
#define _MULTIARRAYMODULE
#define NPY_IT_CONSTRUCTION_TIMING   0
#define NPY_IT_TIME_POINT(var)
#define NPY_IT_DBG_TRACING   0
#define NPY_IT_DBG_PRINT(s)
#define NPY_IT_DBG_PRINT1(s, p1)
#define NPY_IT_DBG_PRINT2(s, p1, p2)
#define NPY_IT_DBG_PRINT3(s, p1, p2, p3)
#define NPY_INTP_ALIGNED(size)   ((size + 0x7)&(-0x8))
#define NPY_ITFLAG_IDENTPERM   0x0001
#define NPY_ITFLAG_NEGPERM   0x0002
#define NPY_ITFLAG_HASINDEX   0x0004
#define NPY_ITFLAG_HASMULTIINDEX   0x0008
#define NPY_ITFLAG_FORCEDORDER   0x0010
#define NPY_ITFLAG_EXLOOP   0x0020
#define NPY_ITFLAG_RANGE   0x0040
#define NPY_ITFLAG_BUFFER   0x0080
#define NPY_ITFLAG_GROWINNER   0x0100
#define NPY_ITFLAG_ONEITERATION   0x0200
#define NPY_ITFLAG_DELAYBUF   0x0400
#define NPY_ITFLAG_NEEDSAPI   0x0800
#define NPY_ITFLAG_REDUCE   0x1000
#define NPY_ITFLAG_REUSE_REDUCE_LOOPS   0x2000
#define NPY_OP_ITFLAG_WRITE   0x01
#define NPY_OP_ITFLAG_READ   0x02
#define NPY_OP_ITFLAG_CAST   0x04
#define NPY_OP_ITFLAG_BUFNEVER   0x08
#define NPY_OP_ITFLAG_ALIGNED   0x10
#define NPY_OP_ITFLAG_REDUCE   0x20
#define NIT_PERM_SIZEOF(itflags, ndim, nop)   NPY_INTP_ALIGNED(NPY_MAXDIMS)
#define NIT_DTYPES_SIZEOF(itflags, ndim, nop)   ((NPY_SIZEOF_INTP)*(nop))
#define NIT_RESETDATAPTR_SIZEOF(itflags, ndim, nop)   ((NPY_SIZEOF_INTP)*(nop+1))
#define NIT_BASEOFFSETS_SIZEOF(itflags, ndim, nop)   ((NPY_SIZEOF_INTP)*(nop+1))
#define NIT_OPERANDS_SIZEOF(itflags, ndim, nop)   ((NPY_SIZEOF_INTP)*(nop))
#define NIT_OPITFLAGS_SIZEOF(itflags, ndim, nop)   (NPY_INTP_ALIGNED(nop))
#define NIT_BUFFERDATA_SIZEOF(itflags, ndim, nop)   ((itflags&NPY_ITFLAG_BUFFER) ? ((NPY_SIZEOF_INTP)*(6 + 9*nop)) : 0)
#define NIT_PERM_OFFSET()   (0)
#define NIT_DTYPES_OFFSET(itflags, ndim, nop)
#define NIT_RESETDATAPTR_OFFSET(itflags, ndim, nop)
#define NIT_BASEOFFSETS_OFFSET(itflags, ndim, nop)
#define NIT_OPERANDS_OFFSET(itflags, ndim, nop)
#define NIT_OPITFLAGS_OFFSET(itflags, ndim, nop)
#define NIT_BUFFERDATA_OFFSET(itflags, ndim, nop)
#define NIT_AXISDATA_OFFSET(itflags, ndim, nop)
#define NIT_ITFLAGS(iter)   ((iter)->itflags)
#define NIT_NDIM(iter)   ((iter)->ndim)
#define NIT_NOP(iter)   ((iter)->nop)
#define NIT_ITERSIZE(iter)   (iter->itersize)
#define NIT_ITERSTART(iter)   (iter->iterstart)
#define NIT_ITEREND(iter)   (iter->iterend)
#define NIT_ITERINDEX(iter)   (iter->iterindex)
#define NIT_PERM(iter)
#define NIT_DTYPES(iter)
#define NIT_RESETDATAPTR(iter)
#define NIT_BASEOFFSETS(iter)
#define NIT_OPERANDS(iter)
#define NIT_OPITFLAGS(iter)
#define NIT_BUFFERDATA(iter)
#define NIT_AXISDATA(iter)
#define NBF_BUFFERSIZE(bufferdata)   ((bufferdata)->buffersize)
#define NBF_SIZE(bufferdata)   ((bufferdata)->size)
#define NBF_BUFITEREND(bufferdata)   ((bufferdata)->bufiterend)
#define NBF_REDUCE_POS(bufferdata)   ((bufferdata)->reduce_pos)
#define NBF_REDUCE_OUTERSIZE(bufferdata)   ((bufferdata)->reduce_outersize)
#define NBF_REDUCE_OUTERDIM(bufferdata)   ((bufferdata)->reduce_outerdim)
#define NBF_STRIDES(bufferdata)
#define NBF_PTRS(bufferdata)
#define NBF_REDUCE_OUTERSTRIDES(bufferdata)
#define NBF_REDUCE_OUTERPTRS(bufferdata)
#define NBF_READTRANSFERFN(bufferdata)
#define NBF_READTRANSFERDATA(bufferdata)
#define NBF_WRITETRANSFERFN(bufferdata)
#define NBF_WRITETRANSFERDATA(bufferdata)
#define NBF_BUFFERS(bufferdata)
#define NAD_SHAPE(axisdata)   ((axisdata)->shape)
#define NAD_INDEX(axisdata)   ((axisdata)->index)
#define NAD_STRIDES(axisdata)
#define NAD_PTRS(axisdata)
#define NAD_NSTRIDES()   ((nop) + ((itflags&NPY_ITFLAG_HASINDEX) ? 1 : 0))
#define NIT_AXISDATA_SIZEOF(itflags, ndim, nop)
#define NIT_INDEX_AXISDATA(axisdata, index)
#define NIT_ADVANCE_AXISDATA(axisdata, count)   axisdata = NIT_INDEX_AXISDATA(axisdata, count)
#define NIT_SIZEOF_ITERATOR(itflags, ndim, nop)

Typedefs

typedef struct NpyIter_AD NpyIter_AxisData
typedef struct NpyIter_BD NpyIter_BufferData

Functions

static int npyiter_check_global_flags (npy_uint32 flags, npy_uint32 *itflags)
static int npyiter_check_op_axes (int nop, int oa_ndim, int **op_axes, npy_intp *itershape)
static int npyiter_calculate_ndim (int nop, PyArrayObject **op_in, int oa_ndim)
static int npyiter_check_per_op_flags (npy_uint32 flags, char *op_itflags)
static int npyiter_prepare_one_operand (PyArrayObject **op, char **op_dataptr, PyArray_Descr *op_request_dtype, PyArray_Descr **op_dtype, npy_uint32 flags, npy_uint32 op_flags, char *op_itflags)
static int npyiter_prepare_operands (int nop, PyArrayObject **op_in, PyArrayObject **op, char **op_dataptr, PyArray_Descr **op_request_dtypes, PyArray_Descr **op_dtype, npy_uint32 flags, npy_uint32 *op_flags, char *op_itflags)
static int npyiter_check_casting (int nop, PyArrayObject **op, PyArray_Descr **op_dtype, NPY_CASTING casting, char *op_itflags)
static int npyiter_fill_axisdata (NpyIter *iter, npy_uint32 flags, char *op_itflags, char **op_dataptr, npy_uint32 *op_flags, int **op_axes, npy_intp *itershape, int output_scalars)
static void npyiter_replace_axisdata (NpyIter *iter, int iop, PyArrayObject *op, int op_ndim, char *op_dataptr, int *op_axes)
static void npyiter_compute_index_strides (NpyIter *iter, npy_uint32 flags)
static void npyiter_apply_forced_iteration_order (NpyIter *iter, NPY_ORDER order)
static void npyiter_flip_negative_strides (NpyIter *iter)
static void npyiter_reverse_axis_ordering (NpyIter *iter)
static void npyiter_find_best_axis_ordering (NpyIter *iter)
static void npyiter_coalesce_axes (NpyIter *iter)
static PyArray_Descrnpyiter_get_common_dtype (int nop, PyArrayObject **op, char *op_itflags, PyArray_Descr **op_dtype, PyArray_Descr **op_request_dtypes, int only_inputs, int output_scalars)
static PyArrayObjectnpyiter_new_temp_array (NpyIter *iter, PyTypeObject *subtype, npy_uint32 flags, char *op_itflags, int op_ndim, npy_intp *shape, PyArray_Descr *op_dtype, int *op_axes)
static int npyiter_allocate_arrays (NpyIter *iter, npy_uint32 flags, PyArray_Descr **op_dtype, PyTypeObject *subtype, npy_uint32 *op_flags, char *op_itflags, int **op_axes, int output_scalars)
static void npyiter_get_priority_subtype (int nop, PyArrayObject **op, char *op_itflags, double *subtype_priority, PyTypeObject **subtype)
static int npyiter_allocate_transfer_functions (NpyIter *iter)
static int npyiter_allocate_buffers (NpyIter *iter, char **errmsg)
static void npyiter_goto_iterindex (NpyIter *iter, npy_intp iterindex)
static void npyiter_copy_from_buffers (NpyIter *iter)
static void npyiter_copy_to_buffers (NpyIter *iter, char **prev_dataptrs)
static npy_intp npyiter_checkreducesize (NpyIter *iter, npy_intp count, npy_intp *reduce_innersize, npy_intp *reduce_outerdim)
NPY_NO_EXPORT NpyIterNpyIter_AdvancedNew (int nop, PyArrayObject **op_in, npy_uint32 flags, NPY_ORDER order, NPY_CASTING casting, npy_uint32 *op_flags, PyArray_Descr **op_request_dtypes, int oa_ndim, int **op_axes, npy_intp *itershape, npy_intp buffersize)
NPY_NO_EXPORT NpyIterNpyIter_MultiNew (int nop, PyArrayObject **op_in, npy_uint32 flags, NPY_ORDER order, NPY_CASTING casting, npy_uint32 *op_flags, PyArray_Descr **op_request_dtypes)
NPY_NO_EXPORT NpyIterNpyIter_New (PyArrayObject *op, npy_uint32 flags, NPY_ORDER order, NPY_CASTING casting, PyArray_Descr *dtype)
NPY_NO_EXPORT NpyIterNpyIter_Copy (NpyIter *iter)
NPY_NO_EXPORT int NpyIter_Deallocate (NpyIter *iter)
NPY_NO_EXPORT int NpyIter_RemoveAxis (NpyIter *iter, int axis)
NPY_NO_EXPORT int NpyIter_RemoveMultiIndex (NpyIter *iter)
NPY_NO_EXPORT int NpyIter_EnableExternalLoop (NpyIter *iter)
NPY_NO_EXPORT int NpyIter_Reset (NpyIter *iter, char **errmsg)
NPY_NO_EXPORT int NpyIter_ResetBasePointers (NpyIter *iter, char **baseptrs, char **errmsg)
NPY_NO_EXPORT int NpyIter_ResetToIterIndexRange (NpyIter *iter, npy_intp istart, npy_intp iend, char **errmsg)
NPY_NO_EXPORT int NpyIter_GotoMultiIndex (NpyIter *iter, npy_intp *multi_index)
NPY_NO_EXPORT int NpyIter_GotoIndex (NpyIter *iter, npy_intp flat_index)
NPY_NO_EXPORT int NpyIter_GotoIterIndex (NpyIter *iter, npy_intp iterindex)
NPY_NO_EXPORT npy_intp NpyIter_GetIterIndex (NpyIter *iter)
static int
npyiter_iternext_itflags
tag_itflags _dims tag_ndim
_iters 
tag_nop (NpyIter *iter)
static int npyiter_buffered_iternext (NpyIter *iter)
static int npyiter_iternext_sizeone (NpyIter *iter)
NPY_NO_EXPORT
NpyIter_IterNextFunc
NpyIter_GetIterNext (NpyIter *iter, char **errmsg)
static void
npyiter_get_multi_index_itflags 
tag_itflags (NpyIter *iter, npy_intp *out_multi_index)
NPY_NO_EXPORT
NpyIter_GetMultiIndexFunc
NpyIter_GetGetMultiIndex (NpyIter *iter, char **errmsg)
NPY_NO_EXPORT npy_bool NpyIter_HasDelayedBufAlloc (NpyIter *iter)
NPY_NO_EXPORT npy_bool NpyIter_HasExternalLoop (NpyIter *iter)
NPY_NO_EXPORT npy_bool NpyIter_HasMultiIndex (NpyIter *iter)
NPY_NO_EXPORT npy_bool NpyIter_HasIndex (NpyIter *iter)
NPY_NO_EXPORT npy_bool NpyIter_RequiresBuffering (NpyIter *iter)
NPY_NO_EXPORT npy_bool NpyIter_IterationNeedsAPI (NpyIter *iter)
NPY_NO_EXPORT int NpyIter_GetNDim (NpyIter *iter)
NPY_NO_EXPORT int NpyIter_GetNOp (NpyIter *iter)
NPY_NO_EXPORT npy_intp NpyIter_GetIterSize (NpyIter *iter)
NPY_NO_EXPORT npy_bool NpyIter_IsBuffered (NpyIter *iter)
NPY_NO_EXPORT npy_bool NpyIter_IsGrowInner (NpyIter *iter)
NPY_NO_EXPORT npy_intp NpyIter_GetBufferSize (NpyIter *iter)
NPY_NO_EXPORT void NpyIter_GetIterIndexRange (NpyIter *iter, npy_intp *istart, npy_intp *iend)
NPY_NO_EXPORT int NpyIter_GetShape (NpyIter *iter, npy_intp *outshape)
NPY_NO_EXPORT int NpyIter_CreateCompatibleStrides (NpyIter *iter, npy_intp itemsize, npy_intp *outstrides)
NPY_NO_EXPORT char ** NpyIter_GetDataPtrArray (NpyIter *iter)
NPY_NO_EXPORT char ** NpyIter_GetInitialDataPtrArray (NpyIter *iter)
NPY_NO_EXPORT PyArray_Descr ** NpyIter_GetDescrArray (NpyIter *iter)
NPY_NO_EXPORT PyArrayObject ** NpyIter_GetOperandArray (NpyIter *iter)
NPY_NO_EXPORT PyArrayObjectNpyIter_GetIterView (NpyIter *iter, npy_intp i)
NPY_NO_EXPORT npy_intpNpyIter_GetIndexPtr (NpyIter *iter)
NPY_NO_EXPORT void NpyIter_GetReadFlags (NpyIter *iter, char *outreadflags)
NPY_NO_EXPORT void NpyIter_GetWriteFlags (NpyIter *iter, char *outwriteflags)
NPY_NO_EXPORT npy_intpNpyIter_GetInnerStrideArray (NpyIter *iter)
NPY_NO_EXPORT npy_intpNpyIter_GetAxisStrideArray (NpyIter *iter, int axis)
NPY_NO_EXPORT void NpyIter_GetInnerFixedStrideArray (NpyIter *iter, npy_intp *out_strides)
NPY_NO_EXPORT npy_intpNpyIter_GetInnerLoopSizePtr (NpyIter *iter)
static const char * npyiter_casting_to_string (NPY_CASTING casting)
static PyObject * npyiter_shape_string (npy_intp n, npy_intp *vals, char *ending)
static npy_intp intp_abs (npy_intp x)
NPY_NO_EXPORT void NpyIter_DebugPrint (NpyIter *iter)

Define Documentation

#define _MULTIARRAYMODULE
#define NAD_INDEX (   axisdata)    ((axisdata)->index)

Referenced by npyiter_coalesce_axes().

#define NAD_NSTRIDES ( )    ((nop) + ((itflags&NPY_ITFLAG_HASINDEX) ? 1 : 0))
#define NAD_PTRS (   axisdata)
Value:
((char **) \
        &(axisdata)->ad_flexdata + 1*(nop+1))

Referenced by npyiter_check_casting(), npyiter_goto_iterindex(), and NpyIter_GotoMultiIndex().

#define NAD_SHAPE (   axisdata)    ((axisdata)->shape)

Referenced by npyiter_coalesce_axes().

#define NAD_STRIDES (   axisdata)
Value:
( \
        &(axisdata)->ad_flexdata + 0)

Referenced by npyiter_coalesce_axes(), NpyIter_GotoMultiIndex(), npyiter_shape_string(), and tag_itflags().

#define NBF_BUFFERS (   bufferdata)
Value:
((char **) \
        (&(bufferdata)->bd_flexdata + 8*(nop)))
#define NBF_BUFFERSIZE (   bufferdata)    ((bufferdata)->buffersize)
#define NBF_BUFITEREND (   bufferdata)    ((bufferdata)->bufiterend)
#define NBF_PTRS (   bufferdata)
Value:
((char **) \
        (&(bufferdata)->bd_flexdata + 1*(nop)))

Referenced by npyiter_iternext_sizeone().

#define NBF_READTRANSFERDATA (   bufferdata)
Value:
((void **) \
        (&(bufferdata)->bd_flexdata + 5*(nop)))
#define NBF_READTRANSFERFN (   bufferdata)
Value:
((PyArray_StridedTransferFn **) \
        (&(bufferdata)->bd_flexdata + 4*(nop)))
#define NBF_REDUCE_OUTERDIM (   bufferdata)    ((bufferdata)->reduce_outerdim)
#define NBF_REDUCE_OUTERPTRS (   bufferdata)
Value:
((char **) \
        (&(bufferdata)->bd_flexdata + 3*(nop)))
#define NBF_REDUCE_OUTERSIZE (   bufferdata)    ((bufferdata)->reduce_outersize)

Referenced by npyiter_coalesce_axes().

#define NBF_REDUCE_OUTERSTRIDES (   bufferdata)
Value:
( \
        (&(bufferdata)->bd_flexdata + 2*(nop)))
#define NBF_REDUCE_POS (   bufferdata)    ((bufferdata)->reduce_pos)
#define NBF_SIZE (   bufferdata)    ((bufferdata)->size)

Referenced by npyiter_coalesce_axes().

#define NBF_STRIDES (   bufferdata)
Value:
( \
        &(bufferdata)->bd_flexdata + 0)
#define NBF_WRITETRANSFERDATA (   bufferdata)
Value:
((void **) \
        (&(bufferdata)->bd_flexdata + 7*(nop)))
#define NBF_WRITETRANSFERFN (   bufferdata)
Value:
((PyArray_StridedTransferFn **) \
        (&(bufferdata)->bd_flexdata + 6*(nop)))
#define NIT_ADVANCE_AXISDATA (   axisdata,
  count 
)    axisdata = NIT_INDEX_AXISDATA(axisdata, count)

Referenced by tag_itflags().

#define NIT_AXISDATA (   iter)
Value:
((NpyIter_AxisData *)( \
        &(iter)->iter_flexdata + NIT_AXISDATA_OFFSET(itflags, ndim, nop)))

Referenced by tag_itflags().

#define NIT_AXISDATA_OFFSET (   itflags,
  ndim,
  nop 
)
Value:
(NIT_BUFFERDATA_OFFSET(itflags, ndim, nop) + \
         NIT_BUFFERDATA_SIZEOF(itflags, ndim, nop))
#define NIT_AXISDATA_SIZEOF (   itflags,
  ndim,
  nop 
)
Value:
(( \ \
        1 + \ \
        1 + \ \
        2*((nop)+1) \
        )*NPY_SIZEOF_INTP )
Size of one AXISDATA struct within the iterator

Referenced by tag_itflags().

#define NIT_BASEOFFSETS (   iter)
Value:
((npy_intp *)( \
        &(iter)->iter_flexdata + NIT_BASEOFFSETS_OFFSET(itflags, ndim, nop)))
#define NIT_BASEOFFSETS_OFFSET (   itflags,
  ndim,
  nop 
)
Value:
(NIT_RESETDATAPTR_OFFSET(itflags, ndim, nop) + \
         NIT_RESETDATAPTR_SIZEOF(itflags, ndim, nop))
#define NIT_BASEOFFSETS_SIZEOF (   itflags,
  ndim,
  nop 
)    ((NPY_SIZEOF_INTP)*(nop+1))
#define NIT_BUFFERDATA (   iter)
Value:
((NpyIter_BufferData *)( \
        &(iter)->iter_flexdata + NIT_BUFFERDATA_OFFSET(itflags, ndim, nop)))

Referenced by npyiter_iternext_sizeone().

#define NIT_BUFFERDATA_OFFSET (   itflags,
  ndim,
  nop 
)
Value:
(NIT_OPITFLAGS_OFFSET(itflags, ndim, nop) + \
         NIT_OPITFLAGS_SIZEOF(itflags, ndim, nop))
#define NIT_BUFFERDATA_SIZEOF (   itflags,
  ndim,
  nop 
)    ((itflags&NPY_ITFLAG_BUFFER) ? ((NPY_SIZEOF_INTP)*(6 + 9*nop)) : 0)
#define NIT_DTYPES (   iter)
Value:
((PyArray_Descr **)( \
        &(iter)->iter_flexdata + NIT_DTYPES_OFFSET(itflags, ndim, nop)))
#define NIT_DTYPES_OFFSET (   itflags,
  ndim,
  nop 
)
Value:
(NIT_PERM_OFFSET() + \
         NIT_PERM_SIZEOF(itflags, ndim, nop))
#define NIT_DTYPES_SIZEOF (   itflags,
  ndim,
  nop 
)    ((NPY_SIZEOF_INTP)*(nop))
#define NIT_INDEX_AXISDATA (   axisdata,
  index 
)
Value:
((NpyIter_AxisData *) \
        (((char *)(axisdata)) + (index)*sizeof_axisdata))
Macro to advance an AXISDATA pointer by a specified count. Requires that sizeof_axisdata be previously initialized to NIT_AXISDATA_SIZEOF(itflags, ndim, nop).

Referenced by npyiter_shape_string(), and tag_itflags().

#define NIT_ITEREND (   iter)    (iter->iterend)
#define NIT_ITERINDEX (   iter)    (iter->iterindex)
#define NIT_ITERSIZE (   iter)    (iter->itersize)
#define NIT_ITERSTART (   iter)    (iter->iterstart)
#define NIT_ITFLAGS (   iter)    ((iter)->itflags)
Internal-only ITERATOR DATA MEMBER ACCESS

Referenced by tag_itflags().

#define NIT_NDIM (   iter)    ((iter)->ndim)

Referenced by tag_itflags().

#define NIT_NOP (   iter)    ((iter)->nop)

Referenced by NpyIter_GetIterNext(), and tag_itflags().

#define NIT_OPERANDS (   iter)
Value:
((PyArrayObject **)( \
        &(iter)->iter_flexdata + NIT_OPERANDS_OFFSET(itflags, ndim, nop)))
#define NIT_OPERANDS_OFFSET (   itflags,
  ndim,
  nop 
)
Value:
(NIT_BASEOFFSETS_OFFSET(itflags, ndim, nop) + \
         NIT_BASEOFFSETS_SIZEOF(itflags, ndim, nop))
#define NIT_OPERANDS_SIZEOF (   itflags,
  ndim,
  nop 
)    ((NPY_SIZEOF_INTP)*(nop))
#define NIT_OPITFLAGS (   iter)
Value:
( \
        &(iter)->iter_flexdata + NIT_OPITFLAGS_OFFSET(itflags, ndim, nop))
#define NIT_OPITFLAGS_OFFSET (   itflags,
  ndim,
  nop 
)
Value:
(NIT_OPERANDS_OFFSET(itflags, ndim, nop) + \
         NIT_OPERANDS_SIZEOF(itflags, ndim, nop))
#define NIT_OPITFLAGS_SIZEOF (   itflags,
  ndim,
  nop 
)    (NPY_INTP_ALIGNED(nop))
#define NIT_PERM (   iter)
Value:
((char*)( \
        &(iter)->iter_flexdata + NIT_PERM_OFFSET()))

Referenced by tag_itflags().

#define NIT_PERM_OFFSET ( )    (0)
Byte offsets of the iterator members starting from iter->iter_flexdata
#define NIT_PERM_SIZEOF (   itflags,
  ndim,
  nop 
)    NPY_INTP_ALIGNED(NPY_MAXDIMS)
Byte sizes of the iterator members
#define NIT_RESETDATAPTR (   iter)
Value:
((char **)( \
        &(iter)->iter_flexdata + NIT_RESETDATAPTR_OFFSET(itflags, ndim, nop)))

Referenced by NpyIter_GetIterNext().

#define NIT_RESETDATAPTR_OFFSET (   itflags,
  ndim,
  nop 
)
Value:
(NIT_DTYPES_OFFSET(itflags, ndim, nop) + \
         NIT_DTYPES_SIZEOF(itflags, ndim, nop))
#define NIT_RESETDATAPTR_SIZEOF (   itflags,
  ndim,
  nop 
)    ((NPY_SIZEOF_INTP)*(nop+1))
#define NIT_SIZEOF_ITERATOR (   itflags,
  ndim,
  nop 
)
Value:
Size of the whole iterator
#define NPY_INTP_ALIGNED (   size)    ((size + 0x7)&(-0x8))
System Message: ERROR/3 (<string>, line 1) Document or section may not begin with a transition.

System Message: ERROR/3 (<string>, line 1) Document may not end with a transition.
Rounds up a number of bytes to be divisible by sizeof intp
#define NPY_IT_CONSTRUCTION_TIMING   0
**** ITERATOR CONSTRUCTION TIMING *********
#define NPY_IT_DBG_PRINT (   s)
#define NPY_IT_DBG_PRINT1 (   s,
  p1 
)

Referenced by npyiter_coalesce_axes().

#define NPY_IT_DBG_PRINT2 (   s,
  p1,
  p2 
)

Referenced by npyiter_coalesce_axes().

#define NPY_IT_DBG_PRINT3 (   s,
  p1,
  p2,
  p3 
)
#define NPY_IT_DBG_TRACING   0
System Message: ERROR/3 (<string>, line 1) Document or section may not begin with a transition.

System Message: ERROR/3 (<string>, line 1) Document may not end with a transition.
**** PRINTF DEBUG TRACING *********
#define NPY_IT_TIME_POINT (   var)
#define NPY_ITFLAG_BUFFER   0x0080
The iterator is buffered
#define NPY_ITFLAG_DELAYBUF   0x0400
Delay buffer allocation until first Reset* call
#define NPY_ITFLAG_EXLOOP   0x0020
The inner loop is handled outside the iterator
#define NPY_ITFLAG_FORCEDORDER   0x0010
The iteration order was forced on construction
#define NPY_ITFLAG_GROWINNER   0x0100
The iterator should grow the buffered inner loop when possible

Referenced by NpyIter_RequiresBuffering().

#define NPY_ITFLAG_HASINDEX   0x0004
The iterator is tracking an index
#define NPY_ITFLAG_HASMULTIINDEX   0x0008
The iterator is tracking a multi-index
#define NPY_ITFLAG_IDENTPERM   0x0001
Internal iterator flags
The perm is the identity
#define NPY_ITFLAG_NEEDSAPI   0x0800
Iteration needs API access during iternext
#define NPY_ITFLAG_NEGPERM   0x0002
The perm has negative entries (indicating flipped axes)
#define NPY_ITFLAG_ONEITERATION   0x0200
There is just one iteration, can specialize iternext for that
#define NPY_ITFLAG_RANGE   0x0040
The iterator is ranged
#define NPY_ITFLAG_REDUCE   0x1000
Iteration includes one or more operands being reduced
#define NPY_ITFLAG_REUSE_REDUCE_LOOPS   0x2000
Reduce iteration doesn't need to recalculate reduce loops next time
#define NPY_OP_ITFLAG_ALIGNED   0x10
The operand is aligned
#define NPY_OP_ITFLAG_BUFNEVER   0x08
The operand never needs buffering
#define NPY_OP_ITFLAG_CAST   0x04
The operand needs type conversion/byte swapping/alignment
#define NPY_OP_ITFLAG_READ   0x02
The operand will be read from

Referenced by NpyIter_GetReadFlags().

#define NPY_OP_ITFLAG_REDUCE   0x20
The operand is being reduced
#define NPY_OP_ITFLAG_WRITE   0x01
Internal iterator per-operand iterator flags
The operand will be written to
#define PY_SSIZE_T_CLEAN

Typedef Documentation

typedef struct NpyIter_AD NpyIter_AxisData

Function Documentation

static npy_intp intp_abs ( npy_intp  x) [static]

Referenced by npyiter_shape_string().

NPY_NO_EXPORT NpyIter* NpyIter_AdvancedNew ( int  nop,
PyArrayObject **  op_in,
npy_uint32  flags,
NPY_ORDER  order,
NPY_CASTING  casting,
npy_uint32 *  op_flags,
PyArray_Descr **  op_request_dtypes,
int  oa_ndim,
int **  op_axes,
npy_intp itershape,
npy_intp  buffersize 
)
Allocate a new iterator for multiple array objects, and advanced
options for controlling the broadcasting, shape, and buffer size.

The iterator being constructed
Per-operand values
The subtype for automatically allocated outputs
Error check 'oa_ndim' and 'op_axes', which must be used together
Check the global iterator flags
Calculate how many dimensions the iterator should have
If 'ndim' is zero, any outputs should be scalars
Allocate memory for the iterator
Fill in the basic data
Prepare all the operands
Set resetindex to zero as well (it's just after the resetdataptr)
Initialize buffer data (must set the buffers and transferdata to NULL before we might deallocate the iterator).
Fill in the AXISDATA arrays and set the ITERSIZE field
If buffering is enabled and no buffersize was given, use a default chosen to be big enough to get some amortization benefits, but small enough to be cache-friendly.
No point in a buffer bigger than the iteration size
If an index was requested, compute the strides for it. Note that we must do this before changing the order of the axes
Initialize the perm to the identity
If an iteration order is being forced, apply it.
Set some flags for allocated outputs
Flag this so later we can avoid flipping axes
If a subtype may be used, indicate so
If the data type wasn't provided, will need to calculate it.
If the ordering was not forced, reorder the axes and flip negative strides to find the best one.
If there's an output being allocated, we must not negate any strides.
If an automatically allocated output didn't have a specified dtype, we need to figure it out now, before allocating the outputs.
Replace all the data types
Replace the NULL data types
All of the data types have been settled, so it's time to check that data type conversions are following the casting rules.
At this point, the iteration order has been finalized. so any allocation of ops that were NULL, or any temporary copying due to casting/byte order/alignment can be done now using a memory layout matching the iterator.
Finally, if a multi-index wasn't requested, it may be possible to coalesce some axes together.
The operation may have changed the layout, so we have to get the internal pointers again.
Now that the axes are finished, check whether we can apply the single iteration optimization to the iternext function.
If REFS_OK was specified, check whether there are any reference arrays and flag it if so.
Iteration needs API access
If buffering is set without delayed allocation
Make the data pointers NULL
Allocate the buffers
Prepare the next buffers and set iterend/size

static int npyiter_allocate_arrays ( NpyIter iter,
npy_uint32  flags,
PyArray_Descr **  op_dtype,
PyTypeObject *  subtype,
npy_uint32 *  op_flags,
char *  op_itflags,
int **  op_axes,
int  output_scalars 
) [static]

NULL means an output the iterator should allocate
Check whether the subtype was disabled
Allocate the output array
Now we need to replace the pointers and strides with values from the new array.
New arrays are aligned and need no cast
If casting is required, the operand is read-only, and it's an array scalar, make a copy whether or not the copy flag is enabled.
Now we need to replace the pointers and strides with values from the temporary array.
New arrays are aligned need no cast, and in the case of scalars, always have stride 0 so never need buffering
If casting is required and permitted
Allocate the temporary array, if possible
If the data will be read, copy it into temp
If the data will be written to, set UPDATEIFCOPY
Now we need to replace the pointers and strides with values from the temporary array.
The temporary copy is aligned and needs no cast
Buffering must be enabled for casting/conversion if copy wasn't specified.
If the operand is aligned, any buffering can use aligned optimizations.
Here we can finally check for contiguous iteration
If no alignment, byte swap, or casting is needed, and the inner stride of this operand works for the whole array, we can set NPY_OP_ITFLAG_BUFNEVER.
Find stride of the first non-empty shape
Check that everything could have coalesced together
If N times the inner stride doesn't equal this stride, the multi-dimensionality is needed.
If we looped all the way to the end, one stride works. Set that stride, because it may not belong to the first dimension.

static int npyiter_allocate_buffers ( NpyIter iter,
char **  errmsg 
) [static]
If errmsg is non-NULL, it should point to a variable which will receive the error message, and no Python exception will be set. This is so that the function can be called from code not holding the GIL.

npy_uint32 itflags = NIT_ITFLAGS(iter);
int ndim = NIT_NDIM(iter);
If we have determined that a buffer may be needed, allocate one.

static int npyiter_allocate_transfer_functions ( NpyIter iter) [static]

int ndim = NIT_NDIM(iter);
Reduction operands may be buffered with a different stride, so we must pass NPY_MAX_INTP to the transfer function factory.
If we have determined that a buffer may be needed, allocate the appropriate transfer functions
If no write back but there are references make a decref fn
By passing NULL to dst_type and setting move_references to 1, we get back a function that just decrements the src references.
If any of the dtype transfer functions needed the API, flag it

static void npyiter_apply_forced_iteration_order ( NpyIter iter,
NPY_ORDER  order 
) [static]
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.

npy_uint32 itflags = NIT_ITFLAGS(iter);
Only need to actually do something if there is more than 1 dim
Only need to actually do something if there is more than 1 dim
Check that all the array inputs are fortran order
Don't set the forced order flag here...

References _PyArray_Descr::byteorder, NPY_IT_DBG_PRINT, NPY_MAXARGS, NPY_NATIVE, PyArray_DescrNewByteorder(), PyArray_ISNBO, PyArray_NDIM, and PyArray_ResultType().

static int npyiter_buffered_iternext ( NpyIter iter) [static]
end repeat*
iternext function that handles the buffering part

int ndim = NIT_NDIM(iter);
If the iterator handles the inner loop, need to increment all the indices and pointers
Increment within the buffer
Write back to the arrays
Check if we're past the end
Increment to the next buffer
Prepare the next buffers and set iterend/size

static int npyiter_calculate_ndim ( int  nop,
PyArrayObject **  op_in,
int  oa_ndim 
) [static]

If 'op_axes' is being used, force 'ndim'
Otherwise it's the maximum 'ndim' from the operands

static const char* npyiter_casting_to_string ( NPY_CASTING  casting) [static]

Referenced by NpyIter_GetReadFlags().

static int npyiter_check_casting ( int  nop,
PyArrayObject **  op,
PyArray_Descr **  op_dtype,
NPY_CASTING  casting,
char *  op_itflags 
) [static]

If the types aren't equivalent, a cast is necessary
Check read (op -> temp) casting
Check write (temp -> op) casting
Indicate that this operand needs casting

References NAD_PTRS.

static int npyiter_check_global_flags ( npy_uint32  flags,
npy_uint32 *  itflags 
) [static]
Internal helper functions
Checks 'flags' for (C|F)_ORDER_INDEX, MULTI_INDEX, and EXTERNAL_LOOP, setting the appropriate internal flags in 'itflags'.
Returns 1 on success, 0 on error.

Check for an index
Check if a multi-index was requested
This flag primarily disables dimension manipulations that would produce an incorrect multi-index.
Check if the caller wants to handle inner iteration
Ranged
Buffering

static int npyiter_check_op_axes ( int  nop,
int  oa_ndim,
int **  op_axes,
npy_intp itershape 
) [static]

Check that there are no duplicates in op_axes

static int npyiter_check_per_op_flags ( npy_uint32  op_flags,
char *  op_itflags 
) [static]
Checks the per-operand input flags, and fills in op_itflags.
Returns 1 on success, 0 on failure.

Check the read/write flags
The read/write flags are mutually exclusive
The read/write flags are mutually exclusive
Check the flags for temporary copies

Referenced by NpyIter_GetIterView().

static npy_intp npyiter_checkreducesize ( NpyIter iter,
npy_intp  count,
npy_intp reduce_innersize,
npy_intp reduce_outerdim 
) [static]
This checks how much space can be buffered without encountering the same value twice, or for operands whose innermost stride is zero, without encountering a different value. By reducing the buffered amount to this size, reductions can be safely buffered.
Reductions are buffered with two levels of looping, to avoid frequent copying to the buffers. The return value is the over-all buffer size, and when the flag NPY_ITFLAG_REDUCE is set, reduce_innersize receives the size of the inner of the two levels of looping.
The value placed in reduce_outerdim is the index into the AXISDATA for where the second level of the double loop begins.
The return value is always a multiple of the value placed in reduce_innersize.

Default to no outer axis
If there's only one dimension, no need to calculate anything
Indicate which REDUCE operands have stride 0 in the inner loop
Go forward through axisdata, calculating the space available
If a reduce stride switched from zero to non-zero, or vice versa, that's the point where the data will stop being the same element or will repeat, and if the buffer starts with an all zero multi-index up to this point, gives us the reduce_innersize.
If we already found more elements than count, or the starting coordinate wasn't zero, the two-level looping is unnecessary/can't be done, so return.
If we broke out of the loop early, we found reduce_innersize
If there was any non-zero coordinate, can't do the double loop
In this case, we can't reuse the reduce loops
In this case, we can reuse the reduce loops
Continue through the rest of the dimensions. If there are two separated reduction axes, we may have to cut the buffer short again.
Indicate which REDUCE operands have stride 0 at the current level
If a reduce stride switched from zero to non-zero, or vice versa, that's the point where the data will stop being the same element or will repeat, and if the buffer starts with an all zero multi-index up to this point, gives us the reduce_innersize.
This terminates the outer level of our double loop.

static void npyiter_coalesce_axes ( NpyIter iter) [static]

The HASMULTIINDEX or IDENTPERM flags do not apply after coalescing
Check that all the axes can be coalesced
If the number of axes shrunk, reset the perm and compress the data into the new layout.
Reset to an identity perm

References NAD_INDEX, NAD_SHAPE, NAD_STRIDES, NBF_REDUCE_OUTERSIZE, NBF_SIZE, NPY_IT_DBG_PRINT1, NPY_IT_DBG_PRINT2, and PyArray_TransferStridedToNDim().

static void npyiter_compute_index_strides ( NpyIter iter,
npy_uint32  flags 
) [static]
Computes the iterator's index strides and initializes the index values to zero.
This must be called before the axes (i.e. the AXISDATA array) may be reordered.

If there is only one element being iterated, we just have to touch the first AXISDATA because nothing will ever be incremented.

NPY_NO_EXPORT NpyIter* NpyIter_Copy ( NpyIter iter)
Makes a copy of the iterator

Allocate memory for the new iterator
Copy the raw values to the new iterator
Take ownership of references to the operands and dtypes
Allocate buffers and make copies of the transfer data if necessary
Initialize the buffers to the current iterindex
Prepare the next buffers and set iterend/size

static void npyiter_copy_from_buffers ( NpyIter iter) [static]
This gets called after the the buffers have been exhausted, and their data needs to be written back to the arrays. The multi-index must be positioned for the beginning of the buffer.

If we're past the end, nothing to copy
Copy the data back to the arrays. If the type has refs, this function moves them so the buffer's refs are released.
Copy back only if the pointer was pointing to the buffer
If this operand is being reduced in the inner loop, its buffering stride was set to zero, and just one element was copied.
If there's no copy back, we may have to decrement refs. In this case, the transfer function has a 'decsrcref' transfer function, so we can use it to do the decrement.
Decrement refs only if the pointer was pointing to the buffer
Decrement refs
Zero out the memory for safety. For instance, if during iteration some Python code copied an array pointing into the buffer, it will get None values for its references after this.

static void npyiter_copy_to_buffers ( NpyIter iter,
char **  prev_dataptrs 
) [static]
This gets called after the iterator has been positioned to a multi-index for the start of a buffer. It decides which operands need a buffer, and copies the data into the buffers.

Have to get this flag before npyiter_checkreducesize sets it for the next iteration.
Calculate the size if using any buffers
If last time around, the reduce loop structure was full, we reuse it
If there are any reduction operands, we may have to make the size smaller so we don't copy the same value into a buffer twice, as the buffering does not have a mechanism to combine values itself.
Calculate the maximum size if using a single stride and no buffers
If the buffer is write-only, these two are NULL, and the buffer pointers will be set up but the read copy won't be done
Never need to buffer this operand
Should not adjust the stride - ad_strides[iop] could be zero, but strides[iop] was initialized to the first non-trivial stride.
Never need to buffer this operand
Should not adjust the stride - ad_strides[iop] could be zero, but strides[iop] was initialized to the first non-trivial stride.
Just a copy
No copyswap or cast was requested, so all we're doing is copying the data to fill the buffer and produce a single stride. If the underlying data already does that, no need to copy it.
If some other op is reduced, we have a double reduce loop
In this case, the buffer is being used
Just a copy, but with a reduction
It's all in one stride in the inner loop dimension
It's all in one stride in the reduce outer loop
Outer reduce loop advances by one item
In this case, the buffer is being used
Both outer and inner reduce loops have stride 0
Outer reduce loop advances by one item
It's all in one stride in the reduce outer loop
Outer reduce loop advances by one item
In this case, the buffer is being used
Reduction in outer reduce loop
Advance to next items in outer reduce loop
In this case, the buffer is being used
The buffer is being used with reduction
Both outer and inner reduce loops have stride 0
Outer reduce loop advances by one item
Reduction in outer reduce loop
Advance to next items in outer reduce loop
If this operand is being reduced in the inner loop, set its buffering stride to zero, and just copy one element.
When we're reducing a single element, and it's still the same element, don't overwrite it even when reuse reduce loops is unset. This preserves the precision of the intermediate calculation.
If the whole buffered loop structure remains the same, and the source pointer for this data didn't change, we don't have to copy the data again.
If the data type requires zero-inititialization
Can't skip the transfer in this case
If the data type requires zero-inititialization
If buffering wasn't needed, we can grow the inner loop to as large as possible.
TODO: Could grow REDUCE loop too with some more logic above.

NPY_NO_EXPORT int NpyIter_CreateCompatibleStrides ( NpyIter iter,
npy_intp  itemsize,
npy_intp outstrides 
)
Builds a set of strides which are the same as the strides of an

output array created using the NPY_ITER_ALLOCATE flag, where NULL was passed for op_axes. This is for data packed contiguously, but not necessarily in C or Fortran order. This should be used together with NpyIter_GetShape and NpyIter_GetNDim.

A use case for this function is to match the shape and layout of the iterator and tack on one or more dimensions. For example, in order to generate a vector per input value for a numerical gradient, you pass in ndim*itemsize for itemsize, then add another dimension to the end with size ndim and stride itemsize. To do the Hessian matrix, you do the same thing but add two dimensions, or take advantage of the symmetry and pack it into 1 dimension with a particular encoding.

This function may only be called if the iterator is tracking a multi-index and if NPY_ITER_DONT_NEGATE_STRIDES was used to prevent an axis from being iterated in reverse order.

If an array is created with this method, simply adding 'itemsize' for each iteration will traverse the new array matching the iterator.

Returns NPY_SUCCEED or NPY_FAIL.

NPY_NO_EXPORT int NpyIter_Deallocate ( NpyIter iter)
Deallocate an iterator

int ndim = NIT_NDIM(iter);
Deallocate any buffers and buffering data
buffers
read bufferdata
write bufferdata
Deallocate all the dtypes and objects that were iterated
Deallocate the iterator memory

Referenced by PyArray_EnsureArray().

NPY_NO_EXPORT void NpyIter_DebugPrint ( NpyIter iter)
For debugging

Print the fixed strides when there's no inner loop

NPY_NO_EXPORT int NpyIter_EnableExternalLoop ( NpyIter iter)
Removes the inner loop handling (so HasExternalLoop returns true)

int ndim = NIT_NDIM(iter);
Check conditions under which this can be done
Set the flag
Check whether we can apply the single iteration optimization to the iternext function.
Reset the iterator

References NPY_FAIL.

static int npyiter_fill_axisdata ( NpyIter iter,
npy_uint32  flags,
char *  op_itflags,
char **  op_dataptr,
npy_uint32 *  op_flags,
int **  op_axes,
npy_intp itershape,
int  output_scalars 
) [static]
Fills in the AXISDATA for the 'nop' operands, broadcasting the dimensionas as necessary. Also fills in the ITERSIZE data member.
If op_axes is not NULL, it should point to an array of ndim-sized arrays, one for each op.
Returns 1 on success, 0 on failure.

First broadcast the shapes together
Negative shape entries are deduced from the operands
Possible if op_axes are being used, but op_axes[iop] is NULL
If a shape was provided with a 1 entry, make sure that entry didn't get expanded by broadcasting.
Now process the operands, filling in the axisdata
If it's writeable, this means a reduction
If it's writeable, this means a reduction
If it's writeable, this means a reduction
Now fill in the ITERSIZE member
The range defaults to everything
Start of error message
Operand shape
Remapped operand shape
Fill in the broadcast shape
Broadcast shape

static void npyiter_find_best_axis_ordering ( NpyIter iter) [static]

Do a custom stable insertion sort. Note that because the AXISDATA has been reversed from C order, this is sorting from smallest stride to biggest stride.
'ax_ipos' is where perm[ax_i0] will get inserted
Set swap even if it's not ambiguous already, because in the case of conflicts between different operands, C-order wins.
Only set swap if it's still ambiguous
A comparison has been done, so it's no longer ambiguous
If the comparison was unambiguous, either shift 'ax_ipos' to 'ax_i1' or stop looking for an insertion point
Insert perm[ax_i0] into the right place
Apply the computed permutation to the AXISDATA array
Use the index as a flag, set each to 1
Apply the permutation by following the cycles
If this axis hasn't been touched yet, process it
Follow the cycle, copying the data
Follow the cycle again, marking it as done
Clear the identity perm flag

References _PyArray_Descr::elsize, and PyArray_malloc.

static void npyiter_flip_negative_strides ( NpyIter iter) [static]
This function negates any strides in the iterator which are negative. When iterating more than one object, it only flips strides when they are all negative or zero.

Check the signs of all the strides, excluding the index stride at the end.
If at least on stride is negative and none are positive, flip all the strides for this dimension.
Adjust the base pointers to start at the end
Flip the stride
Make the perm entry negative so get_multi_index knows it's flipped
If any strides were flipped, the base pointers were adjusted in the first AXISDATA, and need to be copied to all the rest
Indicate that some of the perm entries are negative, and that it's not (strictly speaking) the identity perm.

static PyArray_Descr * npyiter_get_common_dtype ( int  nop,
PyArrayObject **  op,
char *  op_itflags,
PyArray_Descr **  op_dtype,
PyArray_Descr **  op_request_dtypes,
int  only_inputs,
int  output_scalars 
) [static]
Calculates a dtype that all the types can be promoted to, using the ufunc rules. If only_inputs is 1, it leaves any operands that are not read from out of the calculation.

If no dtype was requested and the op is a scalar, pass the op
Otherwise just pass in the dtype

static void npyiter_get_priority_subtype ( int  nop,
PyArrayObject **  op,
char *  op_itflags,
double *  subtype_priority,
PyTypeObject **  subtype 
) [static]
The __array_priority__ attribute of the inputs determines the subtype of any output arrays. This function finds the subtype of the input array with highest priority.
NPY_NO_EXPORT npy_intp* NpyIter_GetAxisStrideArray ( NpyIter iter,
int  axis 
)
Gets the array of strides for the specified axis.

If the iterator is tracking a multi-index, gets the strides for the axis specified, otherwise gets the strides for the iteration axis as Fortran order (fastest-changing axis first).

Returns NULL if an error occurs.

Reverse axis, since the iterator treats them that way
First find the axis in question

NPY_NO_EXPORT npy_intp NpyIter_GetBufferSize ( NpyIter iter)
Gets the size of the buffer, or 0 if buffering is not enabled

int ndim = NIT_NDIM(iter);

NPY_NO_EXPORT char** NpyIter_GetDataPtrArray ( NpyIter iter)
Get the array of data pointers (1 per object being iterated) <blockquote> This function may be safely called without holding the Python GIL.</blockquote>

int ndim = NIT_NDIM(iter);

References _PyArray_Descr::elsize, NPY_STRING, NPY_UNICODE, PyArray_DESCR_REPLACE, and _PyArray_Descr::type_num.

NPY_NO_EXPORT PyArray_Descr** NpyIter_GetDescrArray ( NpyIter iter)
Get the array of data type pointers (1 per object being iterated)

npy_uint32 itflags = NIT_ITFLAGS(iter);
int ndim = NIT_NDIM(iter);
int nop = NIT_NOP(iter);

NPY_NO_EXPORT NpyIter_GetMultiIndexFunc* NpyIter_GetGetMultiIndex ( NpyIter iter,
char **  errmsg 
)
end repeat*
Compute a specialized get_multi_index function for the iterator <blockquote> If errmsg is non-NULL, it should point to a variable which will receive the error message, and no Python exception will be set. This is so that the function can be called from code not holding the GIL.</blockquote>

These flags must be correct
Only these flags affect the iterator memory layout or the get_multi_index behavior. IDENTPERM and NEGPERM are mutually exclusive, so that reduces the number of cases slightly.
begin repeat
#const_itflags = 0,
NPY_ITFLAG_HASINDEX, NPY_ITFLAG_IDENTPERM, NPY_ITFLAG_HASINDEX|NPY_ITFLAG_IDENTPERM, NPY_ITFLAG_NEGPERM, NPY_ITFLAG_HASINDEX|NPY_ITFLAG_NEGPERM, NPY_ITFLAG_BUFFER, NPY_ITFLAG_HASINDEX|NPY_ITFLAG_BUFFER, NPY_ITFLAG_IDENTPERM|NPY_ITFLAG_BUFFER, NPY_ITFLAG_HASINDEX|NPY_ITFLAG_IDENTPERM|NPY_ITFLAG_BUFFER, NPY_ITFLAG_NEGPERM|NPY_ITFLAG_BUFFER, NPY_ITFLAG_HASINDEX|NPY_ITFLAG_NEGPERM|NPY_ITFLAG_BUFFER#
tag_itflags = 0, IND, IDP, INDuIDP, NEGP, INDuNEGP,
BUF, INDuBUF, IDPuBUF, INDuIDPuBUF, NEGPuBUF, INDuNEGPuBUF#
end repeat*
The switch above should have caught all the possibilities.

NPY_NO_EXPORT npy_intp* NpyIter_GetIndexPtr ( NpyIter iter)
Get a pointer to the index, if it is being tracked

int ndim = NIT_NDIM(iter);
The index is just after the data pointers

NPY_NO_EXPORT char** NpyIter_GetInitialDataPtrArray ( NpyIter iter)
Get the array of data pointers (1 per object being iterated),

directly into the arrays (never pointing to a buffer), for starting unbuffered iteration. This always returns the addresses for the iterator position as reset to iterator index 0.

These pointers are different from the pointers accepted by NpyIter_ResetBasePointers, because the direction along some axes may have been reversed, requiring base offsets.

This function may be safely called without holding the Python GIL.

npy_uint32 itflags = NIT_ITFLAGS(iter);
int ndim = NIT_NDIM(iter);

NPY_NO_EXPORT void NpyIter_GetInnerFixedStrideArray ( NpyIter iter,
npy_intp out_strides 
)
Get an array of strides which are fixed. Any strides which may

change during iteration receive the value NPY_MAX_INTP. Once the iterator is ready to iterate, call this to get the strides which will always be fixed in the inner loop, then choose optimized inner loop functions which take advantage of those fixed strides.

This function may be safely called without holding the Python GIL.

Operands which are always/never buffered have fixed strides, and everything has fixed strides when ndim is 0 or 1
If it's a reduction, 0-stride inner loop may have fixed stride
If it's a reduction operand, definitely fixed stride
Otherwise it's a fixed stride if the stride is 0 for all inner dimensions of the reduction double loop
If all the strides were 0, the stride won't change
Inner loop contiguous array means its stride won't change when switching between buffering and not buffering
Otherwise the strides can change if the operand is sometimes buffered, sometimes not.
If there's no buffering, the strides are always fixed

NPY_NO_EXPORT npy_intp* NpyIter_GetInnerLoopSizePtr ( NpyIter iter)
Get a pointer to the size of the inner loop (when HasExternalLoop is true) <blockquote> This function may be safely called without holding the Python GIL.</blockquote>

int ndim = NIT_NDIM(iter);

NPY_NO_EXPORT npy_intp* NpyIter_GetInnerStrideArray ( NpyIter iter)
Get the array of strides for the inner loop (when HasExternalLoop is true) <blockquote> This function may be safely called without holding the Python GIL.</blockquote>

int ndim = NIT_NDIM(iter);

References PyUString_FromFormat.

NPY_NO_EXPORT npy_intp NpyIter_GetIterIndex ( NpyIter iter)
Gets the current iteration index

iterindex is only used if NPY_ITER_RANGED or NPY_ITER_BUFFERED was set

References tag_itflags(), and tag_nop().

NPY_NO_EXPORT void NpyIter_GetIterIndexRange ( NpyIter iter,
npy_intp istart,
npy_intp iend 
)
Gets the range of iteration indices being iterated
NPY_NO_EXPORT NpyIter_IterNextFunc* NpyIter_GetIterNext ( NpyIter iter,
char **  errmsg 
)
Compute the specialized iteration function for an iterator <blockquote> If errmsg is non-NULL, it should point to a variable which will receive the error message, and no Python exception will be set. This is so that the function can be called from code not holding the GIL.</blockquote>

When there is just one iteration and buffering is disabled the iternext function is very simple.
If buffering is enabled.
Ignore all the flags that don't affect the iterator memory layout or the iternext function. Currently only HASINDEX, EXLOOP, and RANGE affect them here.
Switch statements let the compiler optimize this most effectively
The combinations HASINDEX|EXLOOP and RANGE|EXLOOP are excluded by the New functions
begin repeat
#const_itflags = 0,
NPY_ITFLAG_HASINDEX, NPY_ITFLAG_EXLOOP, NPY_ITFLAG_RANGE, NPY_ITFLAG_RANGE|NPY_ITFLAG_HASINDEX#
System Message: WARNING/2 (<string>, line 7) Definition list ends without a blank line; unexpected unindent.

tag_itflags = 0, IND, NOINN, RNG, RNGuIND#

begin repeat1
#const_ndim = 1, 2# #tag_ndim = 1, 2#
begin repeat2
#const_nop = 1, 2# tag_nop = 1, 2#
end repeat2*
Not specialized on nop
end repeat1*
Not specialized on ndim
begin repeat1
#const_nop = 1, 2# tag_nop = 1, 2#
end repeat1*
Not specialized on nop
end repeat*
The switch above should have caught all the possibilities.

References NIT_NOP, and NIT_RESETDATAPTR.

NPY_NO_EXPORT npy_intp NpyIter_GetIterSize ( NpyIter iter)
Gets the number of elements being iterated

References NPY_MAXDIMS.

NPY_NO_EXPORT PyArrayObject* NpyIter_GetIterView ( NpyIter iter,
npy_intp  i 
)
Returns a view to the i-th object with the iterator's internal axes

Don't provide views if buffering is enabled
Retrieve the shape and strides from the axisdata
Tell the view who owns the data
Make sure all the flags are good

References npyiter_check_per_op_flags(), and npyiter_prepare_one_operand().

NPY_NO_EXPORT int NpyIter_GetNDim ( NpyIter iter)
Gets the number of dimensions being iterated
NPY_NO_EXPORT int NpyIter_GetNOp ( NpyIter iter)
Gets the number of operands being iterated

Referenced by npyiter_iterrange_set().

NPY_NO_EXPORT PyArrayObject** NpyIter_GetOperandArray ( NpyIter iter)
Get the array of objects being iterated

npy_uint32 itflags = NIT_ITFLAGS(iter);
int ndim = NIT_NDIM(iter);

NPY_NO_EXPORT void NpyIter_GetReadFlags ( NpyIter iter,
char *  outreadflags 
)
Gets an array of read flags (1 per object being iterated)

npy_uint32 itflags = NIT_ITFLAGS(iter);
int ndim = NIT_NDIM(iter);

References NPY_OP_ITFLAG_READ, npyiter_casting_to_string(), PyArray_CanCastArrayTo(), PyArray_CanCastTypeTo(), and PyArray_DESCR.

NPY_NO_EXPORT int NpyIter_GetShape ( NpyIter iter,
npy_intp outshape 
)
Gets the broadcast shape if a multi-index is being tracked by the iterator,

otherwise gets the shape of the iteration as Fortran-order (fastest-changing index first).

The reason Fortran-order is returned when a multi-index is not enabled is that this is providing a direct view into how the iterator traverses the n-dimensional space. The iterator organizes its memory from fastest index to slowest index, and when a multi-index is enabled, it uses a permutation to recover the original order.

Returns NPY_SUCCEED or NPY_FAIL.

NPY_NO_EXPORT void NpyIter_GetWriteFlags ( NpyIter iter,
char *  outwriteflags 
)
Gets an array of write flags (1 per object being iterated)

npy_uint32 itflags = NIT_ITFLAGS(iter);
int ndim = NIT_NDIM(iter);

static void npyiter_goto_iterindex ( NpyIter iter,
npy_intp  iterindex 
) [static]
This sets the AXISDATA portion of the iterator to the specified iterindex, updating the pointers as well. This function does no error checking.

Set the multi-index, from the fastest-changing to the slowest-changing.
Accumulate the successive pointers with their offsets in the opposite order, starting from the original data pointers.

References NAD_PTRS.

NPY_NO_EXPORT int NpyIter_GotoIndex ( NpyIter iter,
npy_intp  flat_index 
)
If the iterator is tracking an index, sets the iterator

to the specified index.

Returns NPY_SUCCEED on success, NPY_FAIL on failure.

Compute the iterindex corresponding to the flat_index
Extract the index from the flat_index
Add its contribution to iterindex

NPY_NO_EXPORT int NpyIter_GotoIterIndex ( NpyIter iter,
npy_intp  iterindex 
)
Sets the iterator position to the specified iterindex,

which matches the iteration order of the iterator.

Returns NPY_SUCCEED on success, NPY_FAIL on failure.

int ndim = NIT_NDIM(iter);
Check if the new iterindex is already within the buffer
Start the buffer at the provided iterindex
Write back to the arrays
Prepare the next buffers and set iterend/size

NPY_NO_EXPORT int NpyIter_GotoMultiIndex ( NpyIter iter,
npy_intp multi_index 
)
Sets the iterator to the specified multi-index, which must have the

correct number of entries for 'ndim'. It is only valid when NPY_ITER_MULTI_INDEX was passed to the constructor. This operation fails if the multi-index is out of bounds.

Returns NPY_SUCCEED on success, NPY_FAIL on failure.

Compute the iterindex corresponding to the multi-index
If the perm entry is negative, reverse the index
Bounds-check this index

References NAD_PTRS, and NAD_STRIDES.

NPY_NO_EXPORT npy_bool NpyIter_HasDelayedBufAlloc ( NpyIter iter)
Whether the buffer allocation is being delayed

Referenced by npyiter_iterrange_set().

NPY_NO_EXPORT npy_bool NpyIter_HasExternalLoop ( NpyIter iter)
Whether the iterator handles the inner loop

Referenced by npyiter_iterrange_set().

NPY_NO_EXPORT npy_bool NpyIter_HasIndex ( NpyIter iter)
Whether the iterator is tracking an index
NPY_NO_EXPORT npy_bool NpyIter_HasMultiIndex ( NpyIter iter)
Whether the iterator is tracking a multi-index

Referenced by npyiter_index_set().

NPY_NO_EXPORT npy_bool NpyIter_IsBuffered ( NpyIter iter)
Whether the iterator is buffered
NPY_NO_EXPORT npy_bool NpyIter_IsGrowInner ( NpyIter iter)
Whether the inner loop can grow if buffering is unneeded
NPY_NO_EXPORT npy_bool NpyIter_IterationNeedsAPI ( NpyIter iter)
Whether the iteration loop, and in particular the iternext()
function, needs API access. If this is true, the GIL must be retained while iterating.

References NPY_MAXDIMS.

static int npyiter_iternext_sizeone ( NpyIter iter) [static]
end repeat2*
end repeat1*
end repeat*
Specialization of iternext for when the iteration size is 1

References NBF_PTRS, and NIT_BUFFERDATA.

NPY_NO_EXPORT NpyIter* NpyIter_MultiNew ( int  nop,
PyArrayObject **  op_in,
npy_uint32  flags,
NPY_ORDER  order,
NPY_CASTING  casting,
npy_uint32 *  op_flags,
PyArray_Descr **  op_request_dtypes 
)
Allocate a new iterator for more than one array object, using
standard NumPy broadcasting rules and the default buffer size.
NPY_NO_EXPORT NpyIter* NpyIter_New ( PyArrayObject op,
npy_uint32  flags,
NPY_ORDER  order,
NPY_CASTING  casting,
PyArray_Descr dtype 
)
Allocate a new iterator for one array object.

Split the flags into separate global and op flags

static PyArrayObject * npyiter_new_temp_array ( NpyIter iter,
PyTypeObject *  subtype,
npy_uint32  flags,
char *  op_itflags,
int  op_ndim,
npy_intp shape,
PyArray_Descr op_dtype,
int *  op_axes 
) [static]
Allocates a temporary array which can be used to replace op in the iteration. Its dtype will be op_dtype.
The result array has a memory ordering which matches the iterator, which may or may not match that of op. The parameter 'shape' may be NULL, in which case it is filled in from the iterator's shape.
This function must be called before any axes are coalesced.

If it's a scalar, don't need to check the axes
Double-check that the subtype didn't mess with the dimensions
Initialize the strides to invalid values
Apply the perm to get the original axis
If deleting this axis produces a reduction, but reduction wasn't enabled, throw an error
Indicate that a reduction is occurring
Apply the perm to get the original axis
If custom axes were specified, some dimensions may not have been used. Add the REDUCE itflag if this creates a reduction situation.
Ensure there are no dimension gaps in op_axes, and find op_ndim
If there's a gap in the array's dimensions, it's an error. For example, op_axes of [0,2] for the automatically allocated output.
Fill in the missing strides in C order
Copy the missing strides, and multiply the existing strides by the calculated factor. This way, the missing strides are tighter together in memory, which is good for nested loops.
If shape was NULL, set it to the shape we calculated
Allocate the temporary array
If there are any reversed axes, create a view that reverses them
Make sure all the flags are good
Double-check that the subtype didn't mess with the dimensions

static int npyiter_prepare_one_operand ( PyArrayObject **  op,
char **  op_dataptr,
PyArray_Descr op_request_dtype,
PyArray_Descr **  op_dtype,
npy_uint32  flags,
npy_uint32  op_flags,
char *  op_itflags 
) [static]
Prepares a a constructor operand. Assumes a reference to 'op' is owned, and that 'op' may be replaced. Fills in 'op_dtype' and 'ndim'.
Returns 1 on success, 0 on failure.

NULL operands must be automatically allocated outputs
ALLOCATE should be enabled
Writing should be enabled
Reading should be disabled if buffering is enabled without also enabling NPY_ITER_DELAY_BUFALLOC. In all other cases, the caller may initialize the allocated operand to a value before beginning iteration.
If a requested dtype was provided, use it, otherwise NULL
PyArray_DESCR does not give us a reference
If references weren't specifically allowed, make sure there are no references in the inputs or requested dtypes.
Checking whether casts are valid is done later, once the final data types have been selected. For now, just store the requested type.
We just have a borrowed reference to op_request_dtype
If it's a data type without a size, set the size
Store the requested dtype
Check if the operand is in the byte order requested
Check byte order
Replace with a new descr which is in native byte order
Indicate that byte order or alignment needs fixing
Check if the operand is aligned
Check alignment
The check for NPY_ITER_CONTIG can only be done later, once the final iteration order is settled.

Referenced by NpyIter_GetIterView().

static int npyiter_prepare_operands ( int  nop,
PyArrayObject **  op_in,
PyArrayObject **  op,
char **  op_dataptr,
PyArray_Descr **  op_request_dtypes,
PyArray_Descr **  op_dtype,
npy_uint32  flags,
npy_uint32 *  op_flags,
char *  op_itflags 
) [static]
Process all the operands, copying new references so further processing can replace the arrays if copying is necessary.

Check the readonly/writeonly flags, and fill in op_itflags
Prepare the operand. This produces an op_dtype[iop] reference on success.
If all the operands were NULL, it's an error

References NPY_F_CONTIGUOUS, and PyArray_CHKFLAGS.

NPY_NO_EXPORT int NpyIter_RemoveAxis ( NpyIter iter,
int  axis 
)
Removes an axis from iteration. This requires that NPY_ITER_MULTI_INDEX

was set for iterator creation, and does not work if buffering is enabled. This function also resets the iterator to its initial state.

Returns NPY_SUCCEED or NPY_FAIL.

Reverse axis, since the iterator treats them that way
First find the axis in question
If this is it, and it's iterated forward, done
If this is it, but it's iterated backward, must reverse the axis
Adjust baseoffsets and resetbaseptr back to the start of this axis.
Adjust the permutation
Adjust the iteration size
Shift all the axisdata structures by one
If there is more than one dimension, shrink the iterator
Otherwise convert it to a singleton dimension

NPY_NO_EXPORT int NpyIter_RemoveMultiIndex ( NpyIter iter)
Removes multi-index support from an iterator. <blockquote> Returns NPY_SUCCEED or NPY_FAIL.</blockquote>

Make sure the iterator is reset

References NPY_FAIL.

static void npyiter_replace_axisdata ( NpyIter iter,
int  iop,
PyArrayObject op,
int  op_ndim,
char *  op_dataptr,
int *  op_axes 
) [static]
Replaces the AXISDATA for the iop'th operand, broadcasting the dimensions as necessary. Assumes the replacement array is exactly the same shape as the original array used when npy_fill_axisdata was called.
If op_axes is not NULL, it should point to an ndim-sized array.

Replace just the strides which were non-zero, and compute the base data address.
Apply the perm to get the original axis
If the perm entry is negative, flip the axis
Apply the perm to get the original axis
If the perm entry is negative, flip the axis
Now the base data pointer is calculated, set it everywhere it's needed

NPY_NO_EXPORT npy_bool NpyIter_RequiresBuffering ( NpyIter iter)
Whether the iteration could be done with no buffering.

int ndim = NIT_NDIM(iter);
If any operand requires a cast, buffering is mandatory

References NPY_ITFLAG_GROWINNER.

NPY_NO_EXPORT int NpyIter_Reset ( NpyIter iter,
char **  errmsg 
)
Resets the iterator to its initial state <blockquote> If errmsg is non-NULL, it should point to a variable which will receive the error message, and no Python exception will be set. This is so that the function can be called from code not holding the GIL.</blockquote>

int ndim = NIT_NDIM(iter);
If buffer allocation was delayed, do it now
If the iterindex is already right, no need to do anything
Copy any data from the buffers back to the arrays
Prepare the next buffers and set iterend/size

NPY_NO_EXPORT int NpyIter_ResetBasePointers ( NpyIter iter,
char **  baseptrs,
char **  errmsg 
)
Resets the iterator to its initial state, with new base data pointers <blockquote> If errmsg is non-NULL, it should point to a variable which will receive the error message, and no Python exception will be set. This is so that the function can be called from code not holding the GIL.</blockquote>

int ndim = NIT_NDIM(iter);
If buffer allocation was delayed, do it now
Copy any data from the buffers back to the arrays
The new data pointers for resetting
Prepare the next buffers and set iterend/size

NPY_NO_EXPORT int NpyIter_ResetToIterIndexRange ( NpyIter iter,
npy_intp  istart,
npy_intp  iend,
char **  errmsg 
)
Resets the iterator to a new iterator index range <blockquote> If errmsg is non-NULL, it should point to a variable which will receive the error message, and no Python exception will be set. This is so that the function can be called from code not holding the GIL.</blockquote>

int ndim = NIT_NDIM(iter);
int nop = NIT_NOP(iter);

static void npyiter_reverse_axis_ordering ( NpyIter iter) [static]

This loop reverses the order of the AXISDATA array
Store the perm we applied

static PyObject* npyiter_shape_string ( npy_intp  n,
npy_intp vals,
char *  ending 
) [static]

Negative dimension indicates "newaxis", which can be discarded for printing if its a leading dimension. Find the first non-"newaxis" dimension.

References intp_abs(), NAD_STRIDES, and NIT_INDEX_AXISDATA.

static void npyiter_get_multi_index_itflags tag_itflags ( NpyIter iter,
npy_intp out_multi_index 
) [static]
SPECIALIZED getindex functions
begin repeat
#const_itflags = 0,
NPY_ITFLAG_HASINDEX, NPY_ITFLAG_IDENTPERM, NPY_ITFLAG_HASINDEX|NPY_ITFLAG_IDENTPERM, NPY_ITFLAG_NEGPERM, NPY_ITFLAG_HASINDEX|NPY_ITFLAG_NEGPERM, NPY_ITFLAG_BUFFER, NPY_ITFLAG_HASINDEX|NPY_ITFLAG_BUFFER, NPY_ITFLAG_IDENTPERM|NPY_ITFLAG_BUFFER, NPY_ITFLAG_HASINDEX|NPY_ITFLAG_IDENTPERM|NPY_ITFLAG_BUFFER, NPY_ITFLAG_NEGPERM|NPY_ITFLAG_BUFFER, NPY_ITFLAG_HASINDEX|NPY_ITFLAG_NEGPERM|NPY_ITFLAG_BUFFER#
tag_itflags = 0, IND, IDP, INDuIDP, NEGP, INDuNEGP,
BUF, INDuBUF, IDPuBUF, INDuIDPuBUF, NEGPuBUF, INDuNEGPuBUF#

References NAD_STRIDES, NIT_ADVANCE_AXISDATA, NIT_AXISDATA, NIT_AXISDATA_SIZEOF, NIT_INDEX_AXISDATA, NIT_ITFLAGS, NIT_NDIM, NIT_NOP, and NIT_PERM.

Referenced by NpyIter_GetIterIndex().

static int npyiter_buffered_reduce_iternext_iters tag_nop ( NpyIter iter) [static]
SPECIALIZED iternext functions that handle the non-buffering part
begin repeat
#const_itflags = 0,
NPY_ITFLAG_HASINDEX, NPY_ITFLAG_EXLOOP, NPY_ITFLAG_RANGE, NPY_ITFLAG_RANGE|NPY_ITFLAG_HASINDEX#
System Message: WARNING/2 (<string>, line 7) Definition list ends without a blank line; unexpected unindent.

tag_itflags = 0, IND, NOINN, RNG, RNGuIND#

begin repeat1
#const_ndim = 1, 2, NPY_MAXDIMS# #tag_ndim = 1, 2, ANY#
begin repeat2
#const_nop = 1, 2, NPY_MAXDIMS# tag_nop = 1, 2, ANY#
Specialized iternext (&#64;const_itflags&#64;,&#64;tag_ndim&#64;,&#64;tag_nop&#64;)
end repeat2*
end repeat1*
end repeat*
begin repeat
#const_nop = 1, 2, 3, 4, NPY_MAXDIMS# tag_nop = 1, 2, 3, 4, ANY#
Iternext function that handles the reduction buffering part. This is done with a double loop to avoid frequent re-buffering.

Increment index 1
Increment pointer 1
Reset the 1st index to 0
Reset the 1st pointer to the value of the 2nd
Increment index 2
Increment pointer 2
Reset the 1st and 2nd indices to 0
Reset the 1st and 2nd pointers to the value of the 3nd
Increment the index
Increment the pointer
Reset the indices and pointers of all previous axisdatas
Reset the index to 0
Reset the pointer to the updated value

<

ndim != 2

<

ndim != 1
int ndim = NIT_NDIM(iter);
If the iterator handles the inner loop, need to increment all the indices and pointers
Increment within the buffer
The outer increment for the reduce double loop
Save the previously used data pointers
Write back to the arrays
Check if we're past the end
Increment to the next buffer
Prepare the next buffers and set iterend/size

Referenced by NpyIter_GetIterIndex().