numpy  2.0.0
src/multiarray/mapping.c File Reference
#include <Python.h>
#include "structmember.h"
#include "numpy/arrayobject.h"
#include "arrayobject.h"
#include "npy_config.h"
#include "npy_pycompat.h"
#include "common.h"
#include "iterators.h"
#include "mapping.h"
#include "lowlevel_strided_loops.h"
#include "item_selection.h"

Defines

#define PY_SSIZE_T_CLEAN
#define NPY_NO_DEPRECATED_API   NPY_API_VERSION
#define _MULTIARRAYMODULE
#define SOBJ_NOTFANCY   0
#define SOBJ_ISFANCY   1
#define SOBJ_BADARRAY   2
#define SOBJ_TOOMANY   3
#define SOBJ_LISTTUP   4

Functions

static PyObject * array_subscript_simple (PyArrayObject *self, PyObject *op)
NPY_NO_EXPORT Py_ssize_t array_length (PyArrayObject *self)
NPY_NO_EXPORT PyObject * array_big_item (PyArrayObject *self, npy_intp i)
NPY_NO_EXPORT int _array_ass_item (PyArrayObject *self, Py_ssize_t i, PyObject *v)
NPY_NO_EXPORT PyObject * array_item_nice (PyArrayObject *self, Py_ssize_t _i)
NPY_NO_EXPORT int array_ass_big_item (PyArrayObject *self, npy_intp i, PyObject *v)
NPY_NO_EXPORT void PyArray_MapIterSwapAxes (PyArrayMapIterObject *mit, PyArrayObject **ret, int getmap)
static PyObject * PyArray_GetMap (PyArrayMapIterObject *mit)
static int PyArray_SetMap (PyArrayMapIterObject *mit, PyObject *op)
NPY_NO_EXPORT int count_new_axes_0d (PyObject *tuple)
NPY_NO_EXPORT PyObject * add_new_axes_0d (PyArrayObject *arr, int newaxis_count)
static int fancy_indexing_check (PyObject *args)
NPY_NO_EXPORT PyArrayObjectarray_boolean_subscript (PyArrayObject *self, PyArrayObject *bmask, NPY_ORDER order)
NPY_NO_EXPORT int array_ass_boolean_subscript (PyArrayObject *self, PyArrayObject *bmask, PyArrayObject *v, NPY_ORDER order)
NPY_NO_EXPORT PyObject * array_subscript (PyArrayObject *self, PyObject *op)
static int array_ass_sub_simple (PyArrayObject *self, PyObject *ind, PyObject *op)
static int _tuple_of_integers (PyObject *seq, npy_intp *vals, int maxvals)
static int array_ass_sub (PyArrayObject *self, PyObject *ind, PyObject *op)
static PyObject * array_subscript_nice (PyArrayObject *self, PyObject *op)
static int _nonzero_indices (PyObject *myBool, PyArrayIterObject **iters)
static int _convert_obj (PyObject *obj, PyArrayIterObject **iter)
NPY_NO_EXPORT void PyArray_MapIterReset (PyArrayMapIterObject *mit)
NPY_NO_EXPORT void PyArray_MapIterNext (PyArrayMapIterObject *mit)
NPY_NO_EXPORT void PyArray_MapIterBind (PyArrayMapIterObject *mit, PyArrayObject *arr)
NPY_NO_EXPORT PyObject * PyArray_MapIterNew (PyObject *indexobj, int oned, int fancy)
NPY_NO_EXPORT PyObject * PyArray_MapIterArray (PyArrayObject *a, PyObject *index)
static void arraymapiter_dealloc (PyArrayMapIterObject *mit)

Variables

NPY_NO_EXPORT PyMappingMethods array_as_mapping
NPY_NO_EXPORT PyTypeObject PyArrayMapIter_Type

Define Documentation

#define NPY_NO_DEPRECATED_API   NPY_API_VERSION
#include <stdio.h>
#define SOBJ_BADARRAY   2
#define SOBJ_ISFANCY   1
#define SOBJ_LISTTUP   4
#define SOBJ_NOTFANCY   0
#define SOBJ_TOOMANY   3

