Skip to content

Commit a4e274b

Browse files
committed
Apply review comments
1 parent d91b322 commit a4e274b

2 files changed

Lines changed: 91 additions & 22 deletions

File tree

spec/2025.12/migration_guide.md

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,33 +29,39 @@ a set of tools and packages to help you with the migration process:
2929
### Array API Compat
3030

3131
GitHub: [array-api-compat](https://github.com/data-apis/array-api-compat)
32+
User group: Array Consumers
3233

3334
Although NumPy, Dask, CuPy, and PyTorch support the Array API Standard, there
3435
are still some corner cases where their behavior diverges from the standard.
3536
`array-api-compat` provides a compatibility layer to cover these cases as well.
3637
This is also accompanied by a few utility functions for easier introspection
37-
into array objects.
38+
into array objects. As an array consumer you can still rely on the original API
39+
while having access to the standard compatible one.
3840

3941

4042
(array-api-strict)=
4143

4244
### Array API Strict
4345

4446
GitHub: [array-api-strict](https://github.com/data-apis/array-api-strict)
47+
User group: Array Consumers, Array Producers (for testing)
4548

4649
`array-api-strict` is a library that provides a strict and minimal
47-
implementation of the Array API Standard. It is designed to be used as
48-
a reference implementation for testing and development purposes. By comparing
49-
your API calls with `array-api-strict` counterparts, you can ensure that your
50-
library is fully compliant with the standard and can serve as a reliable
51-
reference for other developers in the ecosystem.
50+
implementation of the Array API Standard. For array producers it is designed
51+
to be used as a reference implementation for testing and development purposes.
52+
You can compare your API calls with `array-api-strict` counterparts,
53+
and ensure that your library is fully compliant with the standard and can
54+
serve as a reliable reference for other developers in the ecosystem.
55+
For consumers you can use `array-api-strict` during the development as an
56+
array provider to ensure your code uses API compliant with the standard.
5257

5358

5459
(array-api-tests)=
5560

5661
### Array API Test
5762

5863
GitHub: [array-api-tests](https://github.com/data-apis/array-api-tests)
64+
User group: Array Producers
5965

6066
`array-api-tests` is a collection of tests that can be used to verify the
6167
compliance of your library with the Array API Standard. It includes tests
@@ -69,6 +75,7 @@ standard and can be used with compatible array consumers libraries.
6975
### Array API Extra
7076

7177
GitHub: [array-api-extra](https://github.com/data-apis/array-api-extra)
78+
User group: Array Consumers
7279

7380
`array-api-extra` is a collection of additional utilities and tools that are
7481
missing from the Array API Standard but can be useful for compliant array
@@ -103,7 +110,8 @@ reference implementation.
103110
{ref}`array-api-tests` is a test suite which verifies that your API
104111
for adhering to the standard. For each function or method it confirms
105112
it's importable, verifies the signature, and generates multiple test
106-
cases with hypothesis package and runs asserts for the outputs.
113+
cases with [hypothesis](https://hypothesis.readthedocs.io/en/latest/)
114+
package and runs asserts for the outputs.
107115

108116
The setup details are enclosed in the GitHub repository, so here we
109117
cover only the minimal workflow:
@@ -113,17 +121,20 @@ cover only the minimal workflow:
113121
variable to your package import name.
114122
3. Inside the `array-api-tests` directory run `pytest` command. There are
115123
multiple useful options delivered by the test suite, a few worth mentioning:
116-
- `--max-examples=2` - maximal number of test cases to generate by the
124+
- `--max-examples=1000` - maximal number of test cases to generate by the
117125
hypothesis. This allows you to balance between execution time of the test
118-
suite and thoroughness of the testing.
126+
suite and thoroughness of the testing. It's advised to use as many examples
127+
as the time buget can fit. Each test case is a random combination of
128+
possible inputs: the more cases, the higher chance of finding an unsupported
129+
edge case.
119130
- With `--xfails-file` option you can describe which tests are expected to
120131
fail - it's impossible to get the whole API perfectly implemented on a
121132
first try, so tracking what still fails gives you more control over the
122133
state of your API.
123134
- `-o xfail_strict=<bool>` is often used with the previous one. If a test
124135
expected to fail actually passes (`XPASS`) then you can decide whether
125136
to ignore that fact or raise it as an error.
126-
- `--skips-file` for skipping files. At times some failing tests might stall
137+
- `--skips-file` for skipping tests. At times some failing tests might stall
127138
the execution time of the test suite - in that case the most convenient
128139
option is to skip these for the time being.
129140

@@ -138,15 +149,15 @@ A simpler, and more manual, way of testing the Array API coverage is to
138149
run your API calls along with {ref}`array-api-strict` Python implementation.
139150

140151
This way you can ensure the outputs coming from your API match the minimal
141-
reference implementation, but bare in mind you need to write the tests cases
152+
reference implementation, but bear in mind you need to write the tests cases
142153
yourself, so you need to also take into account the edge cases as well.
143154

144155

145156
(array-consumers)=
146157

147158
## Array Consumers
148159

149-
For array consumers the main premise is keep in mind that your **array
160+
For array consumers the main premise is to keep in mind that your **array
150161
manipulation operations should not lock in for a particular array producing
151162
library**. For instance, if you use NumPy for arrays, then your code could
152163
contain:
@@ -162,8 +173,9 @@ return np.dot(c, b)
162173

163174
The first step should be as simple as assigning `np` namespace to a dedicated
164175
namespace variable - the convention in the ecosystem is to name it `xp`. Then
165-
Making sure that each method and function call is something that Array API
166-
supports is vital (we will get to that soon):
176+
making sure that each method and function call is something that Array API
177+
supports is vital. `dot` is present in the NumPy's API but the standard doesn't
178+
support it. Let's use `tensordot` instead - both NumPy and the standard define it:
167179

168180
```python
169181
import numpy as np
@@ -180,12 +192,23 @@ Then replacing one backend with another one should rely on providing a different
180192
namespace, such as: `xp = torch`, e.g. via environment variable. This can be useful
181193
if you're writing a script or in your custom software. The other alternatives are:
182194

183-
- If you are building a library where the backend is determined by input arrays
184-
passed by the end-user, then a recommended way is to ask your input arrays for a
185-
namespace to use: `xp = arr.__array_namespace__()`
186-
- Each function you implement can have a namespace `xp` as a parameter in the
187-
signature. Then enforcing inputs to be of type by the provided backend can be
188-
achieved with `arg1 = xp.asarray(arg1)` for each input array.
195+
- If you are building a library where the backend is determined by input arrays,
196+
and your function accepts array arguments, then a recommended way is to ask
197+
your input arrays for a namespace to use: `xp = arr.__array_namespace__()`.
198+
If the given library doesn't have it, then [`array_api_compat.array_namespace()`](https://data-apis.org/array-api-compat/helper-functions.html#array_api_compat.array_namespace)
199+
should be used instead:
200+
```python
201+
def func(array1, scalar1, scalar2):
202+
xp = array1.__array_namespace__() # or array_namespace(array1)
203+
return xp.arange(scalar1, scalar2) @ array1
204+
```
205+
- For a function that accepts scalars and returns arrays, use namespace `xp` as
206+
a parameter in the signature. Then enforcing objects to be of type by the
207+
provided backend can be achieved with `arg1 = xp.asarray(arg1)` for each input:
208+
```python
209+
def func(s1, s2, xp):
210+
return xp.arange(s1, s2)
211+
```
189212

190213
If you're relying on NumPy, CuPy, PyTorch, Dask, or JAX then
191214
{ref}`array-api-compat` can come in handy for the transition. The compat layer

spec/2025.12/tutorial_basic.md

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,5 +108,51 @@ def is_converged(xprev, x, N, tol):
108108

109109
At this point the actual execution depends only on `xp` namespace,
110110
and replacing that one variable allow us to switch from e.g. NumPy arrays
111-
to a JAX execution on a GPU. This allows us to be more flexible, and, for
112-
example use lazy evaluation and JIT compile a loop body with JAX's JIT compilation.
111+
to a JAX execution on a GPU. This lets us be more flexible, and, for example,
112+
use lazy evaluation and JIT compile a loop body with JAX's JIT compilation:
113+
114+
```python
115+
import jax
116+
import jax.numpy as jnp
117+
118+
xp = jnp
119+
120+
def hits(G, max_iter=100, tol=1.0e-8, normalized=True):
121+
N = len(G)
122+
h = xp.full((N, 1), 1.0 / N)
123+
A = xp.asarray(G.A)
124+
# Power iteration: make up to max_iter iterations
125+
for _i in range(max_iter):
126+
h, a, conv = loop_body(h, A, N, tol)
127+
if conv:
128+
break
129+
else:
130+
raise Exception("Didn't converge")
131+
if normalized:
132+
h = h / xp.sum(xp.abs(h))
133+
a = a / xp.sum(xp.abs(a))
134+
return h, a
135+
136+
@jax.jit
137+
def loop_body(hprev, A, N, tol):
138+
a = hprev.mT @ A
139+
h = A @ a.mT
140+
h = h / xp.max(h)
141+
conv = is_converged(hprev, h, N, tol)
142+
return h, a, conv
143+
144+
def is_converged(xprev, x, N, tol):
145+
err = xp.sum(xp.abs(x - xprev))
146+
return err < xp.asarray(N * tol)
147+
148+
if __name__ == "__main__":
149+
150+
class Graph():
151+
def __init__(self):
152+
self.A = xp.ones((10, 10))
153+
def __len__(self):
154+
return len(self.A)
155+
156+
G = Graph()
157+
h, a = hits(G)
158+
```

0 commit comments

Comments
 (0)