1
- # NumPy Array API compatibility library
1
+ # Array API compatibility library
2
2
3
- This is a small wrapper around NumPy that is compatible with the [ Array API
4
- standard] ( https://data-apis.org/array-api/latest/ ) . See also [ NEP 47] ( https://numpy.org/neps/nep-0047-array-api-standard.html ) .
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 ) .
5
6
6
7
Unlike ` numpy.array_api ` , this is not a strict minimal implementation of the
7
- Array API, but rather just an extension of the main NumPy namespace with
8
- changes needed to be compliant with the Array API. See
9
- https://numpy.org/doc/stable/reference/array_api.html for a full list of
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
10
16
changes. In particular, unlike ` numpy.array_api ` , this package does not use a
11
17
separate Array object, but rather just uses ` numpy.ndarray ` directly.
12
18
13
19
Note that some of the functionality in this library is backwards incompatible
14
20
with NumPy.
15
21
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
+
16
26
Library authors using the Array API may wish to test against ` numpy.array_api `
17
27
to ensure they are not using functionality outside of the standard, but prefer
18
28
this implementation for end users who use NumPy arrays.
@@ -28,5 +38,145 @@ import numpy as np
28
38
with
29
39
30
40
``` py
31
- import numpy_array_api_compat as np
41
+ import array_api_compat.numpy as np
32
42
```
43
+
44
+ and replace
45
+
46
+ ``` py
47
+ import cupy as cp
48
+ ```
49
+
50
+ with
51
+
52
+ ``` py
53
+ import array_api_compat.cupy as cp
54
+ ```
55
+
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 thew same array object from the wrapped library.
60
+
61
+
62
+ ## Helper Functions
63
+
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:
68
+
69
+ - ` is_array_api_obj(x) ` : Return ` True ` if ` x ` is an array API compatible array
70
+ object.
71
+
72
+ - ` 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.
76
+
77
+ - ` device(x) ` : Equivalent to
78
+ [ ` x.device ` ] ( https://data-apis.org/array-api/latest/API_specification/generated/signatures.array_object.array.device.html )
79
+ in the array API specification. Included because ` numpy.ndarray ` does not
80
+ include the ` device ` attribute and this library does not wrap or extend the
81
+ array object. Note that for NumPy, ` device ` is always ` "cpu" ` .
82
+
83
+ - ` to_device(x, device, /, *, stream=None) ` : Equivalent to
84
+ [ ` 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
88
+ [ Device] ( https://docs.cupy.dev/en/stable/reference/generated/cupy.cuda.Device.html )
89
+ and
90
+ [ Stream] ( https://docs.cupy.dev/en/stable/reference/generated/cupy.cuda.Stream.html )
91
+ objects.
92
+
93
+ ## Known Differences from the Array API Specification
94
+
95
+ There are some known differences between this library and the array API
96
+ specification:
97
+
98
+ - The array methods ` __array_namespace__ ` , ` device ` (for NumPy), ` to_device ` ,
99
+ and ` mT ` are not defined. This reuses ` np.ndarray ` and ` cp.ndarray ` and we
100
+ don't want to monkeypatch or wrap it. The helper functions ` device() ` and
101
+ ` to_device() ` are provided to work around these missing methods (see above).
102
+ ` x.mT ` can be replaced with ` xp.linalg.matrix_transpose(x) ` .
103
+ ` get_namespace(x) ` should be used instead of ` x.__array_namespace__ ` .
104
+
105
+ - NumPy value-based casting for scalars will be in effect unless explicitly
106
+ disabled with the environment variable NPY_PROMOTION_STATE=weak or
107
+ np._ set_promotion_state('weak') (requires NumPy 1.24 or newer, see NEP 50
108
+ and https://github.com/numpy/numpy/issues/22341 )
109
+
110
+ - Functions which are not wrapped may not have the same type annotations
111
+ as the spec.
112
+
113
+ - Functions which are not wrapped may not use positional-only arguments.
114
+
115
+ ## Vendoring
116
+
117
+ This library supports vendoring as an installation method. To vendor the
118
+ library, simply copy ` array_api_compat ` into the appropriate place in the
119
+ library, like
120
+
121
+ ```
122
+ cp -R array_api_compat/ mylib/vendored/array_api_compat
123
+ ```
124
+
125
+ You may also rename it to something else if you like (nowhere in the code
126
+ references the name "array_api_compat").
127
+
128
+ Alternatively, the library may be installed as dependency on PyPI.
129
+
130
+ ## Implementation
131
+
132
+ As noted before, the goal of this library is to reuse the NumPy and CuPy array
133
+ objects, rather than wrapping or extending them. This means that the functions
134
+ need to accept and return ` np.ndarray ` for NumPy and ` cp.ndarray ` for CuPy.
135
+
136
+ Each namespace (` array_api_compat.numpy ` and ` array_api_compat.cupy ` ) is
137
+ populated with the normal library namespace (like ` from numpy import * ` ). Then
138
+ specific functions are replaced with wrapped variants. Wrapped functions that
139
+ have the same logic between NumPy and CuPy (which is most functions) are in
140
+ ` array_api_compat/common/ ` . These functions are defined like
141
+
142
+ ``` py
143
+ # In array_api_compat/common/_aliases.py
144
+
145
+ def acos (x , / , xp ):
146
+ return xp.arccos(x)
147
+ ```
148
+
149
+ The ` xp ` argument refers to the original array namespace (either ` numpy ` or
150
+ ` cupy ` ). Then in the specific ` array_api_compat/numpy ` and
151
+ ` array_api_compat/cupy ` namespace, the ` get_xp ` decorator is applied to these
152
+ functions, which automatically removes the ` xp ` argument from the function
153
+ signature and replaces it with the corresponding array library, like
154
+
155
+ ``` py
156
+ # In array_api_compat/numpy/_aliases.py
157
+
158
+ from ..common import _aliases
159
+
160
+ import numpy as np
161
+
162
+ acos = get_xp(np)(_aliases.acos)
163
+ ```
164
+
165
+ This ` acos ` now has the signature ` acos(x, /) ` and calls ` numpy.arccos ` .
166
+
167
+ Similarly, for CuPy:
168
+
169
+ ``` py
170
+ # In array_api_compat/cupy/_aliases.py
171
+
172
+ from ..common import _aliases
173
+
174
+ import cupy as cp
175
+
176
+ acos = get_xp(cp)(_aliases.acos)
177
+ ```
178
+
179
+ Since NumPy and CuPy are nearly identical in their behaviors, this allows
180
+ writing the wrapping logic for both libraries only once. If support is added
181
+ for other libraries which differ significantly from NumPy, their wrapper code
182
+ should go in their specific sub-namespace instead of ` common/ ` .
0 commit comments