# Indexing¶

Array API specification for indexing arrays.

A conforming implementation of the array API standard must adhere to the following conventions.

## Single-axis Indexing¶

To index a single array axis, an array must support standard Python indexing rules. Let `n`

be the axis (dimension) size.

An integer index must be an object satisfying operator.index (e.g.,

`int`

).Nonnegative indices must start at

`0`

(i.e., zero-based indexing).**Valid**nonnegative indices must reside on the half-open interval`[0, n)`

.Note

This specification does not require bounds checking. The behavior for out-of-bounds integer indices is left unspecified.

Negative indices must count backward from the last array index, starting from

`-1`

(i.e., negative-one-based indexing, where`-1`

refers to the last array index).Note

A negative index

`j`

is equivalent to`n-j`

; the former is syntactic sugar for the latter, providing a shorthand for indexing elements that would otherwise need to be specified in terms of the axis (dimension) size.**Valid**negative indices must reside on the closed interval`[-n, -1]`

.Note

This specification does not require bounds checking. The behavior for out-of-bounds integer indices is left unspecified.

A negative index

`j`

is related to a zero-based nonnegative index`i`

via`i = n+j`

.Colons

`:`

must be used for slices:`start:stop:step`

, where`start`

is inclusive and`stop`

is exclusive.Note

The specification does not support returning scalar (i.e., non-array) values from operations, including indexing. In contrast to standard Python indexing rules, for any index, or combination of indices, which select a single value, the result must be a zero-dimensional array containing the selected value.

### Slice Syntax¶

The basic slice syntax is `i:j:k`

where `i`

is the starting index, `j`

is the stopping index, and `k`

is the step (`k != 0`

). A slice may contain either one or two colons, with either an integer value or nothing on either side of each colon. The following are valid slices.

```
A[:]
A[i:]
A[:j]
A[i:k]
A[::]
A[i::]
A[:j:]
A[::k]
A[i:j:]
A[i::k]
A[:j:k]
A[i::k]
A[i:j:k]
```

Note

Slice syntax can be equivalently achieved using the Python built-in slice() API. From the perspective of `A`

, the behavior of `A[i:j:k]`

and `A[slice(i, j, k)]`

is indistinguishable (i.e., both retrieve the same set of items from `__getitem__`

).

Using a slice to index a single array axis must select `m`

elements with index values

```
i, i+k, i+2k, i+3k, ..., i+(m-1)k
```

where

```
m = q + r
```

and `q`

and `r`

(`r != 0`

) are the quotient and remainder obtained by dividing `j-i`

by `k`

```
j - i = qk + r
```

such that

```
j > i + (m-1)k
```

Note

For `i`

on the interval `[0, n)`

(where `n`

is the axis size), `j`

on the interval `(0, n]`

, `i`

less than `j`

, and positive step `k`

, a starting index `i`

is **always** included, while the stopping index `j`

is **always** excluded. This preserves `x[:i]+x[i:]`

always being equal to `x`

.

Note

Using a slice to index into a single array axis should select the same elements as using a slice to index a Python list of the same size.

Slice syntax must have the following defaults. Let `n`

be the axis (dimension) size.

If

`k`

is not provided (e.g.,`0:10`

),`k`

must equal`1`

.If

`k`

is greater than`0`

and`i`

is not provided (e.g.,`:10:2`

),`i`

must equal`0`

.If

`k`

is greater than`0`

and`j`

is not provided (e.g.,`0::2`

),`j`

must equal`n`

.If

`k`

is less than`0`

and`i`

is not provided (e.g.,`:10:-2`

),`i`

must equal`n-1`

.If

`k`

is less than`0`

and`j`

is not provided (e.g.,`0::-2`

),`j`

must equal`-n-1`

.

Using a slice to index a single array axis must adhere to the following rules. Let `n`

be the axis (dimension) size.

If

`i`

equals`j`

, a slice must return an empty array, whose axis (dimension) size along the indexed axis is`0`

.Indexing via

`:`

and`::`

must be equivalent and have defaults derived from the rules above. Both`:`

and`::`

indicate to select all elements along a single axis (dimension).Note

This specification does not require “clipping” out-of-bounds slice indices. This is in contrast to Python slice semantics where

`0:100`

and`0:10`

are equivalent on a list of length`10`

.

The following ranges for the start and stop values of a slice must be supported. Let `n`

be the axis (dimension) size being sliced. For a slice `i:j:k`

, the behavior specified above should be implemented for the following:

`i`

or`j`

omitted (`None`

).`-n <= i <= n`

.For

`k > 0`

or`k`

omitted (`None`

),`-n <= j <= n`

.For

`k < 0`

,`-n - 1 <= j <= max(0, n - 1)`

.

The behavior outside of these bounds is unspecified.

Note

*Rationale: this is consistent with bounds checking for integer indexing; the behavior of out-of-bounds indices is left unspecified. Implementations may choose to clip (consistent with Python* `list`

*slicing semantics), raise an exception, return junk values, or some other behavior depending on device requirements and performance considerations.*

## Multi-axis Indexing¶

Multi-dimensional arrays must extend the concept of single-axis indexing to multiple axes by applying single-axis indexing rules along each axis (dimension) and supporting the following additional rules. Let `N`

be the number of dimensions (“rank”) of a multi-dimensional array `A`

.

Each axis may be independently indexed via single-axis indexing by providing a comma-separated sequence (“selection tuple”) of single-axis indexing expressions (e.g.,

`A[:, 2:10, :, 5]`

).Note

In Python,

`A[(exp1, exp2, ..., expN)]`

is equivalent to`A[exp1, exp2, ..., expN]`

; the latter is syntactic sugar for the former.Accordingly, if

`A`

