Array API compatibility library

This is a small wrapper around common array libraries that is compatible with the Array API standard. Currently, NumPy, CuPy, PyTorch, Dask, JAX, ndonnx, and Sparse are supported. If you want support for other array libraries, or if you encounter any issues, please open an issue.

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

Currently all libraries here are implemented against the 2023.12 version of the standard.

Installation

array-api-compat is available on both PyPI

python -m pip install array-api-compat

and conda-forge

conda install --channel conda-forge array-api-compat

Usage

The typical usage of this library will be to get the corresponding array API compliant namespace from the input arrays using array_namespace(), like

def your_function(x, y):
    xp = array_api_compat.array_namespace(x, y)
    # Now use xp as the array library namespace
    return xp.mean(x, axis=0) + 2*xp.std(y, axis=0)

If you wish to have library-specific code-paths, you can import the corresponding wrapped namespace for each library, like

import array_api_compat.numpy as np
import array_api_compat.cupy as cp
import array_api_compat.torch as torch
import array_api_compat.dask as da

Note

There are no array_api_compat submodules for JAX, sparse, or ndonnx. These support for these libraries is contained in the libraries themselves (JAX support is in the jax.numpy module in JAX v0.4.32 or newer, and in the jax.experimental.array_api module for older JAX versions). The array-api-compat support for these libraries consists of supporting them in the helper functions.

Each will include all the functions from the normal NumPy/CuPy/PyTorch/dask.array namespace, except that functions that are part of the array API are wrapped so that they have the correct array API behavior. In each case, the array object used will be the same array object from the wrapped library.

Difference between array_api_compat and array_api_strict

array_api_strict is a strict minimal implementation of the array API standard, formerly known as numpy.array_api (see NEP 47). For example, array_api_strict does not include any functions that are not part of the array API specification, and will explicitly disallow behaviors that are not required by the spec (e.g., cross-kind type promotions). (cupy.array_api is similar to array_api_strict)

array_api_compat, on the other hand, is just an extension of the corresponding array library namespaces with changes needed to be compliant with the array API. It includes all additional library functions not mentioned in the spec, and allows any library behaviors not explicitly disallowed by it, such as cross-kind casting.

In particular, unlike array_api_strict, this package does not use a separate Array object, but rather just uses the corresponding array library array objects (numpy.ndarray, cupy.ndarray, torch.Tensor, etc.) directly. This is because those are the objects that are going to be passed as inputs to functions by end users. This does mean that a few behaviors cannot be wrapped (see below), but most of the array API functional, so this does not affect most things.

Array consuming library authors coding against the array API may wish to test against array_api_strict to ensure they are not using functionality outside of the standard, but prefer this implementation for the default behavior for end-users.

Vendoring

This library supports vendoring as an installation method. To vendor the library, simply copy array_api_compat into the appropriate place in the library, like

cp -R array_api_compat/ mylib/vendored/array_api_compat

You may also rename it to something else if you like (nowhere in the code references the name “array_api_compat”).

Alternatively, the library may be installed as dependency from PyPI.

Scope

At this time, the scope of array-api-compat is limited to wrapping array libraries so that they can comply with the array API standard. This includes a small set of helper functions which may be useful to most users of array-api-compat, for instance, functions that provide meta-functionality to aid in supporting the array API, or functions that are necessary to work around wrapping limitations for certain libraries.

Things that are out-of-scope include:

  • functions that have not yet been standardized (although note that functions that are in a draft version of the standard are in scope),

  • functions that are complicated to implement correctly/maintain,

  • anything that requires the use of non-Python code.

If you want a function that is not in array-api-compat that isn’t part of the standard, you should request it either for inclusion in the standard or in specific array libraries.

Why is the scope limited in this way? Firstly, we want to keep array-api-compat as primarily a polyfill compatibility shim. The goal is to let consuming libraries use the array API today, even with array libraries that do not yet fully support it. In an ideal world—one that we hope to eventually see in the future—array-api-compat would be unnecessary, because every array library would fully support the standard.

The inclusion of non-standardized functions in array-api-compat would undermine this goal. But much more importantly, it would also undermine the goals of the Data APIs Consortium. The Consortium creates the array API standard via the consensus of stakeholders from various array libraries and users. If a not-yet-standardized function were included in array-api-compat, it would become de facto standard, bypassing the decision making processes of the Consortium.

Secondly, we want to keep array-api-compat as minimal as possible, so that it is easy for libraries to add as a (possibly vendored) dependency.

Thirdly, array-api-compat has a relatively small development team. Pull requests to array-api-compat would not necessarily receive the same stringent level of scrutiny that changes to established array libraries like NumPy or PyTorch would. For wrapped standard functions, this is fine, since the wrappers typically just clean up a few small inconsistencies from the standard, leaving the complexity of the implementation to the base array library function. Furthermore, standard functions are tested by the rigorous array-api-tests test suite. For this reason, functions that require complex implementations are generally out-of-scope and should be preferred to be implemented in upstream array libraries.