Function Documentation

NPY_NO_EXPORT int _array_ass_item ( PyArrayObject self,
Py_ssize_t  i,
PyObject *  v 
)
static int _convert_obj ( PyObject *  obj,
PyArrayIterObject **  iter 
) [static]
convert an indexing object to an INTP indexing array iterator
if possible -- otherwise, it is a Slice or Ellipsis object and has to be interpreted on bind to a particular array so leave it NULL for now.

References Py_TYPE.

static int _nonzero_indices ( PyObject *  myBool,
PyArrayIterObject **  iters 
) [static]
************ End of Mapping Protocol *************************
***************** Subscript Array Iterator *********************

<blockquote class="first">

</blockquote>

System Message: WARNING/2 (<string>, line 3) Block quote ends without a blank line; unexpected unindent.
This object handles subscript behavior for array objects. *

It is an iterator object with a next method * It abstracts the n-dimensional mapping behavior to make the looping *

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

<blockquote class="last"> code more understandable (maybe) * and so that indexing can be set up ahead of time *</blockquote>

This function takes a Boolean array and constructs index objects and iterators as if nonzero(Bool) had been called
pre-determine how many nonzero entries there are
create count-sized index arrays for each dimension
Loop through the Boolean array and copy coordinates for non-zero entries
Borrowed from ITER_NEXT macro

References PyArrayMapIterObject::ait, PyArrayIterObject_tag::ao, array_subscript_simple(), PyArrayMapIterObject::bscoord, PyArrayMapIterObject::dimensions, PyArrayMapIterObject::indexobj, PyArrayMapIterObject::iteraxes, PyArrayMapIterObject::nd, PyArrayMapIterObject::numiter, PyArray_CheckExact, PyArray_DIMS, PyArray_EnsureArray(), PyArray_IterNew(), PyArray_NDIM, slice_GetIndices(), and PyArrayMapIterObject::subspace.

static int _tuple_of_integers ( PyObject *  seq,
npy_intp vals,
int  maxvals 
) [static]
return -1 if tuple-object seq is not a tuple of integers.
otherwise fill vals with converted integers
NPY_NO_EXPORT PyObject* add_new_axes_0d ( PyArrayObject arr,
int  newaxis_count 
)
NPY_NO_EXPORT int array_ass_big_item ( PyArrayObject self,
npy_intp  i,
PyObject *  v 
)
For multi-dimensional arrays, use CopyObject
Bounds check and get the data pointer
Implements boolean indexing assignment. This takes the one-dimensional array 'v' and assigns its values to all of the elements of 'self' for which the corresponding element of 'op' is True.
This operation is somewhat unfortunate, because to match up with a one-dimensional output array, it has to choose a particular iteration order, in the case of NumPy that is always C order even though this function allows different choices.
Returns 0 on success, -1 on failure.
Correction factor for broadcasting 'bmask' to 'self'
Tweak the strides for 0-dim and broadcasting cases
Create an iterator for the data
Set up the iterator
Get the values needed for the inner loop
Get a dtype transfer function
Skip masked values
Process unmasked values
static int array_ass_sub ( PyArrayObject self,
PyObject *  ind,
PyObject *  op 
) [static]
Doing "a[...] += 1" triggers assigning an array to itself, so this check is needed.
Several different exceptions to the 0-d no-indexing rule <blockquote>

  1. ellipses (handled above generally)
  2. empty tuple
  3. Using newaxis (None)
  4. Boolean mask indexing

</blockquote>

<

don't do anything
Integer-tuple
Boolean indexing special case
Assigning from multi-dimensional 'op' in this case seems inconsistent, so falling through to old code for backwards compatibility.
static int array_ass_sub_simple ( PyArrayObject self,
PyObject *  ind,
PyObject *  op 
) [static]
Another assignment hacked by using CopyObject. This only works if subscript returns a standard view. Again there are two cases. In the first case, PyArray_CopyObject can be used. In the second case, a new indexing function has to be used.
Rest of standard (view-based) indexing
Note: this code path should never be reached with an index that
produces scalars -- those are handled earlier in array_ass_sub

