From 7998fad43607ed2137ca81252500230bfb5112f1 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 8 Mar 2024 16:39:47 -0700 Subject: [PATCH 01/29] Add skeleton for HTML documentaion --- docs/Makefile | 23 ++++++++++++++++++++++ docs/_static/custom.css | 6 ++++++ docs/conf.py | 42 +++++++++++++++++++++++++++++++++++++++++ docs/index.rst | 20 ++++++++++++++++++++ docs/make.bat | 35 ++++++++++++++++++++++++++++++++++ docs/requirements.txt | 6 ++++++ 6 files changed, 132 insertions(+) create mode 100644 docs/Makefile create mode 100644 docs/_static/custom.css create mode 100644 docs/conf.py create mode 100644 docs/index.rst create mode 100644 docs/make.bat create mode 100644 docs/requirements.txt diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..11356c4f --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,23 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +livehtml: + sphinx-autobuild --open-browser --watch .. --port 0 -b html $(SOURCEDIR) $(ALLSPHINXOPTS) $(BUILDDIR)/html diff --git a/docs/_static/custom.css b/docs/_static/custom.css new file mode 100644 index 00000000..9b024274 --- /dev/null +++ b/docs/_static/custom.css @@ -0,0 +1,6 @@ +/* Makes the text look better on Mac retina displays (the Furo CSS disables*/ +/* subpixel antialiasing). */ +body { + -webkit-font-smoothing: auto; + -moz-osx-font-smoothing: auto; +} diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 00000000..e32778c6 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,42 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +import sys +import os +sys.path.insert(0, os.path.abspath('..')) + +project = 'Array API Compat' +copyright = '2024, Consortium for Python Data API Standards' +author = 'Consortium for Python Data API Standards' + +import array_api_compat +version = array_api_compat.__version__ + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + 'myst_parser', + 'sphinx_copybutton', +] + +templates_path = ['_templates'] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +myst_enable_extensions = ["dollarmath", "linkify"] + +# Make sphinx give errors for bad cross-references +nitpicky = True + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'furo' +html_static_path = ['_static'] + +html_css_files = ['custom.css'] diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 00000000..4b567a03 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,20 @@ +.. Array API Compat documentation master file, created by + sphinx-quickstart on Fri Mar 8 16:31:27 2024. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Array API Compat's documentation! +============================================ + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 00000000..32bb2452 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000..dbec7740 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,6 @@ +furo +linkify-it-py +myst-parser +sphinx +sphinx-copybutton +sphinx-autobuild From bdb6c9c2e53e7eb09da1462239e60531d58a7ea8 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 8 Mar 2024 16:41:15 -0700 Subject: [PATCH 02/29] Move README contents to index.md --- docs/index.md | 400 +++++++++++++++++++++++++++++++++++++++++++++++++ docs/index.rst | 20 --- 2 files changed, 400 insertions(+), 20 deletions(-) create mode 100644 docs/index.md delete mode 100644 docs/index.rst diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..88c37283 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,400 @@ +# Array API compatibility library + +This is a small wrapper around common array libraries that is compatible with +the [Array API standard](https://data-apis.org/array-api/latest/). Currently, +NumPy, CuPy, PyTorch, Dask, and JAX are supported. If you want support for other array +libraries, or if you encounter any issues, please [open an +issue](https://github.com/data-apis/array-api-compat/issues). + +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 [2022.12 +version](https://data-apis.org/array-api/2022.12/) of the standard. + +## Install + +`array-api-compat` is available on both [PyPI](https://pypi.org/project/array-api-compat/) + +``` +python -m pip install array-api-compat +``` + +and [Conda-forge](https://anaconda.org/conda-forge/array-api-compat) + +``` +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 + +```py +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 + +```py +import array_api_compat.numpy as np +``` + +```py +import array_api_compat.cupy as cp +``` + +```py +import array_api_compat.torch as torch +``` + +```py +import array_api_compat.dask as da +``` + +> [!NOTE] +> There is no `array_api_compat.jax` submodule. JAX support is contained +> in JAX itself in the `jax.experimental.array_api` module. array-api-compat simply +> wraps that submodule. The main JAX support in this module consists of +> supporting it in the [helper functions](helper-functions) defined below. + +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](https://numpy.org/neps/nep-0047-array-api-standard.html)). 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](https://data-apis.org/array-api/latest/API_specification/type_promotion.html)). +(`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. + +(helper-functions)= +## Helper Functions + +In addition to the wrapped library namespaces and functions in the array API +specification, there are several helper functions included here that aren't +part of the specification but which are useful for using the array API: + +- `is_array_api_obj(x)`: Return `True` if `x` is an array API compatible array + object. + +- `is_numpy_array(x)`, `is_cupy_array(x)`, `is_torch_array(x)`, + `is_dask_array(x)`, `is_jax_array(x)`: return `True` if `x` is an array from + the corresponding library. These functions do not import the underlying + library if it has not already been imported, so they are cheap to use. + +- `array_namespace(*xs)`: Get the corresponding array API namespace for the + arrays `xs`. For example, if the arrays are NumPy arrays, the returned + namespace will be `array_api_compat.numpy`. Note that this function will + also work for namespaces that aren't supported by this compat library but + which do support the array API (i.e., arrays that have the + `__array_namespace__` attribute). + +- `device(x)`: Equivalent to + [`x.device`](https://data-apis.org/array-api/latest/API_specification/generated/signatures.array_object.array.device.html) + in the array API specification. Included because `numpy.ndarray` does not + include the `device` attribute and this library does not wrap or extend the + array object. Note that for NumPy and dask, `device(x)` is always `"cpu"`. + +- `to_device(x, device, /, *, stream=None)`: Equivalent to + [`x.to_device`](https://data-apis.org/array-api/latest/API_specification/generated/signatures.array_object.array.to_device.html). + Included because neither NumPy's, CuPy's, Dask's, nor PyTorch's array objects + include this method. For NumPy, this function effectively does nothing since + the only supported device is the CPU, but for CuPy, this method supports + CuPy CUDA + [Device](https://docs.cupy.dev/en/stable/reference/generated/cupy.cuda.Device.html) + and + [Stream](https://docs.cupy.dev/en/stable/reference/generated/cupy.cuda.Stream.html) + objects. For PyTorch, this is the same as + [`x.to(device)`](https://pytorch.org/docs/stable/generated/torch.Tensor.to.html) + (the `stream` argument is not supported in PyTorch). + +- `size(x)`: Equivalent to + [`x.size`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.array.size.html#array_api.array.size), + i.e., the number of elements in the array. Included because PyTorch's + `Tensor` defines `size` as a method which returns the shape, and this cannot + be wrapped because this compat library doesn't wrap or extend the array + objects. + +## Known Differences from the Array API Specification + +There are some known differences between this library and the array API +specification: + +### NumPy and CuPy + +- The array methods `__array_namespace__`, `device` (for NumPy), `to_device`, + and `mT` are not defined. This reuses `np.ndarray` and `cp.ndarray` and we + don't want to monkeypatch or wrap it. The helper functions `device()` and + `to_device()` are provided to work around these missing methods (see above). + `x.mT` can be replaced with `xp.linalg.matrix_transpose(x)`. + `array_namespace(x)` should be used instead of `x.__array_namespace__`. + +- Value-based casting for scalars will be in effect unless explicitly disabled + with the environment variable `NPY_PROMOTION_STATE=weak` or + `np._set_promotion_state('weak')` (requires NumPy 1.24 or newer, see [NEP + 50](https://numpy.org/neps/nep-0050-scalar-promotion.html) and + https://github.com/numpy/numpy/issues/22341) + +- `asarray()` does not support `copy=False`. + +- Functions which are not wrapped may not have the same type annotations + as the spec. + +- Functions which are not wrapped may not use positional-only arguments. + +The minimum supported NumPy version is 1.21. However, this older version of +NumPy has a few issues: + +- `unique_*` will not compare nans as unequal. +- `finfo()` has no `smallest_normal`. +- No `from_dlpack` or `__dlpack__`. +- `argmax()` and `argmin()` do not have `keepdims`. +- `qr()` doesn't support matrix stacks. +- `asarray()` doesn't support `copy=True` (as noted above, `copy=False` is not + supported even in the latest NumPy). +- Type promotion behavior will be value based for 0-D arrays (and there is no + `NPY_PROMOTION_STATE=weak` to disable this). + +If any of these are an issue, it is recommended to bump your minimum NumPy +version. + +### PyTorch + +- Like NumPy/CuPy, we do not wrap the `torch.Tensor` object. It is missing the + `__array_namespace__` and `to_device` methods, so the corresponding helper + functions `array_namespace()` and `to_device()` in this library should be + used instead (see above). + +- The `x.size` attribute on `torch.Tensor` is a function that behaves + differently from + [`x.size`](https://data-apis.org/array-api/draft/API_specification/generated/array_api.array.size.html) + in the spec. Use the `size(x)` helper function as a portable workaround (see + above). + +- PyTorch does not have unsigned integer types other than `uint8`, and no + attempt is made to implement them here. + +- PyTorch has type promotion semantics that differ from the array API + specification for 0-D tensor objects. The array functions in this wrapper + library do work around this, but the operators on the Tensor object do not, + as no operators or methods on the Tensor object are modified. If this is a + concern, use the functional form instead of the operator form, e.g., `add(x, + y)` instead of `x + y`. + +- [`unique_all()`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.unique_all.html#array_api.unique_all) + is not implemented, due to the fact that `torch.unique` does not support + returning the `indices` array. The other + [`unique_*`](https://data-apis.org/array-api/latest/API_specification/set_functions.html) + functions are implemented. + +- Slices do not support negative steps. + +- [`std()`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.std.html#array_api.std) + and + [`var()`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.var.html#array_api.var) + do not support floating-point `correction`. + +- The `stream` argument of the `to_device()` helper (see above) is not + supported. + +- As with NumPy, type annotations and positional-only arguments may not + exactly match the spec for functions that are not wrapped at all. + +The minimum supported PyTorch version is 1.13. + +### JAX + +Unlike the other libraries supported here, JAX array API support is contained +entirely in the JAX library. The JAX array API support is tracked at +https://github.com/google/jax/issues/18353. + +## Dask + +If you're using dask with numpy, many of the same limitations that apply to numpy +will also apply to dask. Besides those differences, other limitations include missing +sort functionality (no `sort` or `argsort`), and limited support for the optional `linalg` +and `fft` extensions. + +In particular, the `fft` namespace is not compliant with the array API spec. Any functions +that you find under the `fft` namespace are the original, unwrapped functions under [`dask.array.fft`](https://docs.dask.org/en/latest/array-api.html#fast-fourier-transforms), which may or may not be Array API compliant. Use at your own risk! + +For `linalg`, several methods are missing, for example: +- `cross` +- `det` +- `eigh` +- `eigvalsh` +- `matrix_power` +- `pinv` +- `slogdet` +- `matrix_norm` +- `matrix_rank` +Other methods may only be partially implemented or return incorrect results at times. + +The minimum supported Dask version is 2023.12.0. + +## 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 on PyPI. + +## Implementation Notes + +As noted before, the goal of this library is to reuse the NumPy and CuPy array +objects, rather than wrapping or extending them. This means that the functions +need to accept and return `np.ndarray` for NumPy and `cp.ndarray` for CuPy. + +Each namespace (`array_api_compat.numpy`, `array_api_compat.cupy`, and +`array_api_compat.torch`) is populated with the normal library namespace (like +`from numpy import *`). Then specific functions are replaced with wrapped +variants. + +Since NumPy and CuPy are nearly identical in behavior, most wrapping logic can +be shared between them. Wrapped functions that have the same logic between +NumPy and CuPy are in `array_api_compat/common/`. +These functions are defined like + +```py +# In array_api_compat/common/_aliases.py + +def acos(x, /, xp): + return xp.arccos(x) +``` + +The `xp` argument refers to the original array namespace (either `numpy` or +`cupy`). Then in the specific `array_api_compat/numpy/` and +`array_api_compat/cupy/` namespaces, the `@get_xp` decorator is applied to +these functions, which automatically removes the `xp` argument from the +function signature and replaces it with the corresponding array library, like + +```py +# In array_api_compat/numpy/_aliases.py + +from ..common import _aliases + +import numpy as np + +acos = get_xp(np)(_aliases.acos) +``` + +This `acos` now has the signature `acos(x, /)` and calls `numpy.arccos`. + +Similarly, for CuPy: + +```py +# In array_api_compat/cupy/_aliases.py + +from ..common import _aliases + +import cupy as cp + +acos = get_xp(cp)(_aliases.acos) +``` + +Since NumPy and CuPy are nearly identical in their behaviors, this allows +writing the wrapping logic for both libraries only once. + +PyTorch uses a similar layout in `array_api_compat/torch/`, but it differs +enough from NumPy/CuPy that very few common wrappers for those libraries are +reused. + +See https://numpy.org/doc/stable/reference/array_api.html for a full list of +changes from the base NumPy (the differences for CuPy are nearly identical). A +corresponding document does not yet exist for PyTorch, but you can examine the +various comments in the +[implementation](https://github.com/data-apis/array-api-compat/blob/main/array_api_compat/torch/_aliases.py) +to see what functions and behaviors have been wrapped. + + +## Releasing + +To release, first note that CuPy must be tested manually (it isn't tested on +CI). Use the script + +``` +./test_cupy.sh +``` + +on a machine with a CUDA GPU. + +Once you are ready to release, create a PR with a release branch, so that you +can verify that CI is passing. You must edit + +``` +array_api_compat/__init__.py +``` + +and update the version (the version is not computed from the tag because that +would break vendorability). You should also edit + +``` +CHANGELOG.md +``` + +with the changes for the release. + +Then create a tag + +``` +git tag -a +``` + +and push it to GitHub + +``` +git push origin +``` + +Check that the `publish distributions` action works. Note that this action +will run even if the other CI fails, so you must make sure that CI is passing +*before* tagging. + +This does mean you can ignore CI failures, but ideally you should fix any +failures or update the `*-xfails.txt` files before tagging, so that CI and the +cupy tests pass. Otherwise it will be hard to tell what things are breaking in +the future. It's also a good idea to remove any xpasses from those files (but +be aware that some xfails are from flaky failures, so unless you know the +underlying issue has been fixed, a xpass test is probably still xfail). diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index 4b567a03..00000000 --- a/docs/index.rst +++ /dev/null @@ -1,20 +0,0 @@ -.. Array API Compat documentation master file, created by - sphinx-quickstart on Fri Mar 8 16:31:27 2024. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to Array API Compat's documentation! -============================================ - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` From a648ffd4103f698c289db9f3217b7a3c65db3876 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 8 Mar 2024 16:50:33 -0700 Subject: [PATCH 03/29] Split the docs into multiple pages --- docs/dev/implementation-notes.md | 66 +++++++ docs/dev/index.md | 9 + docs/dev/releasing.md | 49 ++++++ docs/differences.md | 116 +++++++++++++ docs/helper-functions.md | 46 +++++ docs/index.md | 285 +------------------------------ 6 files changed, 292 insertions(+), 279 deletions(-) create mode 100644 docs/dev/implementation-notes.md create mode 100644 docs/dev/index.md create mode 100644 docs/dev/releasing.md create mode 100644 docs/differences.md create mode 100644 docs/helper-functions.md diff --git a/docs/dev/implementation-notes.md b/docs/dev/implementation-notes.md new file mode 100644 index 00000000..066f5214 --- /dev/null +++ b/docs/dev/implementation-notes.md @@ -0,0 +1,66 @@ +# Implementation Notes + +As noted before, the goal of this library is to reuse the NumPy and CuPy array +objects, rather than wrapping or extending them. This means that the functions +need to accept and return `np.ndarray` for NumPy and `cp.ndarray` for CuPy. + +Each namespace (`array_api_compat.numpy`, `array_api_compat.cupy`, and +`array_api_compat.torch`) is populated with the normal library namespace (like +`from numpy import *`). Then specific functions are replaced with wrapped +variants. + +Since NumPy and CuPy are nearly identical in behavior, most wrapping logic can +be shared between them. Wrapped functions that have the same logic between +NumPy and CuPy are in `array_api_compat/common/`. +These functions are defined like + +```py +# In array_api_compat/common/_aliases.py + +def acos(x, /, xp): + return xp.arccos(x) +``` + +The `xp` argument refers to the original array namespace (either `numpy` or +`cupy`). Then in the specific `array_api_compat/numpy/` and +`array_api_compat/cupy/` namespaces, the `@get_xp` decorator is applied to +these functions, which automatically removes the `xp` argument from the +function signature and replaces it with the corresponding array library, like + +```py +# In array_api_compat/numpy/_aliases.py + +from ..common import _aliases + +import numpy as np + +acos = get_xp(np)(_aliases.acos) +``` + +This `acos` now has the signature `acos(x, /)` and calls `numpy.arccos`. + +Similarly, for CuPy: + +```py +# In array_api_compat/cupy/_aliases.py + +from ..common import _aliases + +import cupy as cp + +acos = get_xp(cp)(_aliases.acos) +``` + +Since NumPy and CuPy are nearly identical in their behaviors, this allows +writing the wrapping logic for both libraries only once. + +PyTorch uses a similar layout in `array_api_compat/torch/`, but it differs +enough from NumPy/CuPy that very few common wrappers for those libraries are +reused. + +See https://numpy.org/doc/stable/reference/array_api.html for a full list of +changes from the base NumPy (the differences for CuPy are nearly identical). A +corresponding document does not yet exist for PyTorch, but you can examine the +various comments in the +[implementation](https://github.com/data-apis/array-api-compat/blob/main/array_api_compat/torch/_aliases.py) +to see what functions and behaviors have been wrapped. diff --git a/docs/dev/index.md b/docs/dev/index.md new file mode 100644 index 00000000..e18d0787 --- /dev/null +++ b/docs/dev/index.md @@ -0,0 +1,9 @@ +# Development Notes + +```{toctree} +:titlesonly: +:hidden: + +implementation-notes.md +releasing.md +``` diff --git a/docs/dev/releasing.md b/docs/dev/releasing.md new file mode 100644 index 00000000..3b2e1dee --- /dev/null +++ b/docs/dev/releasing.md @@ -0,0 +1,49 @@ +# Releasing + +To release, first note that CuPy must be tested manually (it isn't tested on +CI). Use the script + +``` +./test_cupy.sh +``` + +on a machine with a CUDA GPU. + +Once you are ready to release, create a PR with a release branch, so that you +can verify that CI is passing. You must edit + +``` +array_api_compat/__init__.py +``` + +and update the version (the version is not computed from the tag because that +would break vendorability). You should also edit + +``` +CHANGELOG.md +``` + +with the changes for the release. + +Then create a tag + +``` +git tag -a +``` + +and push it to GitHub + +``` +git push origin +``` + +Check that the `publish distributions` action works. Note that this action +will run even if the other CI fails, so you must make sure that CI is passing +*before* tagging. + +This does mean you can ignore CI failures, but ideally you should fix any +failures or update the `*-xfails.txt` files before tagging, so that CI and the +cupy tests pass. Otherwise it will be hard to tell what things are breaking in +the future. It's also a good idea to remove any xpasses from those files (but +be aware that some xfails are from flaky failures, so unless you know the +underlying issue has been fixed, a xpass test is probably still xfail). diff --git a/docs/differences.md b/docs/differences.md new file mode 100644 index 00000000..3e47fce7 --- /dev/null +++ b/docs/differences.md @@ -0,0 +1,116 @@ +# Differences from the Array API Specification + +There are some known differences between this library and the array API +specification: + +## NumPy and CuPy + +- The array methods `__array_namespace__`, `device` (for NumPy), `to_device`, + and `mT` are not defined. This reuses `np.ndarray` and `cp.ndarray` and we + don't want to monkeypatch or wrap it. The helper functions `device()` and + `to_device()` are provided to work around these missing methods (see above). + `x.mT` can be replaced with `xp.linalg.matrix_transpose(x)`. + `array_namespace(x)` should be used instead of `x.__array_namespace__`. + +- Value-based casting for scalars will be in effect unless explicitly disabled + with the environment variable `NPY_PROMOTION_STATE=weak` or + `np._set_promotion_state('weak')` (requires NumPy 1.24 or newer, see [NEP + 50](https://numpy.org/neps/nep-0050-scalar-promotion.html) and + https://github.com/numpy/numpy/issues/22341) + +- `asarray()` does not support `copy=False`. + +- Functions which are not wrapped may not have the same type annotations + as the spec. + +- Functions which are not wrapped may not use positional-only arguments. + +The minimum supported NumPy version is 1.21. However, this older version of +NumPy has a few issues: + +- `unique_*` will not compare nans as unequal. +- `finfo()` has no `smallest_normal`. +- No `from_dlpack` or `__dlpack__`. +- `argmax()` and `argmin()` do not have `keepdims`. +- `qr()` doesn't support matrix stacks. +- `asarray()` doesn't support `copy=True` (as noted above, `copy=False` is not + supported even in the latest NumPy). +- Type promotion behavior will be value based for 0-D arrays (and there is no + `NPY_PROMOTION_STATE=weak` to disable this). + +If any of these are an issue, it is recommended to bump your minimum NumPy +version. + +## PyTorch + +- Like NumPy/CuPy, we do not wrap the `torch.Tensor` object. It is missing the + `__array_namespace__` and `to_device` methods, so the corresponding helper + functions `array_namespace()` and `to_device()` in this library should be + used instead (see above). + +- The `x.size` attribute on `torch.Tensor` is a function that behaves + differently from + [`x.size`](https://data-apis.org/array-api/draft/API_specification/generated/array_api.array.size.html) + in the spec. Use the `size(x)` helper function as a portable workaround (see + above). + +- PyTorch does not have unsigned integer types other than `uint8`, and no + attempt is made to implement them here. + +- PyTorch has type promotion semantics that differ from the array API + specification for 0-D tensor objects. The array functions in this wrapper + library do work around this, but the operators on the Tensor object do not, + as no operators or methods on the Tensor object are modified. If this is a + concern, use the functional form instead of the operator form, e.g., `add(x, + y)` instead of `x + y`. + +- [`unique_all()`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.unique_all.html#array_api.unique_all) + is not implemented, due to the fact that `torch.unique` does not support + returning the `indices` array. The other + [`unique_*`](https://data-apis.org/array-api/latest/API_specification/set_functions.html) + functions are implemented. + +- Slices do not support negative steps. + +- [`std()`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.std.html#array_api.std) + and + [`var()`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.var.html#array_api.var) + do not support floating-point `correction`. + +- The `stream` argument of the `to_device()` helper (see above) is not + supported. + +- As with NumPy, type annotations and positional-only arguments may not + exactly match the spec for functions that are not wrapped at all. + +The minimum supported PyTorch version is 1.13. + +## JAX + +Unlike the other libraries supported here, JAX array API support is contained +entirely in the JAX library. The JAX array API support is tracked at +https://github.com/google/jax/issues/18353. + +## Dask + +If you're using dask with numpy, many of the same limitations that apply to numpy +will also apply to dask. Besides those differences, other limitations include missing +sort functionality (no `sort` or `argsort`), and limited support for the optional `linalg` +and `fft` extensions. + +In particular, the `fft` namespace is not compliant with the array API spec. Any functions +that you find under the `fft` namespace are the original, unwrapped functions under [`dask.array.fft`](https://docs.dask.org/en/latest/array-api.html#fast-fourier-transforms), which may or may not be Array API compliant. Use at your own risk! + +For `linalg`, several methods are missing, for example: +- `cross` +- `det` +- `eigh` +- `eigvalsh` +- `matrix_power` +- `pinv` +- `slogdet` +- `matrix_norm` +- `matrix_rank` +Other methods may only be partially implemented or return incorrect results at times. + +The minimum supported Dask version is 2023.12.0. diff --git a/docs/helper-functions.md b/docs/helper-functions.md new file mode 100644 index 00000000..8498f268 --- /dev/null +++ b/docs/helper-functions.md @@ -0,0 +1,46 @@ +# Helper Functions + +In addition to the wrapped library namespaces and functions in the array API +specification, there are several helper functions included here that aren't +part of the specification but which are useful for using the array API: + +- `is_array_api_obj(x)`: Return `True` if `x` is an array API compatible array + object. + +- `is_numpy_array(x)`, `is_cupy_array(x)`, `is_torch_array(x)`, + `is_dask_array(x)`, `is_jax_array(x)`: return `True` if `x` is an array from + the corresponding library. These functions do not import the underlying + library if it has not already been imported, so they are cheap to use. + +- `array_namespace(*xs)`: Get the corresponding array API namespace for the + arrays `xs`. For example, if the arrays are NumPy arrays, the returned + namespace will be `array_api_compat.numpy`. Note that this function will + also work for namespaces that aren't supported by this compat library but + which do support the array API (i.e., arrays that have the + `__array_namespace__` attribute). + +- `device(x)`: Equivalent to + [`x.device`](https://data-apis.org/array-api/latest/API_specification/generated/signatures.array_object.array.device.html) + in the array API specification. Included because `numpy.ndarray` does not + include the `device` attribute and this library does not wrap or extend the + array object. Note that for NumPy and dask, `device(x)` is always `"cpu"`. + +- `to_device(x, device, /, *, stream=None)`: Equivalent to + [`x.to_device`](https://data-apis.org/array-api/latest/API_specification/generated/signatures.array_object.array.to_device.html). + Included because neither NumPy's, CuPy's, Dask's, nor PyTorch's array objects + include this method. For NumPy, this function effectively does nothing since + the only supported device is the CPU, but for CuPy, this method supports + CuPy CUDA + [Device](https://docs.cupy.dev/en/stable/reference/generated/cupy.cuda.Device.html) + and + [Stream](https://docs.cupy.dev/en/stable/reference/generated/cupy.cuda.Stream.html) + objects. For PyTorch, this is the same as + [`x.to(device)`](https://pytorch.org/docs/stable/generated/torch.Tensor.to.html) + (the `stream` argument is not supported in PyTorch). + +- `size(x)`: Equivalent to + [`x.size`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.array.size.html#array_api.array.size), + i.e., the number of elements in the array. Included because PyTorch's + `Tensor` defines `size` as a method which returns the shape, and this cannot + be wrapped because this compat library doesn't wrap or extend the array + objects. diff --git a/docs/index.md b/docs/index.md index 88c37283..8c5be326 100644 --- a/docs/index.md +++ b/docs/index.md @@ -101,171 +101,6 @@ 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. -(helper-functions)= -## Helper Functions - -In addition to the wrapped library namespaces and functions in the array API -specification, there are several helper functions included here that aren't -part of the specification but which are useful for using the array API: - -- `is_array_api_obj(x)`: Return `True` if `x` is an array API compatible array - object. - -- `is_numpy_array(x)`, `is_cupy_array(x)`, `is_torch_array(x)`, - `is_dask_array(x)`, `is_jax_array(x)`: return `True` if `x` is an array from - the corresponding library. These functions do not import the underlying - library if it has not already been imported, so they are cheap to use. - -- `array_namespace(*xs)`: Get the corresponding array API namespace for the - arrays `xs`. For example, if the arrays are NumPy arrays, the returned - namespace will be `array_api_compat.numpy`. Note that this function will - also work for namespaces that aren't supported by this compat library but - which do support the array API (i.e., arrays that have the - `__array_namespace__` attribute). - -- `device(x)`: Equivalent to - [`x.device`](https://data-apis.org/array-api/latest/API_specification/generated/signatures.array_object.array.device.html) - in the array API specification. Included because `numpy.ndarray` does not - include the `device` attribute and this library does not wrap or extend the - array object. Note that for NumPy and dask, `device(x)` is always `"cpu"`. - -- `to_device(x, device, /, *, stream=None)`: Equivalent to - [`x.to_device`](https://data-apis.org/array-api/latest/API_specification/generated/signatures.array_object.array.to_device.html). - Included because neither NumPy's, CuPy's, Dask's, nor PyTorch's array objects - include this method. For NumPy, this function effectively does nothing since - the only supported device is the CPU, but for CuPy, this method supports - CuPy CUDA - [Device](https://docs.cupy.dev/en/stable/reference/generated/cupy.cuda.Device.html) - and - [Stream](https://docs.cupy.dev/en/stable/reference/generated/cupy.cuda.Stream.html) - objects. For PyTorch, this is the same as - [`x.to(device)`](https://pytorch.org/docs/stable/generated/torch.Tensor.to.html) - (the `stream` argument is not supported in PyTorch). - -- `size(x)`: Equivalent to - [`x.size`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.array.size.html#array_api.array.size), - i.e., the number of elements in the array. Included because PyTorch's - `Tensor` defines `size` as a method which returns the shape, and this cannot - be wrapped because this compat library doesn't wrap or extend the array - objects. - -## Known Differences from the Array API Specification - -There are some known differences between this library and the array API -specification: - -### NumPy and CuPy - -- The array methods `__array_namespace__`, `device` (for NumPy), `to_device`, - and `mT` are not defined. This reuses `np.ndarray` and `cp.ndarray` and we - don't want to monkeypatch or wrap it. The helper functions `device()` and - `to_device()` are provided to work around these missing methods (see above). - `x.mT` can be replaced with `xp.linalg.matrix_transpose(x)`. - `array_namespace(x)` should be used instead of `x.__array_namespace__`. - -- Value-based casting for scalars will be in effect unless explicitly disabled - with the environment variable `NPY_PROMOTION_STATE=weak` or - `np._set_promotion_state('weak')` (requires NumPy 1.24 or newer, see [NEP - 50](https://numpy.org/neps/nep-0050-scalar-promotion.html) and - https://github.com/numpy/numpy/issues/22341) - -- `asarray()` does not support `copy=False`. - -- Functions which are not wrapped may not have the same type annotations - as the spec. - -- Functions which are not wrapped may not use positional-only arguments. - -The minimum supported NumPy version is 1.21. However, this older version of -NumPy has a few issues: - -- `unique_*` will not compare nans as unequal. -- `finfo()` has no `smallest_normal`. -- No `from_dlpack` or `__dlpack__`. -- `argmax()` and `argmin()` do not have `keepdims`. -- `qr()` doesn't support matrix stacks. -- `asarray()` doesn't support `copy=True` (as noted above, `copy=False` is not - supported even in the latest NumPy). -- Type promotion behavior will be value based for 0-D arrays (and there is no - `NPY_PROMOTION_STATE=weak` to disable this). - -If any of these are an issue, it is recommended to bump your minimum NumPy -version. - -### PyTorch - -- Like NumPy/CuPy, we do not wrap the `torch.Tensor` object. It is missing the - `__array_namespace__` and `to_device` methods, so the corresponding helper - functions `array_namespace()` and `to_device()` in this library should be - used instead (see above). - -- The `x.size` attribute on `torch.Tensor` is a function that behaves - differently from - [`x.size`](https://data-apis.org/array-api/draft/API_specification/generated/array_api.array.size.html) - in the spec. Use the `size(x)` helper function as a portable workaround (see - above). - -- PyTorch does not have unsigned integer types other than `uint8`, and no - attempt is made to implement them here. - -- PyTorch has type promotion semantics that differ from the array API - specification for 0-D tensor objects. The array functions in this wrapper - library do work around this, but the operators on the Tensor object do not, - as no operators or methods on the Tensor object are modified. If this is a - concern, use the functional form instead of the operator form, e.g., `add(x, - y)` instead of `x + y`. - -- [`unique_all()`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.unique_all.html#array_api.unique_all) - is not implemented, due to the fact that `torch.unique` does not support - returning the `indices` array. The other - [`unique_*`](https://data-apis.org/array-api/latest/API_specification/set_functions.html) - functions are implemented. - -- Slices do not support negative steps. - -- [`std()`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.std.html#array_api.std) - and - [`var()`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.var.html#array_api.var) - do not support floating-point `correction`. - -- The `stream` argument of the `to_device()` helper (see above) is not - supported. - -- As with NumPy, type annotations and positional-only arguments may not - exactly match the spec for functions that are not wrapped at all. - -The minimum supported PyTorch version is 1.13. - -### JAX - -Unlike the other libraries supported here, JAX array API support is contained -entirely in the JAX library. The JAX array API support is tracked at -https://github.com/google/jax/issues/18353. - -## Dask - -If you're using dask with numpy, many of the same limitations that apply to numpy -will also apply to dask. Besides those differences, other limitations include missing -sort functionality (no `sort` or `argsort`), and limited support for the optional `linalg` -and `fft` extensions. - -In particular, the `fft` namespace is not compliant with the array API spec. Any functions -that you find under the `fft` namespace are the original, unwrapped functions under [`dask.array.fft`](https://docs.dask.org/en/latest/array-api.html#fast-fourier-transforms), which may or may not be Array API compliant. Use at your own risk! - -For `linalg`, several methods are missing, for example: -- `cross` -- `det` -- `eigh` -- `eigvalsh` -- `matrix_power` -- `pinv` -- `slogdet` -- `matrix_norm` -- `matrix_rank` -Other methods may only be partially implemented or return incorrect results at times. - -The minimum supported Dask version is 2023.12.0. - ## Vendoring This library supports vendoring as an installation method. To vendor the @@ -281,120 +116,12 @@ references the name "array_api_compat"). Alternatively, the library may be installed as dependency on PyPI. -## Implementation Notes - -As noted before, the goal of this library is to reuse the NumPy and CuPy array -objects, rather than wrapping or extending them. This means that the functions -need to accept and return `np.ndarray` for NumPy and `cp.ndarray` for CuPy. -Each namespace (`array_api_compat.numpy`, `array_api_compat.cupy`, and -`array_api_compat.torch`) is populated with the normal library namespace (like -`from numpy import *`). Then specific functions are replaced with wrapped -variants. +```{toctree} +:titlesonly: +:hidden: -Since NumPy and CuPy are nearly identical in behavior, most wrapping logic can -be shared between them. Wrapped functions that have the same logic between -NumPy and CuPy are in `array_api_compat/common/`. -These functions are defined like - -```py -# In array_api_compat/common/_aliases.py - -def acos(x, /, xp): - return xp.arccos(x) +helper-functions.md +differences.md +dev/index.md ``` - -The `xp` argument refers to the original array namespace (either `numpy` or -`cupy`). Then in the specific `array_api_compat/numpy/` and -`array_api_compat/cupy/` namespaces, the `@get_xp` decorator is applied to -these functions, which automatically removes the `xp` argument from the -function signature and replaces it with the corresponding array library, like - -```py -# In array_api_compat/numpy/_aliases.py - -from ..common import _aliases - -import numpy as np - -acos = get_xp(np)(_aliases.acos) -``` - -This `acos` now has the signature `acos(x, /)` and calls `numpy.arccos`. - -Similarly, for CuPy: - -```py -# In array_api_compat/cupy/_aliases.py - -from ..common import _aliases - -import cupy as cp - -acos = get_xp(cp)(_aliases.acos) -``` - -Since NumPy and CuPy are nearly identical in their behaviors, this allows -writing the wrapping logic for both libraries only once. - -PyTorch uses a similar layout in `array_api_compat/torch/`, but it differs -enough from NumPy/CuPy that very few common wrappers for those libraries are -reused. - -See https://numpy.org/doc/stable/reference/array_api.html for a full list of -changes from the base NumPy (the differences for CuPy are nearly identical). A -corresponding document does not yet exist for PyTorch, but you can examine the -various comments in the -[implementation](https://github.com/data-apis/array-api-compat/blob/main/array_api_compat/torch/_aliases.py) -to see what functions and behaviors have been wrapped. - - -## Releasing - -To release, first note that CuPy must be tested manually (it isn't tested on -CI). Use the script - -``` -./test_cupy.sh -``` - -on a machine with a CUDA GPU. - -Once you are ready to release, create a PR with a release branch, so that you -can verify that CI is passing. You must edit - -``` -array_api_compat/__init__.py -``` - -and update the version (the version is not computed from the tag because that -would break vendorability). You should also edit - -``` -CHANGELOG.md -``` - -with the changes for the release. - -Then create a tag - -``` -git tag -a -``` - -and push it to GitHub - -``` -git push origin -``` - -Check that the `publish distributions` action works. Note that this action -will run even if the other CI fails, so you must make sure that CI is passing -*before* tagging. - -This does mean you can ignore CI failures, but ideally you should fix any -failures or update the `*-xfails.txt` files before tagging, so that CI and the -cupy tests pass. Otherwise it will be hard to tell what things are breaking in -the future. It's also a good idea to remove any xpasses from those files (but -be aware that some xfails are from flaky failures, so unless you know the -underlying issue has been fixed, a xpass test is probably still xfail). From 749c38707284e0b4d9207cf17a41408a7e407ff5 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 8 Mar 2024 16:53:42 -0700 Subject: [PATCH 04/29] Fix displaying the version in the docs --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index e32778c6..7c17ce06 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -15,7 +15,7 @@ author = 'Consortium for Python Data API Standards' import array_api_compat -version = array_api_compat.__version__ +release = array_api_compat.__version__ # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration From b9380d4ca9d064f8ab676a7dd35df2f182200e46 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 8 Mar 2024 16:53:54 -0700 Subject: [PATCH 05/29] Add a GitHub icon to the docs --- docs/conf.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 7c17ce06..89e3a24a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -40,3 +40,19 @@ html_static_path = ['_static'] html_css_files = ['custom.css'] + +html_theme_options = { + # See https://pradyunsg.me/furo/customisation/footer/ + "footer_icons": [ + { + "name": "GitHub", + "url": "https://github.com/data-apis/array-api-compat", + "html": """ + + + + """, + "class": "", + }, + ], +} From 3ae70f60c4bc69563ef327374d3bf1ecdac13525 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 8 Mar 2024 16:55:17 -0700 Subject: [PATCH 06/29] Add something to the dev docs index page --- docs/dev/index.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/dev/index.md b/docs/dev/index.md index e18d0787..59cd8551 100644 --- a/docs/dev/index.md +++ b/docs/dev/index.md @@ -1,8 +1,10 @@ # Development Notes +This is internal documentation related to the development of array-api-compat. +It is recommended that contributors read through this documentation. + ```{toctree} :titlesonly: -:hidden: implementation-notes.md releasing.md From d557948f278f40a24869496afb6880f3258f179b Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 8 Mar 2024 18:06:40 -0700 Subject: [PATCH 07/29] Write some dev docs, and various other improvements to the documentation --- docs/dev/implementation-notes.md | 94 +++++++++++++++++++ docs/index.md | 17 ++-- ...rences.md => supported-array-libraries.md} | 36 ++++--- 3 files changed, 129 insertions(+), 18 deletions(-) rename docs/{differences.md => supported-array-libraries.md} (77%) diff --git a/docs/dev/implementation-notes.md b/docs/dev/implementation-notes.md index 066f5214..fd45f9b4 100644 --- a/docs/dev/implementation-notes.md +++ b/docs/dev/implementation-notes.md @@ -1,5 +1,77 @@ # Implementation Notes +This page outlines some notes on the implementation of array-api-compat. These +details are not important for users of the package, but they may be useful to +contributors. + +## Special Considerations + +array-api-compat requires some special development considerations that are +different from most other Python libraries. The goal of array-api-compat is to +be a small library that packages can either vendor or add as a dependency to +implement array API support. Consequently, certain design considerations +should be taken into account: + +- *No Hard Dependencies.* Although array-api-compat "depends" on NumPy, CuPy, + PyTorch, etc., it does not hard depend on them. These libraries are not + imported unless either an array object is passed to `array_namespace()`, or + the specific `array_api_compat.` sub-namespace is explicitly + imported. + +- *Vendorability.* array-api-compat should be [vendorable](vendoring). This + means that, for instance, all imports in the library are relative imports. + No code in the package specifically references the name `array_api_compat` + (we also support renaming the package to something else). + Vendorability support is tested in `tests/test_vendoring.py`. + +- *Pure Python.* To make array-api-compat as easy as possible to add as a + dependency, the code is all pure Python. + +- *Minimal Wrapping Only.* The wrapping functionality is minimal. This means + that if something is difficult to wrap using pure Python, or if trying to + support some array API behavior would require a significant amount of code, + we prefer to leave the behavior as an upstream issue for the array library, + and [document it as a known difference](../supported-array-libraries.md). + + This also means that we do not at this point in time implement anything + other than wrappers for functions in the standard, and basic [helper + functions](../helper-functions.md) that would be useful for most users of + array-api-compat. The addition of functions that are not part of the array + API standard is currently out-of-scope for this package. + +- *No Monkey Patching.* `array-api-compat` should not attempt to modify anything + about the underlying library. It is a *wrapper* library only. + +- *No modifying the array object.* Due to the above restrictions, the array + (or tensor) object of the array library cannot be modified. Any behavior + that is built-in to the array object, such as the behavior of [array + methods](https://data-apis.org/array-api/latest/API_specification/array_object.html) + is therefore left unwrapped. Users can workaround issues by using + corresponding [elementwise + functions](https://data-apis.org/array-api/latest/API_specification/elementwise_functions.html) + instead of operators, and using the [helper + functions](../helper-functions.md) provided by array-api-compat instead of + methods like `to_device`. + +- *Avoid Restricting Non-Standard Behavior.* All array libraries have + functions and behaviors that are outside of the scope of what is specified + by the standard. These behaviors should be left intact whenever possible, + unless the standard explicitly disallows something. This means + + - All namespaces are *extended* with wrapper functions. You may notice the + extensive use of `import *` in various files in `array_api_compat`. While + this would normally be questionable, this is the [one actual legitimate + use-case for `import *`](https://peps.python.org/pep-0008/#imports), to + re-export names from an external namespace. + + - All wrapper functions pass `**kwargs` through to the wrapped function. + + The onus is on users of array-api-compat to ensure they are only using + portable array API behavior, e.g., by testing against [array-api-strict](array-api-strict). + + +## Avoiding Code Duplication + As noted before, the goal of this library is to reuse the NumPy and CuPy array objects, rather than wrapping or extending them. This means that the functions need to accept and return `np.ndarray` for NumPy and `cp.ndarray` for CuPy. @@ -64,3 +136,25 @@ corresponding document does not yet exist for PyTorch, but you can examine the various comments in the [implementation](https://github.com/data-apis/array-api-compat/blob/main/array_api_compat/torch/_aliases.py) to see what functions and behaviors have been wrapped. + +## Tests + +The majority of the behavior for array-api-compat is tested by the +[array-api-tests](https://github.com/data-apis/array-api-tests) test suite for +the array API standard. There are also specific tests in +[`tests/`](https://github.com/data-apis/array-api-compat/tree/main/tests). +These tests should be limited to things that are not tested by the test suite, +e.g., tests for [helper functions](../helper-functions.md) or for behavior that is +not strictly required by the standard. + +array-api-tests is run against all supported libraries are tested on CI +(except for JAX, +[wrapping](jax-support)). This is achieved by a [reusable GitHub Actions +Workflow](https://github.com/data-apis/array-api-compat/blob/main/.github/workflows/array-api-tests.yml). +Most libraries have tests that must be xfailed or skipped for various reasons. +These are defined in specific `-xfails.txt` files and are +automatically forwarded to array-api-tests. + +Array libraries that require a GPU to run (currently only CuPy) cannot be +tested on CI. There is a helper script `test_cupy.sh` that can be used to +manually test CuPy on a machine with a CUDA GPU. diff --git a/docs/index.md b/docs/index.md index 8c5be326..c175ff3c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -71,14 +71,16 @@ 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. +(array-api-strict)= ## 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](https://numpy.org/neps/nep-0047-array-api-standard.html)). 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 +[`array_api_strict`](https://github.com/data-apis/array-api-strict) is a +strict minimal implementation of the array API standard, formerly known as +`numpy.array_api` (see [NEP +47](https://numpy.org/neps/nep-0047-array-api-standard.html)). 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](https://data-apis.org/array-api/latest/API_specification/type_promotion.html)). (`cupy.array_api` is similar to `array_api_strict`) @@ -101,6 +103,7 @@ 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)= ## Vendoring This library supports vendoring as an installation method. To vendor the @@ -122,6 +125,6 @@ Alternatively, the library may be installed as dependency on PyPI. :hidden: helper-functions.md -differences.md +supported-array-libraries.md dev/index.md ``` diff --git a/docs/differences.md b/docs/supported-array-libraries.md similarity index 77% rename from docs/differences.md rename to docs/supported-array-libraries.md index 3e47fce7..90e21e0e 100644 --- a/docs/differences.md +++ b/docs/supported-array-libraries.md @@ -1,16 +1,29 @@ -# Differences from the Array API Specification +# Supported Array Libraries -There are some known differences between this library and the array API -specification: +The following array libraries are supported. This page outlines the known +differences between this library and the array API specification for the +supported packages. -## NumPy and CuPy +Note that the [`array_namespace()`](helper-functions.md) helper will also +support any array library that explicitly supports the array API by defining +[`__array_namespace__`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.array.__array_namespace__.html). + +## [NumPy](https://numpy.org/) and [CuPy](https://cupy.dev/) + +NumPy 2.0 has full array API compatibility. This package is not strictly +necessary for NumPy 2.0 support, but may still be useful for the support of +other libraries, as well as for the [helper functions](helper-functions.md). + +For NumPy 1.26, as well as corresponding versions of CuPy, the following +deviations from the standard should be noted: - The array methods `__array_namespace__`, `device` (for NumPy), `to_device`, and `mT` are not defined. This reuses `np.ndarray` and `cp.ndarray` and we - don't want to monkeypatch or wrap it. The helper functions `device()` and - `to_device()` are provided to work around these missing methods (see above). - `x.mT` can be replaced with `xp.linalg.matrix_transpose(x)`. - `array_namespace(x)` should be used instead of `x.__array_namespace__`. + don't want to monkey patch or wrap it. The [helper + functions](helper-functions.md) `device()` and `to_device()` are provided to + work around these missing methods. `x.mT` can be replaced with + `xp.linalg.matrix_transpose(x)`. `array_namespace(x)` should be used instead + of `x.__array_namespace__`. - Value-based casting for scalars will be in effect unless explicitly disabled with the environment variable `NPY_PROMOTION_STATE=weak` or @@ -41,7 +54,7 @@ NumPy has a few issues: If any of these are an issue, it is recommended to bump your minimum NumPy version. -## PyTorch +## [PyTorch](https://pytorch.org/) - Like NumPy/CuPy, we do not wrap the `torch.Tensor` object. It is missing the `__array_namespace__` and `to_device` methods, so the corresponding helper @@ -85,13 +98,14 @@ version. The minimum supported PyTorch version is 1.13. -## JAX +(jax-support)= +## [JAX](https://jax.readthedocs.io/en/latest/) Unlike the other libraries supported here, JAX array API support is contained entirely in the JAX library. The JAX array API support is tracked at https://github.com/google/jax/issues/18353. -## Dask +## [Dask](https://www.dask.org/) If you're using dask with numpy, many of the same limitations that apply to numpy will also apply to dask. Besides those differences, other limitations include missing From 667a8cb21e49d864b964d5d2c7320c62525ce58b Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 8 Mar 2024 19:12:56 -0700 Subject: [PATCH 08/29] Use autodoc for helper functions This still requires some work to fix cross-linking. --- array_api_compat/common/_helpers.py | 225 +++++++++++++++++++++++++--- docs/conf.py | 8 + docs/helper-functions.md | 46 ------ docs/helper-functions.rst | 50 +++++++ docs/index.md | 17 ++- 5 files changed, 274 insertions(+), 72 deletions(-) delete mode 100644 docs/helper-functions.md create mode 100644 docs/helper-functions.rst diff --git a/array_api_compat/common/_helpers.py b/array_api_compat/common/_helpers.py index ca9b746d..5851ab75 100644 --- a/array_api_compat/common/_helpers.py +++ b/array_api_compat/common/_helpers.py @@ -19,6 +19,24 @@ import warnings def is_numpy_array(x): + """ + Return True if `x` is a NumPy array. + + This function does not import NumPy if it has not already been imported + and is therefore cheap to use. + + This also returns True for `ndarray` subclasses and NumPy scalar objects. + + See Also + -------- + + array_namespace + is_array_api_obj + is_cupy_array + is_torch_array + is_dask_array + is_jax_array + """ # Avoid importing NumPy if it isn't already if 'numpy' not in sys.modules: return False @@ -29,6 +47,24 @@ def is_numpy_array(x): return isinstance(x, (np.ndarray, np.generic)) def is_cupy_array(x): + """ + Return True if `x` is a CuPy array. + + This function does not import CuPy if it has not already been imported + and is therefore cheap to use. + + This also returns True for `cupy.ndarray` subclasses and CuPy scalar objects. + + See Also + -------- + + array_namespace + is_array_api_obj + is_numpy_array + is_torch_array + is_dask_array + is_jax_array + """ # Avoid importing NumPy if it isn't already if 'cupy' not in sys.modules: return False @@ -39,6 +75,22 @@ def is_cupy_array(x): return isinstance(x, (cp.ndarray, cp.generic)) def is_torch_array(x): + """ + Return True if `x` is a PyTorch tensor. + + This function does not import PyTorch if it has not already been imported + and is therefore cheap to use. + + See Also + -------- + + array_namespace + is_array_api_obj + is_numpy_array + is_cupy_array + is_dask_array + is_jax_array + """ # Avoid importing torch if it isn't already if 'torch' not in sys.modules: return False @@ -49,6 +101,22 @@ def is_torch_array(x): return isinstance(x, torch.Tensor) def is_dask_array(x): + """ + Return True if `x` is a dask.array Array. + + This function does not import dask if it has not already been imported + and is therefore cheap to use. + + See Also + -------- + + array_namespace + is_array_api_obj + is_numpy_array + is_cupy_array + is_torch_array + is_jax_array + """ # Avoid importing dask if it isn't already if 'dask.array' not in sys.modules: return False @@ -58,6 +126,23 @@ def is_dask_array(x): return isinstance(x, dask.array.Array) def is_jax_array(x): + """ + Return True if `x` is a JAX array. + + This function does not import JAX if it has not already been imported + and is therefore cheap to use. + + + See Also + -------- + + array_namespace + is_array_api_obj + is_numpy_array + is_cupy_array + is_torch_array + is_dask_array + """ # Avoid importing jax if it isn't already if 'jax' not in sys.modules: return False @@ -68,7 +153,17 @@ def is_jax_array(x): def is_array_api_obj(x): """ - Check if x is an array API compatible array object. + Return True if `x` is an array API compatible array object. + + See Also + -------- + + array_namespace + is_numpy_array + is_cupy_array + is_torch_array + is_dask_array + is_jax_array """ return is_numpy_array(x) \ or is_cupy_array(x) \ @@ -87,17 +182,57 @@ def array_namespace(*xs, api_version=None, _use_compat=True): """ Get the array API compatible namespace for the arrays `xs`. - `xs` should contain one or more arrays. + Parameters + ---------- + xs: arrays + one or more arrays. + + api_version: str + The newest version of the spec that you need support for (currently + the compat library wrapped APIs support v2022.12). + + Returns + ------- + + out: namespace + The array API compatible namespace corresponding to the arrays in `xs`. + + Raises + ------ + TypeError + If `xs` contains arrays from different array libraries or contains a + non-array. + - Typical usage is + Typical usage is to pass the arguments of a function to + `array_namespace()` at the top of a function to get the corresponding + array API namespace: - 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) + .. code:: python + + 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) + + + Wrapped array namespaces can also be imported directly. For example, + `array_namespace(np.array(...))` will return `array_api_compat.numpy`. + This function will also work for any array library not wrapped by + array-api-compat if it explicitly defines `__array_namespace__ + `__ + (the wrapped namespace is always preferred if it exists). + + See Also + -------- + + is_array_api_obj + is_numpy_array + is_cupy_array + is_torch_array + is_dask_array + is_jax_array - api_version should be the newest version of the spec that you need support - for (currently the compat library wrapped APIs only support v2021.12). """ namespaces = set() for x in xs: @@ -181,15 +316,33 @@ def device(x: Array, /) -> Device: """ Hardware device the array data resides on. + This is equivalent to `x.device` according to the `standard + `__. + This helper is included because some array libraries either do not have + the `device` attribute or include it with an incompatible API. + Parameters ---------- x: array - array instance from NumPy or an array API compatible library. + array instance from an array API compatible library. Returns ------- out: device - a ``device`` object (see the "Device Support" section of the array API specification). + a ``device`` object (see the `Device Support `__ + section of the array API specification). + + Notes + ----- + + For NumPy the device is always `"cpu"`. For Dask, the device is always a + special `DASK_DEVICE` object. + + See Also + -------- + + to_device : Move array data to a different device. + """ if is_numpy_array(x): return "cpu" @@ -262,22 +415,52 @@ def to_device(x: Array, device: Device, /, *, stream: Optional[Union[int, Any]] """ Copy the array from the device on which it currently resides to the specified ``device``. + This is equivalent to `x.to_device(device, stream=stream)` according to + the `standard + `__. + This helper is included because some array libraries do not have the + `to_device` method. + Parameters ---------- + x: array - array instance from NumPy or an array API compatible library. + array instance from an array API compatible library. + device: device - a ``device`` object (see the "Device Support" section of the array API specification). + a ``device`` object (see the `Device Support `__ + section of the array API specification). + stream: Optional[Union[int, Any]] - stream object to use during copy. In addition to the types supported in ``array.__dlpack__``, implementations may choose to support any library-specific stream object with the caveat that any code using such an object would not be portable. + stream object to use during copy. In addition to the types supported + in ``array.__dlpack__``, implementations may choose to support any + library-specific stream object with the caveat that any code using + such an object would not be portable. Returns ------- + out: array - an array with the same data and data type as ``x`` and located on the specified ``device``. + an array with the same data and data type as ``x`` and located on the + specified ``device``. + + Notes + ----- + + For NumPy, this function effectively does nothing since the only supported + device is the CPU. For CuPy, this method supports CuPy CUDA `Device + `_ + and `Stream + `_ + objects. For PyTorch, this is the same as ``x.to(device) + `_ (the + ``stream`` argument is not supported in PyTorch). + + See Also + -------- + + device : Hardware device the array data resides on. - .. note:: - If ``stream`` is given, the copy operation should be enqueued on the provided ``stream``; otherwise, the copy operation should be enqueued on the default stream/queue. Whether the copy is performed synchronously or asynchronously is implementation-dependent. Accordingly, if synchronization is required to guarantee data safety, this must be clearly explained in a conforming library's documentation. """ if is_numpy_array(x): if stream is not None: @@ -305,7 +488,13 @@ def to_device(x: Array, device: Device, /, *, stream: Optional[Union[int, Any]] def size(x): """ - Return the total number of elements of x + Return the total number of elements of x. + + This is equivalent to `x.size` according to the `standard + `__. + This helper is included because PyTorch defines `size` in an `incompatible + way `__. + """ if None in x.shape: return None diff --git a/docs/conf.py b/docs/conf.py index 89e3a24a..8c902a8a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -22,6 +22,8 @@ extensions = [ 'myst_parser', + 'sphinx.ext.autodoc', + 'sphinx.ext.napoleon', 'sphinx_copybutton', ] @@ -30,9 +32,15 @@ myst_enable_extensions = ["dollarmath", "linkify"] +napoleon_use_rtype = False +napoleon_use_param = False + # Make sphinx give errors for bad cross-references nitpicky = True +# Lets us use single backticks for code in RST +default_role = 'code' + # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output diff --git a/docs/helper-functions.md b/docs/helper-functions.md deleted file mode 100644 index 8498f268..00000000 --- a/docs/helper-functions.md +++ /dev/null @@ -1,46 +0,0 @@ -# Helper Functions - -In addition to the wrapped library namespaces and functions in the array API -specification, there are several helper functions included here that aren't -part of the specification but which are useful for using the array API: - -- `is_array_api_obj(x)`: Return `True` if `x` is an array API compatible array - object. - -- `is_numpy_array(x)`, `is_cupy_array(x)`, `is_torch_array(x)`, - `is_dask_array(x)`, `is_jax_array(x)`: return `True` if `x` is an array from - the corresponding library. These functions do not import the underlying - library if it has not already been imported, so they are cheap to use. - -- `array_namespace(*xs)`: Get the corresponding array API namespace for the - arrays `xs`. For example, if the arrays are NumPy arrays, the returned - namespace will be `array_api_compat.numpy`. Note that this function will - also work for namespaces that aren't supported by this compat library but - which do support the array API (i.e., arrays that have the - `__array_namespace__` attribute). - -- `device(x)`: Equivalent to - [`x.device`](https://data-apis.org/array-api/latest/API_specification/generated/signatures.array_object.array.device.html) - in the array API specification. Included because `numpy.ndarray` does not - include the `device` attribute and this library does not wrap or extend the - array object. Note that for NumPy and dask, `device(x)` is always `"cpu"`. - -- `to_device(x, device, /, *, stream=None)`: Equivalent to - [`x.to_device`](https://data-apis.org/array-api/latest/API_specification/generated/signatures.array_object.array.to_device.html). - Included because neither NumPy's, CuPy's, Dask's, nor PyTorch's array objects - include this method. For NumPy, this function effectively does nothing since - the only supported device is the CPU, but for CuPy, this method supports - CuPy CUDA - [Device](https://docs.cupy.dev/en/stable/reference/generated/cupy.cuda.Device.html) - and - [Stream](https://docs.cupy.dev/en/stable/reference/generated/cupy.cuda.Stream.html) - objects. For PyTorch, this is the same as - [`x.to(device)`](https://pytorch.org/docs/stable/generated/torch.Tensor.to.html) - (the `stream` argument is not supported in PyTorch). - -- `size(x)`: Equivalent to - [`x.size`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.array.size.html#array_api.array.size), - i.e., the number of elements in the array. Included because PyTorch's - `Tensor` defines `size` as a method which returns the shape, and this cannot - be wrapped because this compat library doesn't wrap or extend the array - objects. diff --git a/docs/helper-functions.rst b/docs/helper-functions.rst new file mode 100644 index 00000000..b8bfc732 --- /dev/null +++ b/docs/helper-functions.rst @@ -0,0 +1,50 @@ +Helper Functions +================ + +.. currentmodule:: array_api_compat + +In addition to the wrapped library namespaces and functions in the array API +specification, there are several helper functions included here that aren't +part of the specification but which are useful for using the array API: + +array_namespace +--------------- + +The `array_namespace()` function is the primary entry-point for array API +consuming libraries. + +.. autofunction:: array_namespace + :canonical: array_api_compat.array_namespace + +Array Method Helpers +-------------------- + +array-api-compat does not attempt to wrap or monkey patch the array object for +any library. Consequently, any API differences for the [array +object](https://data-apis.org/array-api/latest/API_specification/array_object.htmlK +cannot be directly wrapped. Some libraries do not define some of these methods +or define them differently. For these, helper functions are provided which can +be used instead. + +Note that if you have a compatibility issue with an operator method (like +`__add__`, i.e., `+`) you can prefer to use the corresponding [elementwise +function](https://data-apis.org/array-api/latest/API_specification/elementwise_functions.html) +instead, which would be wrapped. + +.. autofunction:: device +.. autofunction:: to_device +.. autofunction:: size + +Inspect Helpers +--------------- + +These convenience functions can be used to test if an array comes from a +specific library without importing that library if it hasn't been imported +yet. + +.. autofunction:: is_array_api_obj +.. autofunction:: is_numpy_array +.. autofunction:: is_cupy_array +.. autofunction:: is_torch_array +.. autofunction:: is_dask_array +.. autofunction:: is_jax_array diff --git a/docs/index.md b/docs/index.md index c175ff3c..a62862fe 100644 --- a/docs/index.md +++ b/docs/index.md @@ -15,7 +15,7 @@ take some time. Currently all libraries here are implemented against the [2022.12 version](https://data-apis.org/array-api/2022.12/) of the standard. -## Install +## Installation `array-api-compat` is available on both [PyPI](https://pypi.org/project/array-api-compat/) @@ -32,7 +32,7 @@ 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 +compliant namespace from the input arrays using {func}`array_namespace()`, like ```py def your_function(x, y): @@ -60,11 +60,12 @@ import array_api_compat.torch as torch import array_api_compat.dask as da ``` -> [!NOTE] -> There is no `array_api_compat.jax` submodule. JAX support is contained -> in JAX itself in the `jax.experimental.array_api` module. array-api-compat simply -> wraps that submodule. The main JAX support in this module consists of -> supporting it in the [helper functions](helper-functions) defined below. +```{note} +There is no `array_api_compat.jax` submodule. JAX support is contained in JAX +itself in the `jax.experimental.array_api` module. array-api-compat simply +wraps that submodule. The main JAX support in this module consists of +supporting it in the [helper functions](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 @@ -124,7 +125,7 @@ Alternatively, the library may be installed as dependency on PyPI. :titlesonly: :hidden: -helper-functions.md +helper-functions.rst supported-array-libraries.md dev/index.md ``` From 9904b08eb7b1e84a504cfc133a5983f1893b8ba2 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 11 Mar 2024 15:37:21 -0600 Subject: [PATCH 09/29] Some improvements to the releasing docs --- docs/dev/releasing.md | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/docs/dev/releasing.md b/docs/dev/releasing.md index 3b2e1dee..5d2cbea3 100644 --- a/docs/dev/releasing.md +++ b/docs/dev/releasing.md @@ -1,7 +1,8 @@ # Releasing -To release, first note that CuPy must be tested manually (it isn't tested on -CI). Use the script +To release, first make sure that all CI tests are passing on `main`. + +Note that CuPy must be tested manually (it isn't tested on CI). Use the script ``` ./test_cupy.sh @@ -25,25 +26,34 @@ CHANGELOG.md with the changes for the release. -Then create a tag +Once everything is ready, create a tag ``` git tag -a ``` +(note the tag names are not prefixed, for instance, the tag for version 1.5 is +just `1.5`) + and push it to GitHub ``` git push origin ``` -Check that the `publish distributions` action works. Note that this action -will run even if the other CI fails, so you must make sure that CI is passing -*before* tagging. +Check that the `publish distributions` action on the tag build works. Note +that this action will run even if the other CI fails, so you must make sure +that CI is passing *before* tagging. This does mean you can ignore CI failures, but ideally you should fix any failures or update the `*-xfails.txt` files before tagging, so that CI and the cupy tests pass. Otherwise it will be hard to tell what things are breaking in the future. It's also a good idea to remove any xpasses from those files (but be aware that some xfails are from flaky failures, so unless you know the -underlying issue has been fixed, a xpass test is probably still xfail). +underlying issue has been fixed, an xpass test is probably still xfail). + +If the publish action fails for some reason and didn't upload the release to +PyPI, you will need to delete the tag and try again. + +After the PyPI package is published, the conda-forge bot should update the +feedstock automatically. From e328261da5881bef6e8878078620ede2e39afde7 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 11 Mar 2024 15:59:12 -0600 Subject: [PATCH 10/29] Update some implementation notes text --- docs/dev/implementation-notes.md | 79 ++++++++++++++++---------------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/docs/dev/implementation-notes.md b/docs/dev/implementation-notes.md index fd45f9b4..f2b36e75 100644 --- a/docs/dev/implementation-notes.md +++ b/docs/dev/implementation-notes.md @@ -39,12 +39,20 @@ should be taken into account: array-api-compat. The addition of functions that are not part of the array API standard is currently out-of-scope for this package. -- *No Monkey Patching.* `array-api-compat` should not attempt to modify anything - about the underlying library. It is a *wrapper* library only. +- *No side-effects*. array_api_compat behavior should be localized to only the + specific code that imports and uses it. It should be invisible to end-users + or users of dependent codes. This specifically applies to the next two + points. -- *No modifying the array object.* Due to the above restrictions, the array - (or tensor) object of the array library cannot be modified. Any behavior - that is built-in to the array object, such as the behavior of [array +- *No Monkey Patching.* `array-api-compat` should not attempt to modify + anything about the underlying library. It is a *wrapper* library only. + +- *No modifying the array object.* The array (or tensor) object of the array + library cannot be modified. Attempting to wrap or subclass a library's array + object would break the localized wrapping goal. + + Any behavior that is built-in to the array object, such as the behavior of + [array methods](https://data-apis.org/array-api/latest/API_specification/array_object.html) is therefore left unwrapped. Users can workaround issues by using corresponding [elementwise @@ -53,10 +61,10 @@ should be taken into account: functions](../helper-functions.md) provided by array-api-compat instead of methods like `to_device`. -- *Avoid Restricting Non-Standard Behavior.* All array libraries have - functions and behaviors that are outside of the scope of what is specified - by the standard. These behaviors should be left intact whenever possible, - unless the standard explicitly disallows something. This means +- *Avoid Restricting Behavior Outside the Scope of the Standard.* All array + libraries have functions and behaviors that are outside of the scope of what + is specified by the standard. These behaviors should be left intact whenever + possible, unless the standard explicitly disallows something. This means - All namespaces are *extended* with wrapper functions. You may notice the extensive use of `import *` in various files in `array_api_compat`. While @@ -72,18 +80,9 @@ should be taken into account: ## Avoiding Code Duplication -As noted before, the goal of this library is to reuse the NumPy and CuPy array -objects, rather than wrapping or extending them. This means that the functions -need to accept and return `np.ndarray` for NumPy and `cp.ndarray` for CuPy. - -Each namespace (`array_api_compat.numpy`, `array_api_compat.cupy`, and -`array_api_compat.torch`) is populated with the normal library namespace (like -`from numpy import *`). Then specific functions are replaced with wrapped -variants. - -Since NumPy and CuPy are nearly identical in behavior, most wrapping logic can -be shared between them. Wrapped functions that have the same logic between -NumPy and CuPy are in `array_api_compat/common/`. +Since NumPy, CuPy, and to a degree, Dask, are nearly identical in behavior, +most wrapping logic can be shared between them. Wrapped functions that have +the same logic between multiple libraries are in `array_api_compat/common/`. These functions are defined like ```py @@ -93,7 +92,7 @@ def acos(x, /, xp): return xp.arccos(x) ``` -The `xp` argument refers to the original array namespace (either `numpy` or +The `xp` argument refers to the original array namespace (e.g., `numpy` or `cupy`). Then in the specific `array_api_compat/numpy/` and `array_api_compat/cupy/` namespaces, the `@get_xp` decorator is applied to these functions, which automatically removes the `xp` argument from the @@ -123,38 +122,38 @@ import cupy as cp acos = get_xp(cp)(_aliases.acos) ``` -Since NumPy and CuPy are nearly identical in their behaviors, this allows -writing the wrapping logic for both libraries only once. - -PyTorch uses a similar layout in `array_api_compat/torch/`, but it differs -enough from NumPy/CuPy that very few common wrappers for those libraries are -reused. - -See https://numpy.org/doc/stable/reference/array_api.html for a full list of -changes from the base NumPy (the differences for CuPy are nearly identical). A -corresponding document does not yet exist for PyTorch, but you can examine the -various comments in the -[implementation](https://github.com/data-apis/array-api-compat/blob/main/array_api_compat/torch/_aliases.py) -to see what functions and behaviors have been wrapped. +Most NumPy and CuPy are defined in this way, since their behaviors are nearly +identical PyTorch uses a similar layout in `array_api_compat/torch/`, but it +differs enough from NumPy/CuPy that very few common wrappers for those +libraries are reused. Dask is close to NumPy in behavior and so most Dask +functions also reuse the NumPy/CuPy common wrappers. ## Tests The majority of the behavior for array-api-compat is tested by the [array-api-tests](https://github.com/data-apis/array-api-tests) test suite for -the array API standard. There are also specific tests in +the array API standard. There are also array-api-compat specific tests in [`tests/`](https://github.com/data-apis/array-api-compat/tree/main/tests). These tests should be limited to things that are not tested by the test suite, -e.g., tests for [helper functions](../helper-functions.md) or for behavior that is -not strictly required by the standard. +e.g., tests for [helper functions](../helper-functions.md) or for behavior +that is not strictly required by the standard. array-api-tests is run against all supported libraries are tested on CI -(except for JAX, -[wrapping](jax-support)). This is achieved by a [reusable GitHub Actions +([except for JAX](jax-support)). This is achieved by a [reusable GitHub Actions Workflow](https://github.com/data-apis/array-api-compat/blob/main/.github/workflows/array-api-tests.yml). Most libraries have tests that must be xfailed or skipped for various reasons. These are defined in specific `-xfails.txt` files and are automatically forwarded to array-api-tests. +You may often need to update these xfail files, either to add new xfails +(e.g., because of new test suite features, or because a test that was +previously thought to be passing actually flaky fails). Try to keep the xfails +files organized, with comments pointing to upstream issues whenever possible. + +From time to time, xpass tests should be removed from the xfail files, but be +aware that many xfail tests are flaky, so an xpass should only be removed if +you know that the underlying issue has been fixed. + Array libraries that require a GPU to run (currently only CuPy) cannot be tested on CI. There is a helper script `test_cupy.sh` that can be used to manually test CuPy on a machine with a CUDA GPU. From 2783a1521150bcfc9adf57d7f9c49f4961dfc815 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 13 Mar 2024 13:40:15 -0600 Subject: [PATCH 11/29] Small cleanups to implementation notes --- docs/dev/implementation-notes.md | 40 ++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/docs/dev/implementation-notes.md b/docs/dev/implementation-notes.md index f2b36e75..9de80c48 100644 --- a/docs/dev/implementation-notes.md +++ b/docs/dev/implementation-notes.md @@ -39,32 +39,34 @@ should be taken into account: array-api-compat. The addition of functions that are not part of the array API standard is currently out-of-scope for this package. -- *No side-effects*. array_api_compat behavior should be localized to only the +- *No Side-effects*. array-api-compat behavior should be localized to only the specific code that imports and uses it. It should be invisible to end-users - or users of dependent codes. This specifically applies to the next two + or users of dependent codes. This in particular implies to the next two points. - *No Monkey Patching.* `array-api-compat` should not attempt to modify anything about the underlying library. It is a *wrapper* library only. -- *No modifying the array object.* The array (or tensor) object of the array - library cannot be modified. Attempting to wrap or subclass a library's array - object would break the localized wrapping goal. +- *No Modifying the Array Object.* The array (or tensor) object of the array + library cannot be modified. This also precludes the creation of array + subclasses or wrapper classes. Any behavior that is built-in to the array object, such as the behavior of [array - methods](https://data-apis.org/array-api/latest/API_specification/array_object.html) + methods](https://data-apis.org/array-api/latest/API_specification/array_object.html), is therefore left unwrapped. Users can workaround issues by using corresponding [elementwise functions](https://data-apis.org/array-api/latest/API_specification/elementwise_functions.html) - instead of operators, and using the [helper - functions](../helper-functions.md) provided by array-api-compat instead of - methods like `to_device`. + instead of + [operators](https://data-apis.org/array-api/latest/API_specification/array_object.html#operators), + and by using the [helper functions](../helper-functions.md) provided by + array-api-compat instead of attributes or methods like `x.to_device()`. -- *Avoid Restricting Behavior Outside the Scope of the Standard.* All array - libraries have functions and behaviors that are outside of the scope of what - is specified by the standard. These behaviors should be left intact whenever - possible, unless the standard explicitly disallows something. This means +- *Avoid Restricting Behavior that is Outside the Scope of the Standard.* All + array libraries have functions and behaviors that are outside of the scope + of what is specified by the standard. These behaviors should be left intact + whenever possible, unless the standard explicitly disallows something. This + means - All namespaces are *extended* with wrapper functions. You may notice the extensive use of `import *` in various files in `array_api_compat`. While @@ -74,8 +76,16 @@ should be taken into account: - All wrapper functions pass `**kwargs` through to the wrapped function. - The onus is on users of array-api-compat to ensure they are only using - portable array API behavior, e.g., by testing against [array-api-strict](array-api-strict). + - Input types not supported by the standard should work if they work in the + underlying wrapped function (for instance, Python scalars or `np.ndarray` + subclasses). + + By keeping underlying behaviors intact, it is easier for libraries to swap + out NumPy or other array libraries for array-api-compat, and it is easier + for libraries to write array library-specific code paths. + + The onus is on users of array-api-compat to ensure their array API code is + portable, e.g., by testing against [array-api-strict](array-api-strict). ## Avoiding Code Duplication From dd1ad489e50e4f7b25f65bcc9705a4db07c6592d Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 13 Mar 2024 13:40:33 -0600 Subject: [PATCH 12/29] Add a section explaining the scope of the library --- docs/index.md | 55 +++++++++++++++++++++++++++++++ docs/supported-array-libraries.md | 5 +++ 2 files changed, 60 insertions(+) diff --git a/docs/index.md b/docs/index.md index a62862fe..5a79e129 100644 --- a/docs/index.md +++ b/docs/index.md @@ -120,6 +120,61 @@ references the name "array_api_compat"). Alternatively, the library may be installed as dependency on PyPI. +(scope)= +## 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](https://data-apis.org/array-api/latest/API_specification/index.html). +This includes a small set of [helper functions](helper-functions.md) 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](https://github.com/data-apis/array-api/issues) 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](https://en.wikipedia.org/wiki/Polyfill_(programming)) 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](https://data-apis.org/). 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](https://github.com/data-apis/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. ```{toctree} :titlesonly: diff --git a/docs/supported-array-libraries.md b/docs/supported-array-libraries.md index 90e21e0e..e340c0da 100644 --- a/docs/supported-array-libraries.md +++ b/docs/supported-array-libraries.md @@ -8,6 +8,11 @@ Note that the [`array_namespace()`](helper-functions.md) helper will also support any array library that explicitly supports the array API by defining [`__array_namespace__`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.array.__array_namespace__.html). +Any reasonably popular array library is in-scope for array-api-compat, +assuming it is possible to wrap it to support the array API without too much +complexity. If your favorite library is not supported, feel free to open an +[issue or pull request](https://github.com/data-apis/array-api-compat/issues). + ## [NumPy](https://numpy.org/) and [CuPy](https://cupy.dev/) NumPy 2.0 has full array API compatibility. This package is not strictly From f66b8acba33cd7bc29fbfa4c9f41abef78c0bd06 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 13 Mar 2024 14:38:18 -0600 Subject: [PATCH 13/29] Fix cross-reference --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 5a79e129..1a74da62 100644 --- a/docs/index.md +++ b/docs/index.md @@ -126,7 +126,7 @@ Alternatively, the library may be installed as dependency on PyPI. 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](https://data-apis.org/array-api/latest/API_specification/index.html). -This includes a small set of [helper functions](helper-functions.md) which may +This includes a small set of [helper functions](helper-functions.rst) 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. From 95395bced260b5d1c747ebacc57050afe27c46a6 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 13 Mar 2024 14:59:59 -0600 Subject: [PATCH 14/29] Fix cross-references to helper functions and some RST issues --- array_api_compat/common/_helpers.py | 2 +- docs/conf.py | 6 ++++++ docs/dev/implementation-notes.md | 12 ++++++------ docs/helper-functions.rst | 10 +++++----- docs/index.md | 4 ++-- docs/supported-array-libraries.md | 25 ++++++++++++------------- 6 files changed, 32 insertions(+), 27 deletions(-) diff --git a/array_api_compat/common/_helpers.py b/array_api_compat/common/_helpers.py index 5851ab75..87caded3 100644 --- a/array_api_compat/common/_helpers.py +++ b/array_api_compat/common/_helpers.py @@ -452,7 +452,7 @@ def to_device(x: Array, device: Device, /, *, stream: Optional[Union[int, Any]] `_ and `Stream `_ - objects. For PyTorch, this is the same as ``x.to(device) + objects. For PyTorch, this is the same as `x.to(device) `_ (the ``stream`` argument is not supported in PyTorch). diff --git a/docs/conf.py b/docs/conf.py index 8c902a8a..1e2e472a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -37,6 +37,12 @@ # Make sphinx give errors for bad cross-references nitpicky = True +# autodoc wants to make cross-references for every type hint. But a lot of +# them don't actually refer to anything that we have a document for. +nitpick_ignore = [ + ("py:class", "Array"), + ("py:class", "Device"), +] # Lets us use single backticks for code in RST default_role = 'code' diff --git a/docs/dev/implementation-notes.md b/docs/dev/implementation-notes.md index 9de80c48..679c11ad 100644 --- a/docs/dev/implementation-notes.md +++ b/docs/dev/implementation-notes.md @@ -14,9 +14,9 @@ should be taken into account: - *No Hard Dependencies.* Although array-api-compat "depends" on NumPy, CuPy, PyTorch, etc., it does not hard depend on them. These libraries are not - imported unless either an array object is passed to `array_namespace()`, or - the specific `array_api_compat.` sub-namespace is explicitly - imported. + imported unless either an array object is passed to + {func}`~.array_namespace()`, or the specific `array_api_compat.` + sub-namespace is explicitly imported. - *Vendorability.* array-api-compat should be [vendorable](vendoring). This means that, for instance, all imports in the library are relative imports. @@ -35,7 +35,7 @@ should be taken into account: This also means that we do not at this point in time implement anything other than wrappers for functions in the standard, and basic [helper - functions](../helper-functions.md) that would be useful for most users of + functions](../helper-functions.rst) that would be useful for most users of array-api-compat. The addition of functions that are not part of the array API standard is currently out-of-scope for this package. @@ -59,7 +59,7 @@ should be taken into account: functions](https://data-apis.org/array-api/latest/API_specification/elementwise_functions.html) instead of [operators](https://data-apis.org/array-api/latest/API_specification/array_object.html#operators), - and by using the [helper functions](../helper-functions.md) provided by + and by using the [helper functions](../helper-functions.rst) provided by array-api-compat instead of attributes or methods like `x.to_device()`. - *Avoid Restricting Behavior that is Outside the Scope of the Standard.* All @@ -145,7 +145,7 @@ The majority of the behavior for array-api-compat is tested by the the array API standard. There are also array-api-compat specific tests in [`tests/`](https://github.com/data-apis/array-api-compat/tree/main/tests). These tests should be limited to things that are not tested by the test suite, -e.g., tests for [helper functions](../helper-functions.md) or for behavior +e.g., tests for [helper functions](../helper-functions.rst) or for behavior that is not strictly required by the standard. array-api-tests is run against all supported libraries are tested on CI diff --git a/docs/helper-functions.rst b/docs/helper-functions.rst index b8bfc732..a30d69ae 100644 --- a/docs/helper-functions.rst +++ b/docs/helper-functions.rst @@ -13,15 +13,16 @@ array_namespace The `array_namespace()` function is the primary entry-point for array API consuming libraries. + .. autofunction:: array_namespace - :canonical: array_api_compat.array_namespace +.. autofunction:: is_array_api_obj Array Method Helpers -------------------- array-api-compat does not attempt to wrap or monkey patch the array object for any library. Consequently, any API differences for the [array -object](https://data-apis.org/array-api/latest/API_specification/array_object.htmlK +object](https://data-apis.org/array-api/latest/API_specification/array_object.html) cannot be directly wrapped. Some libraries do not define some of these methods or define them differently. For these, helper functions are provided which can be used instead. @@ -35,14 +36,13 @@ instead, which would be wrapped. .. autofunction:: to_device .. autofunction:: size -Inspect Helpers ---------------- +Inspection Helpers +------------------ These convenience functions can be used to test if an array comes from a specific library without importing that library if it hasn't been imported yet. -.. autofunction:: is_array_api_obj .. autofunction:: is_numpy_array .. autofunction:: is_cupy_array .. autofunction:: is_torch_array diff --git a/docs/index.md b/docs/index.md index 1a74da62..287c7a12 100644 --- a/docs/index.md +++ b/docs/index.md @@ -23,7 +23,7 @@ version](https://data-apis.org/array-api/2022.12/) of the standard. python -m pip install array-api-compat ``` -and [Conda-forge](https://anaconda.org/conda-forge/array-api-compat) +and [conda-forge](https://anaconda.org/conda-forge/array-api-compat) ``` conda install --channel conda-forge array-api-compat @@ -32,7 +32,7 @@ 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 {func}`array_namespace()`, like +compliant namespace from the input arrays using {func}`~.array_namespace()`, like ```py def your_function(x, y): diff --git a/docs/supported-array-libraries.md b/docs/supported-array-libraries.md index e340c0da..61de4b65 100644 --- a/docs/supported-array-libraries.md +++ b/docs/supported-array-libraries.md @@ -4,8 +4,8 @@ The following array libraries are supported. This page outlines the known differences between this library and the array API specification for the supported packages. -Note that the [`array_namespace()`](helper-functions.md) helper will also -support any array library that explicitly supports the array API by defining +Note that the {func}`~.array_namespace()` helper will also support any array +library that explicitly supports the array API by defining [`__array_namespace__`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.array.__array_namespace__.html). Any reasonably popular array library is in-scope for array-api-compat, @@ -17,7 +17,7 @@ complexity. If your favorite library is not supported, feel free to open an NumPy 2.0 has full array API compatibility. This package is not strictly necessary for NumPy 2.0 support, but may still be useful for the support of -other libraries, as well as for the [helper functions](helper-functions.md). +other libraries, as well as for the [helper functions](helper-functions.rst). For NumPy 1.26, as well as corresponding versions of CuPy, the following deviations from the standard should be noted: @@ -25,10 +25,10 @@ deviations from the standard should be noted: - The array methods `__array_namespace__`, `device` (for NumPy), `to_device`, and `mT` are not defined. This reuses `np.ndarray` and `cp.ndarray` and we don't want to monkey patch or wrap it. The [helper - functions](helper-functions.md) `device()` and `to_device()` are provided to - work around these missing methods. `x.mT` can be replaced with - `xp.linalg.matrix_transpose(x)`. `array_namespace(x)` should be used instead - of `x.__array_namespace__`. + functions](helper-functions.rst) {func}`~.device()` and {func}`~.to_device()` + are provided to work around these missing methods. `x.mT` can be replaced + with `xp.linalg.matrix_transpose(x)`. {func}`~.array_namespace()` should be + used instead of `x.__array_namespace__`. - Value-based casting for scalars will be in effect unless explicitly disabled with the environment variable `NPY_PROMOTION_STATE=weak` or @@ -63,14 +63,14 @@ version. - Like NumPy/CuPy, we do not wrap the `torch.Tensor` object. It is missing the `__array_namespace__` and `to_device` methods, so the corresponding helper - functions `array_namespace()` and `to_device()` in this library should be - used instead (see above). + functions {func}`~.array_namespace()` and {func}`~.to_device()` in this + library should be used instead. - The `x.size` attribute on `torch.Tensor` is a function that behaves differently from [`x.size`](https://data-apis.org/array-api/draft/API_specification/generated/array_api.array.size.html) - in the spec. Use the `size(x)` helper function as a portable workaround (see - above). + in the spec. Use the {func}`~.size()` helper function as a portable + workaround. - PyTorch does not have unsigned integer types other than `uint8`, and no attempt is made to implement them here. @@ -95,8 +95,7 @@ version. [`var()`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.var.html#array_api.var) do not support floating-point `correction`. -- The `stream` argument of the `to_device()` helper (see above) is not - supported. +- The `stream` argument of the {func}`~.to_device()` helper is not supported. - As with NumPy, type annotations and positional-only arguments may not exactly match the spec for functions that are not wrapped at all. From 71906baeaadca4863f36eaa2b72f3f608e2dd6fc Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 13 Mar 2024 15:00:59 -0600 Subject: [PATCH 15/29] Just refer to the library as "array-api-compat" --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 1e2e472a..c1c2f2f6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -10,7 +10,7 @@ import os sys.path.insert(0, os.path.abspath('..')) -project = 'Array API Compat' +project = 'array-api-compat' copyright = '2024, Consortium for Python Data API Standards' author = 'Consortium for Python Data API Standards' From 0896b3db04fb8578a79456b3a96e3d84232d9887 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 13 Mar 2024 15:15:54 -0600 Subject: [PATCH 16/29] Add a favicon --- docs/_static/favicon.png | Bin 0 -> 5152 bytes docs/conf.py | 6 ++++++ 2 files changed, 6 insertions(+) create mode 100644 docs/_static/favicon.png diff --git a/docs/_static/favicon.png b/docs/_static/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..49b7d9d6fa9e82e24c907c324f53df2dbf11a222 GIT binary patch literal 5152 zcmbtY_fr#quSW$bLueVLBCBOjt$?)1EVNA72(py2q_X!EW$#g;>?Io!s0`W277$Rj z4A~$BL}bY3`R=_x;QQ|7F8SQ$l1nb*ha^fzOXU_dD>WGz*)26yWxapc{g1l$>)~zbW<#cE?ToeIQggJlv(dA$wDxuFw~-+uW9(E@Ry6RL+0Ld6 zVD14&Xj4bllBofU$OT6q0z!p?4Y;{lrMN@20Ec@z$*g{H3O|91dkTqQlx`FCsK!sj z*F2$#U!lGmUs|tm{|rfPZT7!f*!?K}qVfB@`2NoD_OCAt*Z!sQA5ygyo2I9Pa768T zE#fYB+4s_Bfgen9b%pc3ja@_wYUn}tpAP)++X}Jw2<;&aruk^rjuTBBoHa%2#A>ID z_^aQS&H{^^yo1;sAhyv!9Ugt<()Q!uXR{d8AQGKrul~lzf6KBc%1jzV(B~1Xbkg5| z?etPEv%kPlTJfIY@;#?iB^$`e3&e0HSLhD)b1s%dPL}wsh07oyHxg@}x*~JQ3$lp! zv1aQn^i)N;coC->Lv8;9S3+_%xH z0%+Qj2;}e&wiSIKtkHOTD%q{!(Q=)GnW|R*k#{!R3L-A=#S$1kbLvdg0D;e#S3Q7z z4RRZmYT%`{HzT*=_u7$d?oeet<7s)(5^;glW)>Db0@G|fI@YC$!7Fx^fIZvG_kwnI zUQ#3j8t3Gf=Hjffq$#WdB37D1rkY#>oKcZdcPmWHblmI;o<)Pl2eIcJ6#Pcf@M2~q z4G=@`-*m~gKxF+puI{bsPe!9rt4Vcrbt@IJTJZ_UTLpt33!UKvX-$L`H3)oMtZQVJ zP%&!cGNl=sx^kz3Xg>Ht7Ew>n)x7|3fBM8vepbXn7^R)t>bQU5>cs$e8tu6Q>qU?d zdlgW0+JZKRG2P;{7ng6-`N0!hcJ~o^-(l1nMq*~_X@1c z{XD=EWc{Wm#58J)!D4H~H4Ml!EycMOXjDnP*6_*9!@MIad-cKcE$OR16fb5&Ey1%f z_jTY{DK+8m{1K1$`hftdvq8OP6Vf*kVdeMrWmwv53HzGAGdYW~*!@v)kX^}h-rmLq zQLIP$xiZU{NMndXaAVsKqW{xsbjCx{`Jm%|JxX#?amF?H?8fTxaAK{<%3a?lAGh0YUuO7?YoQ z#><#~|LDiWMh%hMlDkDL9=tNZ7eZD&z7XukC z*KftDvTreSe`HsMZf?cEYgC*(M+Hc_H(-hVVUZ=z=zQ@cMa7UUw;!oJzpEMHm0!PU zP>%QK^~DZSU7j8M;t&G&xNB(ssRj&tadmRwH;|$#SDJq?pEh4Sm}L%>p%qTn$f;N_ z=907~-VIwAvUv&DQ}fG+H=F))!n4)Bqrv-UT$ku`&J(w3wMcNSl0G6>t*yF5X_*sG z#5T7!T~P4w8c6!KLTp6&edAXE;4(AQUZQ$ZE`jdnXJ1Zm*Xviel@rw{`SiD3+udF! zX_-yVi;+>{osrB@uFo>K8nsQvhJnCKw zI&o7(y9N;M?}ObgR*8~%Vs9Db8+=9)^9>+5Y76_!SZhE2@*IPIau#pq^L$)M%>sJf zoV<7dII%}uXqC0#*Yi%=KJ18zV3fvn9--5;*!e#*LzC&Uryi4EiQ&CAP9G@cqPBCY zW5;4^cMZ5UUU1!1R@DUwf$?ptlm_3Y40BNq?4r9H25%+GibQ=Qk8-m5PX0=8=`=q} z8Roaw%2=r4*~k_YA&HMKT6fN`dE@OJq_yTsm5+C_>h(XzZi$r9`+^DdD@=J238JuZ zw(UG4sI_f9Y2@l;XWR&R5#^Pgh6-6{|0>~Qvv1lB*f!0uex+t?@MQd_atznzJ(7%? zG6GFGj1%rm)53cd2%%R)5Me(%Rzi))y8wHrSRDe2V``)}2|{5^tL^Fa8saR}DFRl^ z=%n=I54H@`(JM_j%PP+9J1XU(&#Bt+;9isCVjgb{lrc=~dOMKUdsP5f&`s@!b*h~n zYBm(la*8@{C$bod@{J+D#gqw`zSeorFjawGwbQ-QNRWaO%XzVV_26J_G280*R^g-o zn2<=h1g$Dpr^BBd823c{*hv0N^t}%ug}Hi%%OCW@(LSw9WZ~fD3G8tV#148>*@qjX z$*U}5p8|=npIwqH*9jMG%D1;P5aF>0QUClN>+THZ&diRw7ty3u6Is1$kXAf)Dg`&0J3WkRm2&ViD%>80L{{>C5cDtLFIe$}m&NtoZ(ft#8xc-+`^+ zYV9|scV&}D>AOsz&ZctL8>Bd(x`F_!+%@(^YEr#7*qV|m3 z&lbVZ3_tv75!)VJz185TT&n+gpaPLv03GJ+O<(SYVI1kITMEXLGxdZG7#A~bOrk-W zt+wpz0lbfPCbRaBni#w0ZDJVmr$j(YE$cAa7rc+=ht0Gm5O*z^K+D7n$Ej|-T%1Qe z{nyi{kISilJ0{jAos~9t)DMoXR=PFUXAkwbcvC^seOt6&(ay@KJdDRRUj)^O^xE^| ze&Xt#c*qvB{vI*X-TupKHmtDO?8E6RUmI4k+xE7Gq0vkRHr_Cmz=NQmt#w!eo@L*8tzf;~RESc1Sa6~%6Q0SVNXY!~PpwcOr zqEyYMEcu0j&HmG(p);BeN>QvaAzLI|=8zBtq;5xr0`S)iZrqp>1O?O2U_$v)cGStw z7?($|5@q)zr{tF3g=Gb%j)q?0gu@GzN^$COzYd5btIrLzZvl08LpI)T1W;bJ&uINw zJ8OJ5lgekvS~~1@aN%JcftX!UbhmtiqnD~O$=PR}Yp;%kyB-QYs&7}jC4806#NFKcn|;?wrV5TQ-{8pUl#K&6EI+M3rT{Qy|Qx=sN1GpbF`ElW3wY zUsv(PWS9dC6@$w)Mf)VY%BI=F!ErA7>U^8ons27&9?TPubj4pjShHg0F8@umQ=w2r zLbEivFld}%E37{e88BD9N!ME5Q$iu31F5Kt^cT9cXb1%dW74Vn&_G@#3 zOEJ8El8gV9PVeOU#%8L{Mf=#?n1(IbUULUWw-&fN05e#&>VFOcSqDdxv9?)@gjs0R z-!{buNuQ4h6>rqC2F_!g79=->F}7n&!5U2vd>N9(F-5qSI-_1pFBPWxrqq1st|3x+ ze$euRaB$(fC!rHrRYi4YSp5D-$=qKkt8X-f^V{#q`-#PMf*E>LJ=2ObDC@*gy^Ot( zoxf#gCHTk1bajRH25-;;I235|>y*Y+>2%fiOc-a4)(L&FMCEGb%u<($TGcP{=zB?! z^wItb<~#O32$CcRN%z*Hvqf$eY`Jo_T+*rMc5s9OGX{@6gw#SZCYM^E zXysI?bjbZ-_iHITNM7A``Frp0#@zcB0JBE)*>lC6BzzI_Nqc;5y>C9+*Q`lUVB=Gv zEJJjxP8C*?d=}y_zcNt-@nhEUq)&s%%9lST3}Dv%E*A3vkdL?mlKZ9tc_0-?`)dN8 zPbXkKmrqFK&wBf6tZ_GjuEn1sAn=hRH2>#j$jKQ|vxY3Xo4V6}Aw;a9qbQ zEnV+7|44O}GV5e|C=lEh7HLe&9Vpg(J-z zk_M`uzq5QQfA0u5N&#g!E-D9dg^Mazn-NRio0blI(&Gs4WwR<@>2YyM6i;S3JT~Zl z{ssH&i_bt*bxQFSmv`Kx>-pWZwXa_cE!Uls8h^65|J(DE8AZ8U8Hp{bklP{ZSphJI zg7mF5WT#2|U*8(`x5$ya*Bvkn*^^)DfxX!A5QrLKDGczsV`IoKZQV)FnA+*>Vf44e zwu-w|q2qZEfh%>ymWaBH>kr6XWk%93Ty_fLWbJC0R(*b11yr4ae;krNFiq&rUg?sb zoZ^1(OL=ZM)W>wyS7~i*Kl*p`dG_vC{-sDi^dxof$D$EA3Zbiz!D~=juJD2tg$WKP zJzw%C#YFNyaZQbxYbzxGok!$nU{p2j?%W`XTq$zHY^hwRN3`F;vpQO^Qk+?A z=t}_$v9Cp$;)vWn;wLo24ta4GxgIl$5O(pxRW8!4k!3}OCT7VR1?t>XpN0>o?lC-< zeMttjP(0g%W`!H~{OrXhZ_LH@s5-iUa$Nmpg*qWyuGay3;aHI);nGS7a!zhZMm#2| z_ZduPT_U(L?CQ&jh+L(pfEggkXXZ4QfMFKBt)prV|Iqy4!-Jwxl3D0k7G~@a0D30D z*(b=v7pf{Z6Z?jGBBlq@eew)k5F*x79qE#ZHqJW*5H4;4VLjc6)Kam8TWnn?zyU%} zj5~dF+|aYJ3pbAz6}PFM3A3N&VRzIOL&DvDr2Ocgd$XU_QfP-{ zlVE~ojJ@*^cLQyR`?OStc7g7TQ_*j0^+{W)7xJYTlm@XYZV@K^TF fj4S;lm+S^vx7AGCc+NcFza7=kTFRBEXTkpkp8ADl literal 0 HcmV?d00001 diff --git a/docs/conf.py b/docs/conf.py index c1c2f2f6..ea1e018f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -70,3 +70,9 @@ }, ], } + +# Logo + +html_favicon = "_static/favicon.png" + +# html_logo = "_static/logo.svg" From 1062501f409222edb117a7b0aa4b1662e5ce3afd Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 13 Mar 2024 15:25:54 -0600 Subject: [PATCH 17/29] Add docs build and deploy workflow --- .github/workflows/docs.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/docs.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..e7b9ea9a --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,30 @@ +name: Docs +on: [push, pull_request] +jobs: + docs: + runs-on: ubuntu-latest + strategy: + fail-fast: false + environment: + name: docs-build-and-deploy + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + - name: Install Dependencies + run: | + python -m pip install -r docs/requirements.txt + + - name: Build Docs + run: | + cd docs + make html + + # Note, the gh-pages deployment requires setting up a SSH deploy key. + # See + # https://github.com/JamesIves/github-pages-deploy-action/tree/dev#using-an-ssh-deploy-key- + - name: Deploy + uses: JamesIves/github-pages-deploy-action@v4 + if: ${{ github.ref == 'refs/heads/main' }} + with: + folder: docs/_build/html + ssh-key: ${{ secrets.DEPLOY_KEY }} From 99e663d34fd39d3f98fd255e8e14c5198abe04f4 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 13 Mar 2024 15:30:09 -0600 Subject: [PATCH 18/29] Allow docs to deploy from this branch to see if it works --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e7b9ea9a..7a95bd0d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -24,7 +24,7 @@ jobs: # https://github.com/JamesIves/github-pages-deploy-action/tree/dev#using-an-ssh-deploy-key- - name: Deploy uses: JamesIves/github-pages-deploy-action@v4 - if: ${{ github.ref == 'refs/heads/main' }} + if: ${{ github.ref == 'refs/heads/docs' }} with: folder: docs/_build/html ssh-key: ${{ secrets.DEPLOY_KEY }} From 3585a8a83c45fc6eb9afa3222ecce15f9742da14 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 13 Mar 2024 15:41:59 -0600 Subject: [PATCH 19/29] Disable Furo fancy scroll behavior --- docs/_static/custom.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/_static/custom.css b/docs/_static/custom.css index 9b024274..bac04989 100644 --- a/docs/_static/custom.css +++ b/docs/_static/custom.css @@ -4,3 +4,9 @@ body { -webkit-font-smoothing: auto; -moz-osx-font-smoothing: auto; } + +/* Disable the fancy scrolling behavior when jumping to headers (this is too + slow for long pages) */ +html { + scroll-behavior: auto; +} From 1344568f227d6da83f1e36d8ce7be6893b4a94b4 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 13 Mar 2024 15:44:23 -0600 Subject: [PATCH 20/29] Use intersphinx instead of links to other docs' URLs --- array_api_compat/common/_helpers.py | 16 +++++++--------- docs/conf.py | 8 ++++++++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/array_api_compat/common/_helpers.py b/array_api_compat/common/_helpers.py index 87caded3..25419c01 100644 --- a/array_api_compat/common/_helpers.py +++ b/array_api_compat/common/_helpers.py @@ -448,13 +448,11 @@ def to_device(x: Array, device: Device, /, *, stream: Optional[Union[int, Any]] ----- For NumPy, this function effectively does nothing since the only supported - device is the CPU. For CuPy, this method supports CuPy CUDA `Device - `_ - and `Stream - `_ - objects. For PyTorch, this is the same as `x.to(device) - `_ (the - ``stream`` argument is not supported in PyTorch). + device is the CPU. For CuPy, this method supports CuPy CUDA + :external+cupy:class:`Device ` and + :external+cupy:class:`Stream ` objects. For PyTorch, + this is the same as :external+torch:meth:`x.to(device) ` + (the ``stream`` argument is not supported in PyTorch). See Also -------- @@ -492,8 +490,8 @@ def size(x): This is equivalent to `x.size` according to the `standard `__. - This helper is included because PyTorch defines `size` in an `incompatible - way `__. + This helper is included because PyTorch defines `size` in an + :external+torch:meth:`incompatible way `. """ if None in x.shape: diff --git a/docs/conf.py b/docs/conf.py index ea1e018f..d8d5c2da 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -24,9 +24,17 @@ 'myst_parser', 'sphinx.ext.autodoc', 'sphinx.ext.napoleon', + 'sphinx.ext.intersphinx', 'sphinx_copybutton', ] +intersphinx_mapping = { + 'cupy': ('https://docs.cupy.dev/en/stable', None), + 'torch': ('https://pytorch.org/docs/stable/', None), +} +# Require :external: to reference intersphinx. +intersphinx_disabled_reftypes = ['*'] + templates_path = ['_templates'] exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] From 1321368e270c692da4704b1692090a2e9408c2d0 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 13 Mar 2024 16:23:23 -0600 Subject: [PATCH 21/29] Clear most of the README content and point to the docs --- README.md | 392 +----------------------------------------------------- 1 file changed, 1 insertion(+), 391 deletions(-) diff --git a/README.md b/README.md index 784197dc..8f567606 100644 --- a/README.md +++ b/README.md @@ -6,394 +6,4 @@ NumPy, CuPy, PyTorch, Dask, and JAX are supported. If you want support for other libraries, or if you encounter any issues, please [open an issue](https://github.com/data-apis/array-api-compat/issues). -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 [2022.12 -version](https://data-apis.org/array-api/2022.12/) of the standard. - -## Install - -`array-api-compat` is available on both [PyPI](https://pypi.org/project/array-api-compat/) - -``` -python -m pip install array-api-compat -``` - -and [Conda-forge](https://anaconda.org/conda-forge/array-api-compat) - -``` -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 - -```py -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 - -```py -import array_api_compat.numpy as np -``` - -```py -import array_api_compat.cupy as cp -``` - -```py -import array_api_compat.torch as torch -``` - -```py -import array_api_compat.dask as da -``` - -> [!NOTE] -> There is no `array_api_compat.jax` submodule. JAX support is contained -> in JAX itself in the `jax.experimental.array_api` module. array-api-compat simply -> wraps that submodule. The main JAX support in this module consists of -> supporting it in the [helper functions](#helper-functions) defined below. - -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](https://numpy.org/neps/nep-0047-array-api-standard.html)). 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](https://data-apis.org/array-api/latest/API_specification/type_promotion.html)). -(`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. - -## Helper Functions - -In addition to the wrapped library namespaces and functions in the array API -specification, there are several helper functions included here that aren't -part of the specification but which are useful for using the array API: - -- `is_array_api_obj(x)`: Return `True` if `x` is an array API compatible array - object. - -- `is_numpy_array(x)`, `is_cupy_array(x)`, `is_torch_array(x)`, - `is_dask_array(x)`, `is_jax_array(x)`: return `True` if `x` is an array from - the corresponding library. These functions do not import the underlying - library if it has not already been imported, so they are cheap to use. - -- `array_namespace(*xs)`: Get the corresponding array API namespace for the - arrays `xs`. For example, if the arrays are NumPy arrays, the returned - namespace will be `array_api_compat.numpy`. Note that this function will - also work for namespaces that aren't supported by this compat library but - which do support the array API (i.e., arrays that have the - `__array_namespace__` attribute). - -- `device(x)`: Equivalent to - [`x.device`](https://data-apis.org/array-api/latest/API_specification/generated/signatures.array_object.array.device.html) - in the array API specification. Included because `numpy.ndarray` does not - include the `device` attribute and this library does not wrap or extend the - array object. Note that for NumPy and dask, `device(x)` is always `"cpu"`. - -- `to_device(x, device, /, *, stream=None)`: Equivalent to - [`x.to_device`](https://data-apis.org/array-api/latest/API_specification/generated/signatures.array_object.array.to_device.html). - Included because neither NumPy's, CuPy's, Dask's, nor PyTorch's array objects - include this method. For NumPy, this function effectively does nothing since - the only supported device is the CPU, but for CuPy, this method supports - CuPy CUDA - [Device](https://docs.cupy.dev/en/stable/reference/generated/cupy.cuda.Device.html) - and - [Stream](https://docs.cupy.dev/en/stable/reference/generated/cupy.cuda.Stream.html) - objects. For PyTorch, this is the same as - [`x.to(device)`](https://pytorch.org/docs/stable/generated/torch.Tensor.to.html) - (the `stream` argument is not supported in PyTorch). - -- `size(x)`: Equivalent to - [`x.size`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.array.size.html#array_api.array.size), - i.e., the number of elements in the array. Included because PyTorch's - `Tensor` defines `size` as a method which returns the shape, and this cannot - be wrapped because this compat library doesn't wrap or extend the array - objects. - -## Known Differences from the Array API Specification - -There are some known differences between this library and the array API -specification: - -### NumPy and CuPy - -- The array methods `__array_namespace__`, `device` (for NumPy), `to_device`, - and `mT` are not defined. This reuses `np.ndarray` and `cp.ndarray` and we - don't want to monkeypatch or wrap it. The helper functions `device()` and - `to_device()` are provided to work around these missing methods (see above). - `x.mT` can be replaced with `xp.linalg.matrix_transpose(x)`. - `array_namespace(x)` should be used instead of `x.__array_namespace__`. - -- Value-based casting for scalars will be in effect unless explicitly disabled - with the environment variable `NPY_PROMOTION_STATE=weak` or - `np._set_promotion_state('weak')` (requires NumPy 1.24 or newer, see [NEP - 50](https://numpy.org/neps/nep-0050-scalar-promotion.html) and - https://github.com/numpy/numpy/issues/22341) - -- `asarray()` does not support `copy=False`. - -- Functions which are not wrapped may not have the same type annotations - as the spec. - -- Functions which are not wrapped may not use positional-only arguments. - -The minimum supported NumPy version is 1.21. However, this older version of -NumPy has a few issues: - -- `unique_*` will not compare nans as unequal. -- `finfo()` has no `smallest_normal`. -- No `from_dlpack` or `__dlpack__`. -- `argmax()` and `argmin()` do not have `keepdims`. -- `qr()` doesn't support matrix stacks. -- `asarray()` doesn't support `copy=True` (as noted above, `copy=False` is not - supported even in the latest NumPy). -- Type promotion behavior will be value based for 0-D arrays (and there is no - `NPY_PROMOTION_STATE=weak` to disable this). - -If any of these are an issue, it is recommended to bump your minimum NumPy -version. - -### PyTorch - -- Like NumPy/CuPy, we do not wrap the `torch.Tensor` object. It is missing the - `__array_namespace__` and `to_device` methods, so the corresponding helper - functions `array_namespace()` and `to_device()` in this library should be - used instead (see above). - -- The `x.size` attribute on `torch.Tensor` is a function that behaves - differently from - [`x.size`](https://data-apis.org/array-api/draft/API_specification/generated/array_api.array.size.html) - in the spec. Use the `size(x)` helper function as a portable workaround (see - above). - -- PyTorch does not have unsigned integer types other than `uint8`, and no - attempt is made to implement them here. - -- PyTorch has type promotion semantics that differ from the array API - specification for 0-D tensor objects. The array functions in this wrapper - library do work around this, but the operators on the Tensor object do not, - as no operators or methods on the Tensor object are modified. If this is a - concern, use the functional form instead of the operator form, e.g., `add(x, - y)` instead of `x + y`. - -- [`unique_all()`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.unique_all.html#array_api.unique_all) - is not implemented, due to the fact that `torch.unique` does not support - returning the `indices` array. The other - [`unique_*`](https://data-apis.org/array-api/latest/API_specification/set_functions.html) - functions are implemented. - -- Slices do not support negative steps. - -- [`std()`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.std.html#array_api.std) - and - [`var()`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.var.html#array_api.var) - do not support floating-point `correction`. - -- The `stream` argument of the `to_device()` helper (see above) is not - supported. - -- As with NumPy, type annotations and positional-only arguments may not - exactly match the spec for functions that are not wrapped at all. - -The minimum supported PyTorch version is 1.13. - -### JAX - -Unlike the other libraries supported here, JAX array API support is contained -entirely in the JAX library. The JAX array API support is tracked at -https://github.com/google/jax/issues/18353. - -## Dask - -If you're using dask with numpy, many of the same limitations that apply to numpy -will also apply to dask. Besides those differences, other limitations include missing -sort functionality (no `sort` or `argsort`), and limited support for the optional `linalg` -and `fft` extensions. - -In particular, the `fft` namespace is not compliant with the array API spec. Any functions -that you find under the `fft` namespace are the original, unwrapped functions under [`dask.array.fft`](https://docs.dask.org/en/latest/array-api.html#fast-fourier-transforms), which may or may not be Array API compliant. Use at your own risk! - -For `linalg`, several methods are missing, for example: -- `cross` -- `det` -- `eigh` -- `eigvalsh` -- `matrix_power` -- `pinv` -- `slogdet` -- `matrix_norm` -- `matrix_rank` -Other methods may only be partially implemented or return incorrect results at times. - -The minimum supported Dask version is 2023.12.0. - -## 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 on PyPI. - -## Implementation Notes - -As noted before, the goal of this library is to reuse the NumPy and CuPy array -objects, rather than wrapping or extending them. This means that the functions -need to accept and return `np.ndarray` for NumPy and `cp.ndarray` for CuPy. - -Each namespace (`array_api_compat.numpy`, `array_api_compat.cupy`, and -`array_api_compat.torch`) is populated with the normal library namespace (like -`from numpy import *`). Then specific functions are replaced with wrapped -variants. - -Since NumPy and CuPy are nearly identical in behavior, most wrapping logic can -be shared between them. Wrapped functions that have the same logic between -NumPy and CuPy are in `array_api_compat/common/`. -These functions are defined like - -```py -# In array_api_compat/common/_aliases.py - -def acos(x, /, xp): - return xp.arccos(x) -``` - -The `xp` argument refers to the original array namespace (either `numpy` or -`cupy`). Then in the specific `array_api_compat/numpy/` and -`array_api_compat/cupy/` namespaces, the `@get_xp` decorator is applied to -these functions, which automatically removes the `xp` argument from the -function signature and replaces it with the corresponding array library, like - -```py -# In array_api_compat/numpy/_aliases.py - -from ..common import _aliases - -import numpy as np - -acos = get_xp(np)(_aliases.acos) -``` - -This `acos` now has the signature `acos(x, /)` and calls `numpy.arccos`. - -Similarly, for CuPy: - -```py -# In array_api_compat/cupy/_aliases.py - -from ..common import _aliases - -import cupy as cp - -acos = get_xp(cp)(_aliases.acos) -``` - -Since NumPy and CuPy are nearly identical in their behaviors, this allows -writing the wrapping logic for both libraries only once. - -PyTorch uses a similar layout in `array_api_compat/torch/`, but it differs -enough from NumPy/CuPy that very few common wrappers for those libraries are -reused. - -See https://numpy.org/doc/stable/reference/array_api.html for a full list of -changes from the base NumPy (the differences for CuPy are nearly identical). A -corresponding document does not yet exist for PyTorch, but you can examine the -various comments in the -[implementation](https://github.com/data-apis/array-api-compat/blob/main/array_api_compat/torch/_aliases.py) -to see what functions and behaviors have been wrapped. - - -## Releasing - -To release, first note that CuPy must be tested manually (it isn't tested on -CI). Use the script - -``` -./test_cupy.sh -``` - -on a machine with a CUDA GPU. - -Once you are ready to release, create a PR with a release branch, so that you -can verify that CI is passing. You must edit - -``` -array_api_compat/__init__.py -``` - -and update the version (the version is not computed from the tag because that -would break vendorability). You should also edit - -``` -CHANGELOG.md -``` - -with the changes for the release. - -Then create a tag - -``` -git tag -a -``` - -and push it to GitHub - -``` -git push origin -``` - -Check that the `publish distributions` action works. Note that this action -will run even if the other CI fails, so you must make sure that CI is passing -*before* tagging. - -This does mean you can ignore CI failures, but ideally you should fix any -failures or update the `*-xfails.txt` files before tagging, so that CI and the -cupy tests pass. Otherwise it will be hard to tell what things are breaking in -the future. It's also a good idea to remove any xpasses from those files (but -be aware that some xfails are from flaky failures, so unless you know the -underlying issue has been fixed, a xpass test is probably still xfail). +See the documentation for more details https://data-apis.org/array-api-compat/ From 22224551c46eb96d955e1fee5391f00b960662a6 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 13 Mar 2024 18:28:22 -0600 Subject: [PATCH 22/29] Don't force push to gh-pages --- .github/workflows/docs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 7a95bd0d..e382ed6f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -28,3 +28,4 @@ jobs: with: folder: docs/_build/html ssh-key: ${{ secrets.DEPLOY_KEY }} + force: no From fd4b0ed209ab7112f897777fca4804941fb6fd43 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 13 Mar 2024 18:43:00 -0600 Subject: [PATCH 23/29] Add CONTRIBUTING.md --- CONTRIBUTING.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..d2469f4a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,7 @@ +Contributions to array-api-compat are welcome, so long as they are [in +scope](https://data-apis.org/array-api-compat/index.html#scope). + +Contributors are encouraged to read through the [development +notes](https://data-apis.org/array-api-compat/dev/index.html) for the package +to get full context on some of the design decisions and implementation +details used in the codebase. From fe645ffc8c14b6fef66c874403351a873cffec26 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 13 Mar 2024 18:53:04 -0600 Subject: [PATCH 24/29] Some small docs changes --- docs/dev/implementation-notes.md | 9 +++++---- docs/supported-array-libraries.md | 8 ++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/dev/implementation-notes.md b/docs/dev/implementation-notes.md index 679c11ad..707bd940 100644 --- a/docs/dev/implementation-notes.md +++ b/docs/dev/implementation-notes.md @@ -37,9 +37,10 @@ should be taken into account: other than wrappers for functions in the standard, and basic [helper functions](../helper-functions.rst) that would be useful for most users of array-api-compat. The addition of functions that are not part of the array - API standard is currently out-of-scope for this package. + API standard is currently out-of-scope for this package (see the + [Scope](scope) section of the documentation). -- *No Side-effects*. array-api-compat behavior should be localized to only the +- *No Side-Effects*. array-api-compat behavior should be localized to only the specific code that imports and uses it. It should be invisible to end-users or users of dependent codes. This in particular implies to the next two points. @@ -51,8 +52,8 @@ should be taken into account: library cannot be modified. This also precludes the creation of array subclasses or wrapper classes. - Any behavior that is built-in to the array object, such as the behavior of - [array + Any non-standard behavior that is built-in to the array object, such as the + behavior of [array methods](https://data-apis.org/array-api/latest/API_specification/array_object.html), is therefore left unwrapped. Users can workaround issues by using corresponding [elementwise diff --git a/docs/supported-array-libraries.md b/docs/supported-array-libraries.md index 61de4b65..861b74bd 100644 --- a/docs/supported-array-libraries.md +++ b/docs/supported-array-libraries.md @@ -66,11 +66,11 @@ version. functions {func}`~.array_namespace()` and {func}`~.to_device()` in this library should be used instead. -- The `x.size` attribute on `torch.Tensor` is a function that behaves - differently from +- The {external+torch:meth}`x.size() ` attribute on + `torch.Tensor` is a method that behaves differently from the [`x.size`](https://data-apis.org/array-api/draft/API_specification/generated/array_api.array.size.html) - in the spec. Use the {func}`~.size()` helper function as a portable - workaround. + attribute in the spec. Use the {func}`~.size()` helper function as a + portable workaround. - PyTorch does not have unsigned integer types other than `uint8`, and no attempt is made to implement them here. From 0f7fdadf4f98f3b38190434948500a1941ec6254 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 14 Mar 2024 14:41:42 -0600 Subject: [PATCH 25/29] Revert "Allow docs to deploy from this branch to see if it works" This reverts commit 99e663d34fd39d3f98fd255e8e14c5198abe04f4. --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e382ed6f..07603e5a 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -24,7 +24,7 @@ jobs: # https://github.com/JamesIves/github-pages-deploy-action/tree/dev#using-an-ssh-deploy-key- - name: Deploy uses: JamesIves/github-pages-deploy-action@v4 - if: ${{ github.ref == 'refs/heads/docs' }} + if: ${{ github.ref == 'refs/heads/main' }} with: folder: docs/_build/html ssh-key: ${{ secrets.DEPLOY_KEY }} From 00d025d8ed6a30e33844a326b9d6e22fefd9f743 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 14 Mar 2024 14:48:07 -0600 Subject: [PATCH 26/29] Use better header name --- docs/helper-functions.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/helper-functions.rst b/docs/helper-functions.rst index a30d69ae..0b0e013a 100644 --- a/docs/helper-functions.rst +++ b/docs/helper-functions.rst @@ -7,8 +7,8 @@ In addition to the wrapped library namespaces and functions in the array API specification, there are several helper functions included here that aren't part of the specification but which are useful for using the array API: -array_namespace ---------------- +Entry-point Helpers +------------------- The `array_namespace()` function is the primary entry-point for array API consuming libraries. From c68389e7026d68773ec3c44e0a1c77984fb721b6 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 14 Mar 2024 14:49:34 -0600 Subject: [PATCH 27/29] Fix some RST syntax --- docs/helper-functions.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/helper-functions.rst b/docs/helper-functions.rst index 0b0e013a..dcaa2e44 100644 --- a/docs/helper-functions.rst +++ b/docs/helper-functions.rst @@ -21,15 +21,16 @@ Array Method Helpers -------------------- array-api-compat does not attempt to wrap or monkey patch the array object for -any library. Consequently, any API differences for the [array -object](https://data-apis.org/array-api/latest/API_specification/array_object.html) +any library. Consequently, any API differences for the `array object +`__ cannot be directly wrapped. Some libraries do not define some of these methods or define them differently. For these, helper functions are provided which can be used instead. Note that if you have a compatibility issue with an operator method (like -`__add__`, i.e., `+`) you can prefer to use the corresponding [elementwise -function](https://data-apis.org/array-api/latest/API_specification/elementwise_functions.html) +`__add__`, i.e., `+`) you can prefer to use the corresponding `elementwise +function +`__ instead, which would be wrapped. .. autofunction:: device From 8ae7ea4555e59a4770bface7dca4a6798b87a38a Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 14 Mar 2024 14:56:29 -0600 Subject: [PATCH 28/29] Split implementation notes into separate pages --- docs/dev/implementation-notes.md | 121 ----------------------------- docs/dev/index.md | 2 + docs/dev/special-considerations.md | 83 ++++++++++++++++++++ docs/dev/tests.md | 29 +++++++ 4 files changed, 114 insertions(+), 121 deletions(-) create mode 100644 docs/dev/special-considerations.md create mode 100644 docs/dev/tests.md diff --git a/docs/dev/implementation-notes.md b/docs/dev/implementation-notes.md index 707bd940..48fb01f1 100644 --- a/docs/dev/implementation-notes.md +++ b/docs/dev/implementation-notes.md @@ -1,96 +1,5 @@ # Implementation Notes -This page outlines some notes on the implementation of array-api-compat. These -details are not important for users of the package, but they may be useful to -contributors. - -## Special Considerations - -array-api-compat requires some special development considerations that are -different from most other Python libraries. The goal of array-api-compat is to -be a small library that packages can either vendor or add as a dependency to -implement array API support. Consequently, certain design considerations -should be taken into account: - -- *No Hard Dependencies.* Although array-api-compat "depends" on NumPy, CuPy, - PyTorch, etc., it does not hard depend on them. These libraries are not - imported unless either an array object is passed to - {func}`~.array_namespace()`, or the specific `array_api_compat.` - sub-namespace is explicitly imported. - -- *Vendorability.* array-api-compat should be [vendorable](vendoring). This - means that, for instance, all imports in the library are relative imports. - No code in the package specifically references the name `array_api_compat` - (we also support renaming the package to something else). - Vendorability support is tested in `tests/test_vendoring.py`. - -- *Pure Python.* To make array-api-compat as easy as possible to add as a - dependency, the code is all pure Python. - -- *Minimal Wrapping Only.* The wrapping functionality is minimal. This means - that if something is difficult to wrap using pure Python, or if trying to - support some array API behavior would require a significant amount of code, - we prefer to leave the behavior as an upstream issue for the array library, - and [document it as a known difference](../supported-array-libraries.md). - - This also means that we do not at this point in time implement anything - other than wrappers for functions in the standard, and basic [helper - functions](../helper-functions.rst) that would be useful for most users of - array-api-compat. The addition of functions that are not part of the array - API standard is currently out-of-scope for this package (see the - [Scope](scope) section of the documentation). - -- *No Side-Effects*. array-api-compat behavior should be localized to only the - specific code that imports and uses it. It should be invisible to end-users - or users of dependent codes. This in particular implies to the next two - points. - -- *No Monkey Patching.* `array-api-compat` should not attempt to modify - anything about the underlying library. It is a *wrapper* library only. - -- *No Modifying the Array Object.* The array (or tensor) object of the array - library cannot be modified. This also precludes the creation of array - subclasses or wrapper classes. - - Any non-standard behavior that is built-in to the array object, such as the - behavior of [array - methods](https://data-apis.org/array-api/latest/API_specification/array_object.html), - is therefore left unwrapped. Users can workaround issues by using - corresponding [elementwise - functions](https://data-apis.org/array-api/latest/API_specification/elementwise_functions.html) - instead of - [operators](https://data-apis.org/array-api/latest/API_specification/array_object.html#operators), - and by using the [helper functions](../helper-functions.rst) provided by - array-api-compat instead of attributes or methods like `x.to_device()`. - -- *Avoid Restricting Behavior that is Outside the Scope of the Standard.* All - array libraries have functions and behaviors that are outside of the scope - of what is specified by the standard. These behaviors should be left intact - whenever possible, unless the standard explicitly disallows something. This - means - - - All namespaces are *extended* with wrapper functions. You may notice the - extensive use of `import *` in various files in `array_api_compat`. While - this would normally be questionable, this is the [one actual legitimate - use-case for `import *`](https://peps.python.org/pep-0008/#imports), to - re-export names from an external namespace. - - - All wrapper functions pass `**kwargs` through to the wrapped function. - - - Input types not supported by the standard should work if they work in the - underlying wrapped function (for instance, Python scalars or `np.ndarray` - subclasses). - - By keeping underlying behaviors intact, it is easier for libraries to swap - out NumPy or other array libraries for array-api-compat, and it is easier - for libraries to write array library-specific code paths. - - The onus is on users of array-api-compat to ensure their array API code is - portable, e.g., by testing against [array-api-strict](array-api-strict). - - -## Avoiding Code Duplication - Since NumPy, CuPy, and to a degree, Dask, are nearly identical in behavior, most wrapping logic can be shared between them. Wrapped functions that have the same logic between multiple libraries are in `array_api_compat/common/`. @@ -138,33 +47,3 @@ identical PyTorch uses a similar layout in `array_api_compat/torch/`, but it differs enough from NumPy/CuPy that very few common wrappers for those libraries are reused. Dask is close to NumPy in behavior and so most Dask functions also reuse the NumPy/CuPy common wrappers. - -## Tests - -The majority of the behavior for array-api-compat is tested by the -[array-api-tests](https://github.com/data-apis/array-api-tests) test suite for -the array API standard. There are also array-api-compat specific tests in -[`tests/`](https://github.com/data-apis/array-api-compat/tree/main/tests). -These tests should be limited to things that are not tested by the test suite, -e.g., tests for [helper functions](../helper-functions.rst) or for behavior -that is not strictly required by the standard. - -array-api-tests is run against all supported libraries are tested on CI -([except for JAX](jax-support)). This is achieved by a [reusable GitHub Actions -Workflow](https://github.com/data-apis/array-api-compat/blob/main/.github/workflows/array-api-tests.yml). -Most libraries have tests that must be xfailed or skipped for various reasons. -These are defined in specific `-xfails.txt` files and are -automatically forwarded to array-api-tests. - -You may often need to update these xfail files, either to add new xfails -(e.g., because of new test suite features, or because a test that was -previously thought to be passing actually flaky fails). Try to keep the xfails -files organized, with comments pointing to upstream issues whenever possible. - -From time to time, xpass tests should be removed from the xfail files, but be -aware that many xfail tests are flaky, so an xpass should only be removed if -you know that the underlying issue has been fixed. - -Array libraries that require a GPU to run (currently only CuPy) cannot be -tested on CI. There is a helper script `test_cupy.sh` that can be used to -manually test CuPy on a machine with a CUDA GPU. diff --git a/docs/dev/index.md b/docs/dev/index.md index 59cd8551..c7eb6c08 100644 --- a/docs/dev/index.md +++ b/docs/dev/index.md @@ -6,6 +6,8 @@ It is recommended that contributors read through this documentation. ```{toctree} :titlesonly: +special-considerations.md implementation-notes.md +tests.md releasing.md ``` diff --git a/docs/dev/special-considerations.md b/docs/dev/special-considerations.md new file mode 100644 index 00000000..b57bdbcd --- /dev/null +++ b/docs/dev/special-considerations.md @@ -0,0 +1,83 @@ +# Special Considerations + +array-api-compat requires some special development considerations that are +different from most other Python libraries. The goal of array-api-compat is to +be a small library that packages can either vendor or add as a dependency to +implement array API support. Consequently, certain design considerations +should be taken into account: + +- *No Hard Dependencies.* Although array-api-compat "depends" on NumPy, CuPy, + PyTorch, etc., it does not hard depend on them. These libraries are not + imported unless either an array object is passed to + {func}`~.array_namespace()`, or the specific `array_api_compat.` + sub-namespace is explicitly imported. + +- *Vendorability.* array-api-compat should be [vendorable](vendoring). This + means that, for instance, all imports in the library are relative imports. + No code in the package specifically references the name `array_api_compat` + (we also support renaming the package to something else). + Vendorability support is tested in `tests/test_vendoring.py`. + +- *Pure Python.* To make array-api-compat as easy as possible to add as a + dependency, the code is all pure Python. + +- *Minimal Wrapping Only.* The wrapping functionality is minimal. This means + that if something is difficult to wrap using pure Python, or if trying to + support some array API behavior would require a significant amount of code, + we prefer to leave the behavior as an upstream issue for the array library, + and [document it as a known difference](../supported-array-libraries.md). + + This also means that we do not at this point in time implement anything + other than wrappers for functions in the standard, and basic [helper + functions](../helper-functions.rst) that would be useful for most users of + array-api-compat. The addition of functions that are not part of the array + API standard is currently out-of-scope for this package (see the + [Scope](scope) section of the documentation). + +- *No Side-Effects*. array-api-compat behavior should be localized to only the + specific code that imports and uses it. It should be invisible to end-users + or users of dependent codes. This in particular implies to the next two + points. + +- *No Monkey Patching.* `array-api-compat` should not attempt to modify + anything about the underlying library. It is a *wrapper* library only. + +- *No Modifying the Array Object.* The array (or tensor) object of the array + library cannot be modified. This also precludes the creation of array + subclasses or wrapper classes. + + Any non-standard behavior that is built-in to the array object, such as the + behavior of [array + methods](https://data-apis.org/array-api/latest/API_specification/array_object.html), + is therefore left unwrapped. Users can workaround issues by using + corresponding [elementwise + functions](https://data-apis.org/array-api/latest/API_specification/elementwise_functions.html) + instead of + [operators](https://data-apis.org/array-api/latest/API_specification/array_object.html#operators), + and by using the [helper functions](../helper-functions.rst) provided by + array-api-compat instead of attributes or methods like `x.to_device()`. + +- *Avoid Restricting Behavior that is Outside the Scope of the Standard.* All + array libraries have functions and behaviors that are outside of the scope + of what is specified by the standard. These behaviors should be left intact + whenever possible, unless the standard explicitly disallows something. This + means + + - All namespaces are *extended* with wrapper functions. You may notice the + extensive use of `import *` in various files in `array_api_compat`. While + this would normally be questionable, this is the [one actual legitimate + use-case for `import *`](https://peps.python.org/pep-0008/#imports), to + re-export names from an external namespace. + + - All wrapper functions pass `**kwargs` through to the wrapped function. + + - Input types not supported by the standard should work if they work in the + underlying wrapped function (for instance, Python scalars or `np.ndarray` + subclasses). + + By keeping underlying behaviors intact, it is easier for libraries to swap + out NumPy or other array libraries for array-api-compat, and it is easier + for libraries to write array library-specific code paths. + + The onus is on users of array-api-compat to ensure their array API code is + portable, e.g., by testing against [array-api-strict](array-api-strict). diff --git a/docs/dev/tests.md b/docs/dev/tests.md new file mode 100644 index 00000000..6a1ee2f7 --- /dev/null +++ b/docs/dev/tests.md @@ -0,0 +1,29 @@ +# Tests + +The majority of the behavior for array-api-compat is tested by the +[array-api-tests](https://github.com/data-apis/array-api-tests) test suite for +the array API standard. There are also array-api-compat specific tests in +[`tests/`](https://github.com/data-apis/array-api-compat/tree/main/tests). +These tests should be limited to things that are not tested by the test suite, +e.g., tests for [helper functions](../helper-functions.rst) or for behavior +that is not strictly required by the standard. + +array-api-tests is run against all supported libraries are tested on CI +([except for JAX](jax-support)). This is achieved by a [reusable GitHub Actions +Workflow](https://github.com/data-apis/array-api-compat/blob/main/.github/workflows/array-api-tests.yml). +Most libraries have tests that must be xfailed or skipped for various reasons. +These are defined in specific `-xfails.txt` files and are +automatically forwarded to array-api-tests. + +You may often need to update these xfail files, either to add new xfails +(e.g., because of new test suite features, or because a test that was +previously thought to be passing actually flaky fails). Try to keep the xfails +files organized, with comments pointing to upstream issues whenever possible. + +From time to time, xpass tests should be removed from the xfail files, but be +aware that many xfail tests are flaky, so an xpass should only be removed if +you know that the underlying issue has been fixed. + +Array libraries that require a GPU to run (currently only CuPy) cannot be +tested on CI. There is a helper script `test_cupy.sh` that can be used to +manually test CuPy on a machine with a CUDA GPU. From 3d38978950b28b25703db42b96725591a27f81ba Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 14 Mar 2024 15:02:26 -0600 Subject: [PATCH 29/29] Use bold instead of italics for the special considerations --- docs/dev/special-considerations.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/dev/special-considerations.md b/docs/dev/special-considerations.md index b57bdbcd..da868f31 100644 --- a/docs/dev/special-considerations.md +++ b/docs/dev/special-considerations.md @@ -6,22 +6,22 @@ be a small library that packages can either vendor or add as a dependency to implement array API support. Consequently, certain design considerations should be taken into account: -- *No Hard Dependencies.* Although array-api-compat "depends" on NumPy, CuPy, +- **No Hard Dependencies.** Although array-api-compat "depends" on NumPy, CuPy, PyTorch, etc., it does not hard depend on them. These libraries are not imported unless either an array object is passed to {func}`~.array_namespace()`, or the specific `array_api_compat.` sub-namespace is explicitly imported. -- *Vendorability.* array-api-compat should be [vendorable](vendoring). This +- **Vendorability.** array-api-compat should be [vendorable](vendoring). This means that, for instance, all imports in the library are relative imports. No code in the package specifically references the name `array_api_compat` (we also support renaming the package to something else). Vendorability support is tested in `tests/test_vendoring.py`. -- *Pure Python.* To make array-api-compat as easy as possible to add as a +- **Pure Python.** To make array-api-compat as easy as possible to add as a dependency, the code is all pure Python. -- *Minimal Wrapping Only.* The wrapping functionality is minimal. This means +- **Minimal Wrapping Only.** The wrapping functionality is minimal. This means that if something is difficult to wrap using pure Python, or if trying to support some array API behavior would require a significant amount of code, we prefer to leave the behavior as an upstream issue for the array library, @@ -34,15 +34,15 @@ should be taken into account: API standard is currently out-of-scope for this package (see the [Scope](scope) section of the documentation). -- *No Side-Effects*. array-api-compat behavior should be localized to only the +- **No Side-Effects**. array-api-compat behavior should be localized to only the specific code that imports and uses it. It should be invisible to end-users or users of dependent codes. This in particular implies to the next two points. -- *No Monkey Patching.* `array-api-compat` should not attempt to modify +- **No Monkey Patching.** `array-api-compat` should not attempt to modify anything about the underlying library. It is a *wrapper* library only. -- *No Modifying the Array Object.* The array (or tensor) object of the array +- **No Modifying the Array Object.** The array (or tensor) object of the array library cannot be modified. This also precludes the creation of array subclasses or wrapper classes. @@ -57,7 +57,7 @@ should be taken into account: and by using the [helper functions](../helper-functions.rst) provided by array-api-compat instead of attributes or methods like `x.to_device()`. -- *Avoid Restricting Behavior that is Outside the Scope of the Standard.* All +- **Avoid Restricting Behavior that is Outside the Scope of the Standard.** All array libraries have functions and behaviors that are outside of the scope of what is specified by the standard. These behaviors should be left intact whenever possible, unless the standard explicitly disallows something. This