numpy  2.0.0
src/multiarray/nditer_constr.c File Reference
#include "nditer_impl.h"
#include "arrayobject.h"

Defines

#define NPY_NO_DEPRECATED_API   NPY_API_VERSION
#define NPY_ITERATOR_IMPLEMENTATION_CODE

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, npyiter_opitflags *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, npyiter_opitflags *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, npyiter_opitflags *op_itflags, npy_int8 *out_maskop)
static int npyiter_check_casting (int nop, PyArrayObject **op, PyArray_Descr **op_dtype, NPY_CASTING casting, npyiter_opitflags *op_itflags)
static int npyiter_fill_axisdata (NpyIter *iter, npy_uint32 flags, npyiter_opitflags *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 PyArray_Descrnpyiter_get_common_dtype (int nop, PyArrayObject **op, npyiter_opitflags *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, npyiter_opitflags *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, npyiter_opitflags *op_itflags, int **op_axes, int output_scalars)
static void npyiter_get_priority_subtype (int nop, PyArrayObject **op, npyiter_opitflags *op_itflags, double *subtype_priority, PyTypeObject **subtype)
static int npyiter_allocate_transfer_functions (NpyIter *iter)
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)
static const char * npyiter_casting_to_string (NPY_CASTING casting)
static PyObject * npyiter_shape_string (npy_intp n, npy_intp *vals, char *ending)
static int check_mask_for_writemasked_reduction (NpyIter *iter, int iop)
static NPY_INLINE npy_intp intp_abs (npy_intp x)

Define Documentation

Indicate that this .c file is allowed to include the header
#define NPY_NO_DEPRECATED_API   NPY_API_VERSION

Function Documentation

static int check_mask_for_writemasked_reduction ( NpyIter iter,
int  iop 
) [static]
Checks that the mask broadcasts to the WRITEMASK REDUCE operand 'iop', but 'iop' never broadcasts to the mask. If 'iop' broadcasts to the mask, the result would be more than one mask value per reduction element, something which is invalid.
This check should only be called after all the operands have been filled in.
Returns 1 on success, 0 on error.
If 'iop' is being broadcast to 'maskop', we have the invalid situation described above.
static NPY_INLINE npy_intp intp_abs ( npy_intp  x) [static]
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
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,
npyiter_opitflags *  op_itflags,
int **  op_axes,
int  output_scalars 
) [static]
Check whether there are any WRITEMASKED REDUCE operands which should be validated after all the strides are filled in.
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. TODO: It might be possible to do a view into

System Message: ERROR/3 (<string>, line 3) Unexpected indentation.

<blockquote> op[iop]'s mask instead here.</blockquote>

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, 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.
Check whether there are any WRITEMASKED REDUCE operands which should be validated now that all the strides are filled in.
If the ARRAYMASK has 'bigger' dimensions than this REDUCE WRITEMASKED operand, the result would be more than one mask value per reduction element, something which is invalid. This function provides validation for that.
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 the operand is WRITEMASKED, use a masked transfer fn
If the mask's stride is contiguous, use it, otherwise the mask may or may not be buffered, so the stride could be inconsistent.
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_MAXARGS, NPY_NATIVE, PyArray_DescrNewByteorder(), PyArray_ISNBO, PyArray_NDIM, and PyArray_ResultType().

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]

References PyArray_DIMS, and PyArray_NDIM.

static int npyiter_check_casting ( int  nop,
PyArrayObject **  op,
PyArray_Descr **  op_dtype,
NPY_CASTING  casting,
npyiter_opitflags *  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 NPY_ITER_NO_BROADCAST, NPY_ITER_REDUCE_OK, NPY_SIZEOF_INTP, PyArray_DIM, PyArray_NDIM, and PyArray_STRIDE.

static int npyiter_check_global_flags ( npy_uint32  flags,
npy_uint32 *  itflags 
) [static]
Internal helper functions private to this file
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,
npyiter_opitflags *  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
Check the flag for a write masked operands

References _PyArray_Descr::flags, NPY_ITEM_IS_POINTER, NPY_ITEM_REFCOUNT, NPY_ITER_ALIGNED, NPY_ITER_NBO, NPY_ITER_ZEROSIZE_OK, NPY_NATIVE, PyArray_AdaptFlexibleDType(), PyArray_BYTES, PyArray_DESCR, PyArray_DescrNewByteorder(), PyArray_FailUnlessWriteable(), PyArray_ISALIGNED, PyArray_ISNBO, and PyArray_SIZE.

Referenced by npyiter_prepare_one_operand().

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.
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

References NPY_AUXDATA_FREE, NPY_SUCCEED, and PyArray_free.

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

References NPY_MAXDIMS.

Referenced by apply_business_day_count(), business_day_offset(), and PyUFunc_Reduce().

static int npyiter_fill_axisdata ( NpyIter iter,
npy_uint32  flags,
npyiter_opitflags *  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
The ARRAYMASK can't be a reduction, because it would be possible to write back to the array once when the ARRAYMASK says 'True', then have the reduction on the ARRAYMASK later flip to 'False', indicating that the write back should never have been done, and violating the strict masking semantics
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
Broadcast shape

References npyiter_shape_string(), PyArray_DIMS, PyArray_NDIM, PyUString_ConcatAndDel, and PyUString_FromString.

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 NPY_MAX_INTP.

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 operand strides.
If at least one 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.

References _PyArray_Descr::elsize, NPY_ITER_REDUCE_OK, NPY_MAX_INTP, NPY_MAXDIMS, and PyArray_NewFromDescr().

static PyArray_Descr * npyiter_get_common_dtype ( int  nop,
PyArrayObject **  op,
npyiter_opitflags *  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,
npyiter_opitflags *  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 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.

Referenced by apply_business_day_count(), and business_day_offset().

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,
npyiter_opitflags *  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.
There is an interaction with array-dtypes here, which generally works. Let's say you make an nditer with an output dtype of a 2-double array. All-scalar inputs will result in a 1-dimensional output with shape (2). Everything still works out in the nditer, because the new dimension is always added on the end, and it cares about what happens at the beginning.
If it's a scalar, don't need to check the axes
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
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,
npyiter_opitflags *  op_itflags 
) [static]
Prepares a a constructor operand. Assumes a reference to 'op' is owned, and that 'op' may be replaced. Fills in 'op_dataptr', 'op_dtype', and may modify 'op_itflags'.
Returns 1 on success, 0 on failure.
NULL operands must be automatically allocated outputs
ALLOCATE or VIRTUAL 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
Specify bool if no dtype was requested for the mask
VIRTUAL operands must be 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 the requested dtype is flexible, adapt it
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.

References npyiter_check_per_op_flags().

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,
npyiter_opitflags *  op_itflags,
npy_int8 *  out_maskop 
) [static]
Process all the operands, copying new references so further processing can replace the arrays if copying is necessary.
Here we just prepare the provided operands.
Check the readonly/writeonly flags, and fill in op_itflags
Extract the operand which is for masked iteration
Prepare the operand. This produces an op_dtype[iop] reference on success.
If all the operands were NULL, it's an error
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
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 it's a leading dimension. Find the first non-"newaxis" dimension.

Referenced by npyiter_fill_axisdata().