has rank`1`

, then`A[(2:10,)]`

must be equivalent to`A[2:10]`

. If`A`

has rank`2`

, then`A[(2:10, :)]`

must be equivalent to`A[2:10, :]`

. And so on and so forth.Providing a single nonnegative integer

`i`

as a single-axis index must index the same elements as the slice`i:i+1`

.Providing a single negative integer

`i`

as a single-axis index must index the same elements as the slice`n+i:n+i+1`

, where`n`

is the axis (dimension) size.Providing a single integer as a single-axis index must reduce the number of array dimensions by

`1`

(i.e., the array rank must decrease by one; if`A`

has rank`2`

,`rank(A)-1 == rank(A[0, :])`

). In particular, a selection tuple with the`m`

th element an integer (and all other entries`:`

) indexes a sub-array with rank`N-1`

.Note

When providing a single integer as a single-axis index to an array of rank

`1`

, the result should be an array of rank`0`

, not a NumPy scalar. Note that this behavior differs from NumPy.Providing a slice must retain array dimensions (i.e., the array rank must remain the same;

`rank(A) == rank(A[:])`

).Providing ellipsis must apply

`:`

to each dimension necessary to index all dimensions (e.g., if`A`

has rank`4`

,`A[1:, ..., 2:5] == A[1:, :, :, 2:5]`

). Only a single ellipsis must be allowed. An`IndexError`

exception must be raised if more than one ellipsis is provided.Providing an empty tuple or an ellipsis to an array of rank

`0`

must result in an array of the same rank (i.e., if`A`

has rank`0`

,`A == A[()]`

and`A == A[...]`

).Note

This behavior differs from NumPy where providing an empty tuple to an array of rank

`0`

returns a NumPy scalar.Each

`None`

in the selection tuple must expand the dimensions of the resulting selection by one dimension of size`1`

. The position of the added dimension must be the same as the position of`None`

in the selection tuple.Note

Expanding dimensions can be equivalently achieved via repeated invocation of

`expand_dims()`

.Except in the case of providing a single ellipsis (e.g.,

`A[2:10, ...]`

or`A[1:, ..., 2:5]`

), the number of provided single-axis indexing expressions (excluding`None`

) should equal`N`

. For example, if`A`

has rank`2`

, a single-axis indexing expression should be explicitly provided for both axes (e.g.,`A[2:10, :]`

). An`IndexError`

exception should be raised if the number of provided single-axis indexing expressions (excluding`None`

) is less than`N`

.Note

Some libraries, such as SymPy, support flat indexing (i.e., providing a single-axis indexing expression to a higher-dimensional array). That practice is not supported here.

To perform flat indexing, use

`reshape(x, (-1,))[integer]`

.An

`IndexError`

exception must be raised if the number of provided single-axis indexing expressions (excluding`None`

) is greater than`N`

.Note

This specification leaves unspecified the behavior of providing a slice which attempts to select elements along a particular axis, but whose starting index is out-of-bounds.

*Rationale: this is consistent with bounds-checking for single-axis indexing. An implementation may choose to set the axis (dimension) size of the result array to*`0`

*, raise an exception, return junk values, or some other behavior depending on device requirements and performance considerations.*

## Boolean Array Indexing¶

Data-dependent output shape

For common boolean array use cases (e.g., using a dynamically-sized boolean array mask to filter the values of another array), the shape of the output array is data-dependent; hence, array libraries which build computation graphs (e.g., JAX, Dask, etc.) may find boolean array indexing difficult to implement. Accordingly, such libraries may choose to omit boolean array indexing. See Data-dependent output shapes section for more details.

An array must support indexing where the **sole index** is an `M`

-dimensional boolean array `B`

with shape `S1 = (s1, ..., sM)`

according to the following rules. Let `A`

be an `N`

-dimensional array with shape `S2 = (s1, ..., sM, ..., sN)`

.

Note

The prohibition against combining boolean array indices with other single-axis indexing expressions includes the use of

`None`

. To expand dimensions of the returned array, use repeated invocation of`expand_dims()`

.

If

`N >= M`

, then`A[B]`

must replace the first`M`

dimensions of`A`

with a single dimension having a size equal to the number of`True`

elements in`B`

. The values in the resulting array must be in row-major (C-style order); this is equivalent to`A[nonzero(B)]`

.Note

For example, if

`N == M == 2`

, indexing`A`

via a boolean array`B`

will return a one-dimensional array whose size is equal to the number of`True`

elements in`B`

.If

`N < M`

, then an`IndexError`

exception must be raised.The size of each dimension in

`B`

must equal the size of the corresponding dimension in`A`

or be`0`

, beginning with the first dimension in`A`

. If a dimension size does not equal the size of the corresponding dimension in`A`

and is not`0`

, then an`IndexError`

exception must be raised.The elements of a boolean index array must be iterated in row-major, C-style order, with the exception of zero-dimensional boolean arrays.

A zero-dimensional boolean index array (equivalent to

`True`

or`False`

) must follow the same axis replacement rules stated above. Namely, a zero-dimensional boolean index array removes zero dimensions and adds a single dimension of length`1`

if the index array’s value is`True`

and of length`0`

if the index array’s value is`False`

. Accordingly, for a zero-dimensional boolean index array`B`

, the result of`A[B]`

has shape`S = (1, s1, ..., sN)`

if the index array’s value is`True`

and has shape`S = (0, s1, ..., sN)`

if the index array’s value is`False`

.

## Return Values¶

The result of an indexing operation (e.g., multi-axis indexing, boolean array indexing, etc) must be an array of the same data type as the indexed array.

Note

The specified return value behavior includes indexing operations which return a single value (e.g., accessing a single element within a one-dimensional array).