References PyArray_CopyObject().

NPY_NO_EXPORT PyObject* array_big_item ( PyArrayObject self,
npy_intp  i 
)
Bounds check and get the data pointer
Create the view array
Set the base object

Referenced by array_item_nice(), and fancy_indexing_check().

Implements boolean indexing. This produces a one-dimensional array which picks out all of the elements of 'self' for which the corresponding element of 'op' is True.
This operation is somewhat unfortunate, because to produce a one-dimensional output array, it has to choose a particular iteration order, in the case of NumPy that is always C order even though this function allows different choices.
Correction factor for broadcasting 'bmask' to 'self'
Allocate the output of the boolean indexing
Create an iterator for the data
Set up the iterator
Get a dtype transfer function
Get the values needed for the inner loop
Skip masked values
Process unmasked values
NPY_NO_EXPORT PyObject* array_item_nice ( PyArrayObject self,
Py_ssize_t  _i 
)
contains optimization for 1-d arrays
Workaround Python 2.4: Py_ssize_t not the same as npyint_p
Bounds check and get the data pointer

References array_big_item(), PyArray_FailUnlessWriteable(), and PyArray_NDIM.

Referenced by array_contains().

NPY_NO_EXPORT Py_ssize_t array_length ( PyArrayObject self)
System Message: SEVERE/4 (<string>, line 1)
Title overline & underline mismatch.

                    IMPLEMENT MAPPING PROTOCOL                          ***
 

References PyArray_NDIM.

Referenced by array_contains().

NPY_NO_EXPORT PyObject* array_subscript ( PyArrayObject self,
PyObject *  op 
)
Check for multiple field access
extract multiple fields if all elements in sequence are either string or unicode (i.e. no break occurred).
Allow Boolean mask selection also
Boolean indexing special case
The SIZE check might be overly cautious
wrap arguments into a mapiter object
static PyObject* array_subscript_nice ( PyArrayObject self,
PyObject *  op 
) [static]
There are places that require that array_subscript return a PyArrayObject and not possibly a scalar. Thus, this is the function exposed to Python so that 0-dim arrays are passed as scalars
optimization for a tuple of integers
mp could be a scalar if op is not an Int, Scalar, Long or other Index object and still convertable to an integer (so that the code goes to array_subscript_simple). So, this cast is a bit dangerous..
The following adds some additional logic to avoid calling PyArray_Return if there is an ellipsis.
NPY_NO_EXPORT PyObject * array_subscript_simple ( PyArrayObject self,
PyObject *  op 
) [static]
Called when treating array object like a mapping -- called first from Python when using a[object] unless object is a standard slice object (not an extended one).
There are two situations: <blockquote>
1 - the subscript is a standard view and a reference to the array can be returned
2 - the subscript uses Boolean masks or integer indexing and therefore a new array is created and returned. </blockquote>
PyNumber_Index was introduced in Python 2.5 because of NumPy. http://www.python.org/dev/peps/pep-0357/ Let's use it for indexing!
Unfortunately, SciPy and possibly other code seems to rely on the lenient coercion. :(

<

PY_VERSION_HEX >= 0x02050000
Standard (view-based) Indexing
Create a view using the indexing result

Referenced by _nonzero_indices().

static void arraymapiter_dealloc ( PyArrayMapIterObject mit) [static]
NPY_NO_EXPORT int count_new_axes_0d ( PyObject *  tuple)
static int fancy_indexing_check ( PyObject *  args) [static]
This checks the args for any fancy indexing objects
Sequences < NPY_MAXDIMS with any slice objects or newaxis, or Ellipsis is considered standard as long as there are also no Arrays and or additional sequences embedded.

References array_big_item(), NPY_ARRAY_UPDATE_ALL, NPY_MAXDIMS, parse_index(), Py_TYPE, PyArray_DATA, PyArray_DESCR, PyArray_FLAGS, PyArray_NewFromDescr(), PyArray_PyIntAsIntp(), PyArray_SetBaseObject(), and PyArray_UpdateFlags().

static PyObject* PyArray_GetMap ( PyArrayMapIterObject mit) [static]
Unbound map iterator --- Bind should have been called
This relies on the map iterator object telling us the shape
of the new array in nd and dimensions.
Now just iterate through the new array filling it in with the next object from the original array as defined by the mapping iterator
check for consecutive axes

<

then we need to swap

References PyArrayMapIterObject::iteraxes, and PyArray_MapIterSwapAxes().

NPY_NO_EXPORT PyObject* PyArray_MapIterArray ( PyArrayObject a,
PyObject *  index 
)
Bind a mapiteration to a particular array <blockquote>
Determine if subspace iteration is necessary. If so, 1) Fill in mit->iteraxes 2) Create subspace iterator 3) Update nd, dimensions, and size.
Subspace iteration is necessary if: PyArray_NDIM(arr) > mit->numiter </blockquote>
Need to check for index-errors somewhere.
Let's do it at bind time and also convert all <0 values to >0 here as well.
no subspace iteration needed. Finish up and Return
all indexing arrays have been converted to 0 therefore we can extract the subspace with a simple getitem call which will use view semantics
But, be sure to do it with a true array.
Expand dimensions of result
Now, we still need to interpret the ellipsis and slice objects to determine which axes the indexing arrays are referring to
The number of dimensions an ellipsis takes up
Now fill in iteraxes -- remember indexing arrays have been converted to 0's in mit->indexobj
Only expand the first ellipsis
We need to fill in the starting coordinates for the subspace
Should be slice object or another Ellipsis
Here check the indexes (now that we have iteraxes)
NPY_NO_EXPORT PyObject* PyArray_MapIterNew ( PyObject *  indexobj,
int  oned,
int  fancy 
)
Must have some kind of fancy indexing if we are here indexobj is either a list, an arrayobject, or a tuple (with at least 1 list or arrayobject or Bool object)
convert all inputs to iterators
must be a tuple
Make a copy of the tuple -- we will be replacing index objects with 0's
we need to grow the new indexing object and fill it with 0s for each of the iterators produced
Store the number of iterators actually converted These will be mapped to actual axes at bind time
This function needs to update the state of the map iterator
and point mit->dataptr to the memory-location of the next object
Sub-space iteration
reset coord to coordinates of beginning of the subspace

