Skip to content

Commit b222f96

Browse files
authored
Add array indexing specification (#46)
1 parent eb0a991 commit b222f96

File tree

2 files changed

+172
-0
lines changed

2 files changed

+172
-0
lines changed

spec/API_specification/indexing.md

+170
Original file line numberDiff line numberDiff line change
@@ -1 +1,171 @@
1+
.. _indexing:
2+
13
# Indexing
4+
5+
> Array API specification for indexing arrays.
6+
7+
A conforming implementation of the array API standard must adhere to the following conventions.
8+
9+
## Single-axis Indexing
10+
11+
To index a single array axis, an array must support standard Python indexing rules. Let `n` be the axis (dimension) size.
12+
13+
- An integer index must be an object satisfying [`operator.index`](https://www.python.org/dev/peps/pep-0357/) (e.g., `int`).
14+
15+
- Nonnegative indices must start at `0` (i.e., zero-based indexing).
16+
17+
- **Valid** nonnegative indices must reside on the half-open interval `[0, n)`.
18+
19+
.. note::
20+
21+
This specification does not require bounds checking. The behavior for out-of-bounds integer indices is left unspecified.
22+
23+
- 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).
24+
25+
.. note::
26+
27+
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.
28+
29+
- **Valid** negative indices must reside on the closed interval `[-n, -1]`.
30+
31+
.. note::
32+
33+
This specification does not require bounds checking. The behavior for out-of-bounds integer indices is left unspecified.
34+
35+
- A negative index `j` is related to a zero-based nonnegative index `i` via `i = n+j`.
36+
37+
- Colons `:` must be used for [slices](https://docs.python.org/3/library/functions.html#slice): `start:stop:step`, where `start` is inclusive and `stop` is exclusive.
38+
39+
### Slice Syntax
40+
41+
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.
42+
43+
```text
44+
A[:]
45+
A[i:]
46+
A[:j]
47+
A[i:k]
48+
A[::]
49+
A[i::]
50+
A[:j:]
51+
A[::k]
52+
A[i:j:]
53+
A[i::k]
54+
A[:j:k]
55+
A[i::k]
56+
A[i:j:k]
57+
```
58+
59+
.. note::
60+
61+
Slice syntax can be equivalently achieved using the Python built-in [`slice()`](https://docs.python.org/3/library/functions.html#slice) API. From the perspective from `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__`).
62+
63+
Using a slice to index a single array axis must select `m` elements with index values
64+
65+
```text
66+
i, i+k, i+2k, i+3k, ..., i+(m-1)k
67+
```
68+
69+
where
70+
71+
```text
72+
m = q + r
73+
```
74+
75+
and `q` and `r` (`r != 0`) are the quotient and remainder obtained by dividing `j-i` by `k`
76+
77+
```text
78+
j - i = qk + r
79+
```
80+
81+
such that
82+
83+
```text
84+
j > i + (m-1)k
85+
```
86+
87+
.. note::
88+
89+
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`.
90+
91+
.. note::
92+
93+
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.
94+
95+
Slice syntax must have the following defaults. Let `n` be the axis (dimension) size.
96+
97+
- If `k` is not provided (e.g., `0:10`), `k` must equal `1`.
98+
- If `k` is greater than `0` and `i` is not provided (e.g., `:10:2`), `i` must equal `0`.
99+
- If `k` is greater than `0` and `j` is not provided (e.g., `0::2`), `j` must equal `n`.
100+
- If `k` is less than `0` and `i` is not provided (e.g., `:10:-2`), `i` must equal `n-1`.
101+
- If `k` is less than `0` and `j` is not provided (e.g., `0::-2`), `j` must equal `-n-1`.
102+
103+
Using a slice to index a single array axis must adhere to the following rules. Let `n` be the axis (dimension) size.
104+
105+
- If `i` equals `j`, a slice must return an empty array, whose axis (dimension) size along the indexed axis is `0`.
106+
107+
- 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).
108+
109+
.. note::
110+
111+
This specification does not require "clipping" out-of-bounds indices (i.e., requiring the starting and stopping indices `i` and `j` be bound by `0` and `n`, respectively).
112+
113+
_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, raise an exception, return junk values, or some other behavior depending on device requirements and performance considerations._
114+
115+
.. note::
116+
117+
This specification leaves unspecified the behavior of indexing a single array axis with an out-of-bounds slice (i.e., a slice which does not select any array axis elements).
118+
119+
_Rationale: this is consistent with bounds checking for integer indexing; the behavior of out-of-bounds indices is left unspecified. Implementations may choose to return an empty array (whose axis (dimension) size along the indexed axis is `0`), raise an exception, or some other behavior depending on device requirements and performance considerations._
120+
121+
## Multi-axis Indexing
122+
123+
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`.
124+
125+
- 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]`).
126+
127+
.. note::
128+
129+
In Python, `x[(exp1, exp2, ..., expN)]` is equivalent to `x[exp1, exp2, ..., expN]`; the latter is syntactic sugar for the former.
130+
131+
- Providing a single nonnegative integer `i` as a single-axis index must index the same elements as the slice `i:i+1`.
132+
133+
- Providing a single negative integer `i` as a single-axis index must index the same elements as the slice `n+i:n`, where `n` is the axis (dimension) size.
134+
135+
- Providing a single integer as a single-axis index must reduce the number of array dimensions by `1` (i.e., the array rank should 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`.
136+
137+
- Providing a slice must retain array dimensions (i.e., the array rank must remain the same; `rank(A) == rank(A[:])`).
138+
139+
- If the number of provided single-axis indexing expressions is less than `N`, then `:` must be assumed for the remaining dimensions (e.g., if `A` has rank `2`, `A[2:10] == A[2:10, :]`).
140+
141+
- An `IndexError` exception must be raised if the number of provided single-axis indexing expressions is greater than `N`.
142+
143+
- Providing [ellipsis](https://docs.python.org/3/library/constants.html#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.
144+
145+
- The result of multi-axis indexing must be an array of the same data type as the indexed array.
146+
147+
.. note::
148+
149+
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.
150+
151+
_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._
152+
153+
## Boolean Array Indexing
154+
155+
An array must support indexing via a **single** `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)`.
156+
157+
- 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)]`.
158+
159+
.. note::
160+
161+
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`.
162+
163+
- If `N < M`, then an `IndexError` exception must be raised.
164+
165+
- 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.
166+
167+
- The elements of a boolean index array must be iterated in row-major, C-style order, with the exception of zero-dimensional boolean arrays.
168+
169+
- 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`.
170+
171+
- The result of indexing into an array via a boolean index array must be an array of the same data type as the indexed array.

spec/API_specification/searching_functions.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
.. _searching-functions:
2+
13
# Searching Functions
24

35
> Array API specification for functions for searching arrays.

0 commit comments

Comments
 (0)