Skip to content

Commit 0565dee

Browse files
committed
Update the README
1 parent 6354cd9 commit 0565dee

File tree

1 file changed

+150
-69
lines changed

1 file changed

+150
-69
lines changed

README.md

Lines changed: 150 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,114 @@
11
# Array API compatibility library
22

3-
This is a small wrapper around NumPy and CuPy that is compatible with the
4-
[Array API standard](https://data-apis.org/array-api/latest/). See also [NEP
5-
47](https://numpy.org/neps/nep-0047-array-api-standard.html).
6-
7-
Unlike `numpy.array_api`, this is not a strict minimal implementation of the
8-
Array API, but rather just an extension of the main NumPy and CuPy namespaces
9-
with changes needed to be compliant with the Array API.
10-
11-
Library authors using the Array API may wish to test against `numpy.array_api`
12-
to ensure they are not using functionality outside of the standard, but prefer
13-
this implementation for the default when working with NumPy or CuPy arrays.
14-
15-
See https://numpy.org/doc/stable/reference/array_api.html for a full list of
16-
changes. In particular, unlike `numpy.array_api`, this package does not use a
17-
separate Array object, but rather just uses `numpy.ndarray` directly.
3+
This is a small wrapper around common array libraries that is compatible with
4+
the [Array API standard](https://data-apis.org/array-api/latest/). Currently,
5+
NumPy, CuPy, and PyTorch are supported. If you want support for other array
6+
libraries, or if you encounter any issues, please [open an
7+
issue](https://github.com/data-apis/array-api-compat/issues).
188

199
Note that some of the functionality in this library is backwards incompatible
20-
with NumPy.
10+
with the corresponding wrapped libraries. The end-goal is to eventually make
11+
each array library itself fully compatible with the array API, but this
12+
requires making backwards incompatible changes in many cases, so this will
13+
take some time.
2114

22-
This library also supports CuPy in addition to NumPy. If you want support for
23-
other array libraries, please [open an
24-
issue](https://github.com/data-apis/array-api-compat/issues).
25-
26-
Library authors using the Array API may wish to test against `numpy.array_api`
27-
to ensure they are not using functionality outside of the standard, but prefer
28-
this implementation for end users who use NumPy arrays.
15+
Currently all libraries here are implemented against the 2021.12 version of
16+
the standard. Support for the [2022.12
17+
version](https://data-apis.org/array-api/2022.12/changelog.html), which adds
18+
complex number support as well as several additional functions, will be added
19+
later this year.
2920

3021
## Usage
3122

32-
To use this library replace
23+
The typical usage of this library will be to get the corresponding array API
24+
compliant namespace from the input arrays using `get_namespace()`, like
3325

3426
```py
35-
import numpy as np
27+
def your_function(x, y):
28+
xp = array_api_compat.get_namespace(x, y)
29+
# Now use xp as the array library namespace
30+
return xp.mean(x, axis=0) + 2*xp.std(y, axis=0)
3631
```
3732

38-
with
33+
If you wish to have library-specific code-paths, you can import the
34+
corresponding wrapped namespace for each library, like
3935

4036
```py
4137
import array_api_compat.numpy as np
42-
```
4338

44-
and replace
45-
46-
```py
47-
import cupy as cp
48-
```
49-
50-
with
51-
52-
```py
5339
import array_api_compat.cupy as cp
54-
```
5540

56-
Each will include all the functions from the normal NumPy/CuPy namespace,
57-
except that functions that are part of the array API are wrapped so that they
58-
have the correct array API behavior. In each case, the array object used will
59-
be the same array object from the wrapped library.
41+
import array_api_compat.torch as torch
42+
```
6043

44+
Each will include all the functions from the normal NumPy/CuPy/Torch
45+
namespace, except that functions that are part of the array API are wrapped so
46+
that they have the correct array API behavior. In each case, the array object
47+
used will be the same array object from the wrapped library.
48+
49+
## Difference between `array_api_compat` and `numpy.array_api`
50+
51+
`numpy.array_api` is a strict minimal implementation of the Array API (see
52+
[NEP 47](https://numpy.org/neps/nep-0047-array-api-standard.html)). For
53+
example, `numpy.array_api` does not include any functions that are not part of
54+
the array API specification, and will explicitly disallow behaviors that are
55+
not required by the spec (e.g., [cross-kind type
56+
promotions](https://data-apis.org/array-api/latest/API_specification/type_promotion.html)).
57+
(`cupy.array_api` is similar to `numpy.array_api`)
58+
59+
`array_api_compat`, on the other hand, is just an extension of the
60+
corresponding array library namespaces with changes needed to be compliant
61+
with the array API. It includes all additional library functions not mentioned
62+
in the spec, and allows any library behaviors not explicitly disallowed by it,
63+
such as cross-kind casting.
64+
65+
In particular, unlike `numpy.array_api`, this package does not use a separate
66+
`Array` object, but rather just uses the corresponding array library array
67+
objects (`numpy.ndarray`, `cupy.ndarray`, `torch.Tensor`, etc.) directly. This
68+
is because those are the objects that are going to be passed as inputs to
69+
functions by end users. This does mean that a few behaviors cannot be wrapped
70+
(see below), but most of the array API functional, so this does not affect
71+
most things.
72+
73+
Array consuming library authors coding against the array API may wish to test
74+
against `numpy.array_api` to ensure they are not using functionality outside
75+
of the standard, but prefer this implementation for the default behavior for
76+
end-users.
6177

6278
## Helper Functions
6379

64-
In addition to the default NumPy/CuPy namespace and functions in the array API
65-
specification, there are several helper functions
66-
included that aren't part of the specification but which are useful for using
67-
the array API:
80+
In addition to the wrapped library namespaces and functions in the array API
81+
specification, there are several helper functions included here that aren't
82+
part of the specification but which are useful for using the array API:
6883

6984
- `is_array_api_obj(x)`: Return `True` if `x` is an array API compatible array
7085
object.
7186

7287
- `get_namespace(*xs)`: Get the corresponding array API namespace for the
73-
arrays `xs`. If the arrays are NumPy or CuPy arrays, the returned namespace
74-
will be `array_api_compat.numpy` or `array_api_compat.cupy` so that it is
75-
array API compatible.
88+
arrays `xs`. For example, if the arrays are NumPy arrays, the returned
89+
namespace will be `array_api_compat.numpy`. Note that this function will
90+
also work for namespaces that aren't supported by this compat library but
91+
which do support the array API (i.e., arrays that have the
92+
`__array_namespace__` attribute).
7693

7794
- `device(x)`: Equivalent to
7895
[`x.device`](https://data-apis.org/array-api/latest/API_specification/generated/signatures.array_object.array.device.html)
7996
in the array API specification. Included because `numpy.ndarray` does not
8097
include the `device` attribute and this library does not wrap or extend the
81-
array object. Note that for NumPy, `device` is always `"cpu"`.
98+
array object. Note that for NumPy, `device(x)` is always `"cpu"`.
8299

83100
- `to_device(x, device, /, *, stream=None)`: Equivalent to
84101
[`x.to_device`](https://data-apis.org/array-api/latest/API_specification/generated/signatures.array_object.array.to_device.html).
85-
Included because neither NumPy's nor CuPy's ndarray objects include this
86-
method. For NumPy, this function effectively does nothing since the only
87-
supported device is the CPU, but for CuPy, this method supports CuPy CUDA
102+
Included because neither NumPy's, CuPy's, nor PyTorch's array objects
103+
include this method. For NumPy, this function effectively does nothing since
104+
the only supported device is the CPU, but for CuPy, this method supports
105+
CuPy CUDA
88106
[Device](https://docs.cupy.dev/en/stable/reference/generated/cupy.cuda.Device.html)
89107
and
90108
[Stream](https://docs.cupy.dev/en/stable/reference/generated/cupy.cuda.Stream.html)
91-
objects.
109+
objects. For PyTorch, this is the same as
110+
[`x.to(device)`](https://pytorch.org/docs/stable/generated/torch.Tensor.to.html)
111+
(the `stream` argument is not supported in PyTorch).
92112

93113
- `size(x)`: Equivalent to
94114
[`x.size`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.array.size.html#array_api.array.size),
@@ -102,23 +122,71 @@ the array API:
102122
There are some known differences between this library and the array API
103123
specification:
104124

125+
### NumPy and CuPy
126+
105127
- The array methods `__array_namespace__`, `device` (for NumPy), `to_device`,
106128
and `mT` are not defined. This reuses `np.ndarray` and `cp.ndarray` and we
107129
don't want to monkeypatch or wrap it. The helper functions `device()` and
108130
`to_device()` are provided to work around these missing methods (see above).
109131
`x.mT` can be replaced with `xp.linalg.matrix_transpose(x)`.
110132
`get_namespace(x)` should be used instead of `x.__array_namespace__`.
111133

112-
- NumPy value-based casting for scalars will be in effect unless explicitly
113-
disabled with the environment variable NPY_PROMOTION_STATE=weak or
114-
np._set_promotion_state('weak') (requires NumPy 1.24 or newer, see NEP 50
115-
and https://github.com/numpy/numpy/issues/22341)
134+
- Value-based casting for scalars will be in effect unless explicitly disabled
135+
with the environment variable `NPY_PROMOTION_STATE=weak` or
136+
`np._set_promotion_state('weak')` (requires NumPy 1.24 or newer, see [NEP
137+
50](https://numpy.org/neps/nep-0050-scalar-promotion.html) and
138+
https://github.com/numpy/numpy/issues/22341)
116139

117140
- Functions which are not wrapped may not have the same type annotations
118141
as the spec.
119142

120143
- Functions which are not wrapped may not use positional-only arguments.
121144

145+
### PyTorch
146+
147+
- Like NumPy/CuPy, we do not wrap the `torch.Tensor` object. It is missing the
148+
`__array_namespace__` and `to_device` methods, so the corresponding helper
149+
functions `get_namespace()` and `to_device()` in this library should be
150+
used instead (see above).
151+
152+
- The `x.size` attribute on `torch.Tensor` is a function that behaves
153+
differently from
154+
[`x.size`](https://data-apis.org/array-api/draft/API_specification/generated/array_api.array.size.html)
155+
in the spec. Use the `size(x)` helper function as a portable workaround (see
156+
above).
157+
158+
- The `linalg` extension is not yet implemented.
159+
160+
- PyTorch does not have unsigned integer types other than `uint8`, and no
161+
attempt is made to implement them here.
162+
163+
- PyTorch has type promotion semantics that differ from the array API
164+
specification for 0-D tensor objects. The array functions in this wrapper
165+
library do work around this, but the operators on the Tensor object do not,
166+
as no operators or methods on the Tensor object are modified. If this is a
167+
concern, use the functional form instead of the operator form, e.g., `add(x,
168+
y)` instead of `x + y`.
169+
170+
- [`unique_all()`](https://data-apis.org/array-api/late
171+
st/API_specification/generated/array_api.unique_all.html#array_api.unique_all)
172+
is not implemented, due to the fact that `torch.unique` does not support
173+
returning the `indices` array. The other
174+
[`unique_*`](https://data-apis.org/array-api/latest/API_specification/set_functions.html)
175+
functions are implemented.
176+
177+
- Slices do not support negative steps.
178+
179+
- [`std()`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.std.html#array_api.std)
180+
and
181+
[`var()`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.var.html#array_api.var)
182+
do not support floating-point `correction`.
183+
184+
- The `stream` argument of the `to_device()` helper (see above) is not
185+
supported.
186+
187+
- As with NumPy, type annotations and positional-only arguments may not
188+
exactly match the spec for functions that are not wrapped at all.
189+
122190
## Vendoring
123191

124192
This library supports vendoring as an installation method. To vendor the
@@ -140,11 +208,15 @@ As noted before, the goal of this library is to reuse the NumPy and CuPy array
140208
objects, rather than wrapping or extending them. This means that the functions
141209
need to accept and return `np.ndarray` for NumPy and `cp.ndarray` for CuPy.
142210

143-
Each namespace (`array_api_compat.numpy` and `array_api_compat.cupy`) is
144-
populated with the normal library namespace (like `from numpy import *`). Then
145-
specific functions are replaced with wrapped variants. Wrapped functions that
146-
have the same logic between NumPy and CuPy (which is most functions) are in
147-
`array_api_compat/common/`. These functions are defined like
211+
Each namespace (`array_api_compat.numpy`, `array_api_compat.cupy`, and
212+
`array_api_compat.torch`) is populated with the normal library namespace (like
213+
`from numpy import *`). Then specific functions are replaced with wrapped
214+
variants.
215+
216+
Since NumPy and CuPy are nearly identical in behavior, most wrapping logic can
217+
be shared between them. Wrapped functions that have the same logic between
218+
NumPy and CuPy are in `array_api_compat/common/`.
219+
These functions are defined like
148220

149221
```py
150222
# In array_api_compat/common/_aliases.py
@@ -154,10 +226,10 @@ def acos(x, /, xp):
154226
```
155227

156228
The `xp` argument refers to the original array namespace (either `numpy` or
157-
`cupy`). Then in the specific `array_api_compat/numpy` and
158-
`array_api_compat/cupy` namespace, the `get_xp` decorator is applied to these
159-
functions, which automatically removes the `xp` argument from the function
160-
signature and replaces it with the corresponding array library, like
229+
`cupy`). Then in the specific `array_api_compat/numpy/` and
230+
`array_api_compat/cupy/` namespaces, the `@get_xp` decorator is applied to
231+
these functions, which automatically removes the `xp` argument from the
232+
function signature and replaces it with the corresponding array library, like
161233

162234
```py
163235
# In array_api_compat/numpy/_aliases.py
@@ -184,6 +256,15 @@ acos = get_xp(cp)(_aliases.acos)
184256
```
185257

186258
Since NumPy and CuPy are nearly identical in their behaviors, this allows
187-
writing the wrapping logic for both libraries only once. If support is added
188-
for other libraries which differ significantly from NumPy, their wrapper code
189-
should go in their specific sub-namespace instead of `common/`.
259+
writing the wrapping logic for both libraries only once.
260+
261+
PyTorch uses a similar layout in `array_api_compat/torch/`, but it differs
262+
enough from NumPy/CuPy that very few common wrappers for those libraries are
263+
reused.
264+
265+
See https://numpy.org/doc/stable/reference/array_api.html for a full list of
266+
changes from the base NumPy (the differences for CuPy are nearly identical). A
267+
corresponding document does not yet exist for PyTorch, but you can examine the
268+
various comments in the
269+
[implementation](https://github.com/data-apis/array-api-compat/blob/main/array_api_compat/torch/_aliases.py)
270+
to see what functions and behaviors have been wrapped.

0 commit comments

Comments
 (0)