Referenced by test_pydatamem_seteventhook_start().

Reset the map iterator to the beginning
NPY_NO_EXPORT void PyArray_MapIterSwapAxes ( PyArrayMapIterObject mit,
PyArrayObject **  ret,
int  getmap 
)
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.
arr might not have the right number of dimensions and need to be reshaped first by pre-pending ones
Setting and getting need to have different permutations. On the get we are permuting the returned object, but on setting we are permuting the object-to-be-set. The set permutation is the inverse of the get permutation.
For getting the array the tuple for transpose is (n1,...,n1+n2-1,0,...,n1-1,n1+n2,...,n3-1) n1 is the number of dimensions of the broadcast index array n2 is the number of dimensions skipped at the start n3 is the number of dimensions of the result
For setting the array the tuple for transpose is (n2,...,n1+n2-1,0,...,n2-1,n1+n2,...n3-1)
use n1 as the boundary if getting but n2 if setting

References PyArray_Dims::ptr.

Referenced by PyArray_GetMap().

static int PyArray_SetMap ( PyArrayMapIterObject mit,
PyObject *  op 
) [static]
Unbound Map Iterator

<

then we need to swap
Be sure values array is "broadcastable"
to shape of mit->dimensions, mit->nd
Need to decref arrays with objects in them
ignored unless VOID array with object's

Variable Documentation

NPY_NO_EXPORT PyMappingMethods array_as_mapping
Initial value:
 {






    (inquiry)array_length,              

    (binaryfunc)array_subscript_nice,       
    (objobjargproc)array_ass_sub,       
}
The mapiter object must be created new each time. It does not work to bind to a new array, and continue.
This was the orginal intention, but currently that does not work. Do not expose the MapIter_Type to Python.
It's not very useful anyway, since mapiter(indexobj); mapiter.bind(a); mapiter is equivalent to a[indexobj].flat but the latter gets to use slice syntax.