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