1
1
# Array API compatibility library
2
2
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 ) .
18
8
19
9
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.
21
14
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.
29
20
30
21
## Usage
31
22
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
33
25
34
26
``` 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 )
36
31
```
37
32
38
- with
33
+ If you wish to have library-specific code-paths, you can import the
34
+ corresponding wrapped namespace for each library, like
39
35
40
36
``` py
41
37
import array_api_compat.numpy as np
42
- ```
43
38
44
- and replace
45
-
46
- ``` py
47
- import cupy as cp
48
- ```
49
-
50
- with
51
-
52
- ``` py
53
39
import array_api_compat.cupy as cp
54
- ```
55
40
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
+ ```
60
43
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.
61
77
62
78
## Helper Functions
63
79
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:
68
83
69
84
- ` is_array_api_obj(x) ` : Return ` True ` if ` x ` is an array API compatible array
70
85
object.
71
86
72
87
- ` 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).
76
93
77
94
- ` device(x) ` : Equivalent to
78
95
[ ` x.device ` ] ( https://data-apis.org/array-api/latest/API_specification/generated/signatures.array_object.array.device.html )
79
96
in the array API specification. Included because ` numpy.ndarray ` does not
80
97
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" ` .
82
99
83
100
- ` to_device(x, device, /, *, stream=None) ` : Equivalent to
84
101
[ ` 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
88
106
[ Device] ( https://docs.cupy.dev/en/stable/reference/generated/cupy.cuda.Device.html )
89
107
and
90
108
[ 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).
92
112
93
113
- ` size(x) ` : Equivalent to
94
114
[ ` 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:
102
122
There are some known differences between this library and the array API
103
123
specification:
104
124
125
+ ### NumPy and CuPy
126
+
105
127
- The array methods ` __array_namespace__ ` , ` device ` (for NumPy), ` to_device ` ,
106
128
and ` mT ` are not defined. This reuses ` np.ndarray ` and ` cp.ndarray ` and we
107
129
don't want to monkeypatch or wrap it. The helper functions ` device() ` and
108
130
` to_device() ` are provided to work around these missing methods (see above).
109
131
` x.mT ` can be replaced with ` xp.linalg.matrix_transpose(x) ` .
110
132
` get_namespace(x) ` should be used instead of ` x.__array_namespace__ ` .
111
133
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 )
116
139
117
140
- Functions which are not wrapped may not have the same type annotations
118
141
as the spec.
119
142
120
143
- Functions which are not wrapped may not use positional-only arguments.
121
144
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
+
122
190
## Vendoring
123
191
124
192
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
140
208
objects, rather than wrapping or extending them. This means that the functions
141
209
need to accept and return ` np.ndarray ` for NumPy and ` cp.ndarray ` for CuPy.
142
210
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
148
220
149
221
``` py
150
222
# In array_api_compat/common/_aliases.py
@@ -154,10 +226,10 @@ def acos(x, /, xp):
154
226
```
155
227
156
228
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
161
233
162
234
``` py
163
235
# In array_api_compat/numpy/_aliases.py
@@ -184,6 +256,15 @@ acos = get_xp(cp)(_aliases.acos)
184
256
```
185
257
186
258
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