Skip to content

Commit 397713f

Browse files
authored
Merge pull request #9 from asmeurer/docs-update
Update documentation
2 parents e8a9164 + c0f1534 commit 397713f

File tree

4 files changed

+230
-117
lines changed

4 files changed

+230
-117
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,5 @@ cython_debug/
158158
# and can be added to the global gitignore or merged into this file. For a more nuclear
159159
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
160160
#.idea/
161+
162+
.DS_Store

CHANGELOG.md

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# array-api-strict Changelog
2+
3+
## 1.0 (????)
4+
5+
This is the first release of `array_api_strict`. It is extracted from
6+
`numpy.array_api`, which was included as an experimental submodule in NumPy
7+
versions prior to 2.0. Note that the commit history in this repository is
8+
extracted from the git history of numpy/array_api/ (see the [README](README.md)).
9+
10+
Additionally, the following changes are new to `array_api_strict` from
11+
`numpy.array_api` in NumPy 1.26 (the last NumPy feature release to include
12+
`numpy.array_api`):
13+
14+
- ``array_api_strict`` was made more portable. In particular:
15+
16+
- ``array_api_strict`` no longer uses ``"cpu"`` as its "device", but rather a
17+
separate ``CPU_DEVICE`` object (which is not accessible in the namespace).
18+
This is because "cpu" is not part of the array API standard.
19+
20+
- ``array_api_strict`` now uses separate wrapped objects for dtypes.
21+
Previously it reused the ``numpy`` dtype objects. This makes it clear
22+
which behaviors on dtypes are part of the array API standard (effectively,
23+
the standard only requires ``==`` on dtype objects).
24+
25+
- ``numpy.array_api.nonzero`` now errors on zero-dimensional arrays, as
26+
required by the array API standard.
27+
28+
- Support for the optional [fft
29+
extension](https://data-apis.org/array-api/latest/extensions/fourier_transform_functions.html)
30+
was added.

README.md

+184-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,193 @@
1-
array-api-strict
2-
================
1+
# array-api-strict
32

4-
A strict, minimal implementation of the [Python array
3+
`array_api_strict` is a strict, minimal implementation of the [Python array
54
API](https://data-apis.org/array-api/latest/)
65

6+
The purpose of array-api-strict is to provide an implementation of the array
7+
API for consuming libraries to test against so they can be completely sure
8+
their usage of the array API is portable.
9+
10+
It is *not* intended to be used by end-users. End-users of the array API
11+
should just use their favorite array library (NumPy, CuPy, PyTorch, etc.) as
12+
usual. It is also not intended to be used as a dependency by consuming
13+
libraries. Consuming library code should use the
14+
[array-api-compat](https://github.com/data-apis/array-api-compat) package to
15+
support the array API. Rather, it is intended to be used in the test suites of
16+
consuming libraries to test their array API usage.
17+
18+
## Install
19+
20+
`array-api-strict` is available on both
21+
[PyPI](https://pypi.org/project/array-api-strict/)
22+
23+
```
24+
python -m pip install array-api-strict
25+
```
26+
27+
and [Conda-forge](https://anaconda.org/conda-forge/array-api-strict)
28+
29+
```
30+
conda install --channel conda-forge array-api-strict
31+
```
32+
33+
array-api-strict supports NumPy 1.26 and (the upcoming) NumPy 2.0.
34+
35+
## Rationale
36+
37+
The array API has many functions and behaviors that are required to be
38+
implemented by conforming libraries, but it does not, in most cases, disallow
39+
implementing additional functions, keyword arguments, and behaviors that
40+
aren't explicitly required by the standard.
41+
42+
However, this poses a problem for consumers of the array API, as they may
43+
accidentally use a function or rely on a behavior which just happens to be
44+
implemented in every array library they test against (e.g., NumPy and
45+
PyTorch), but isn't required by the standard and may not be included in other
46+
libraries.
47+
48+
array-api-strict solves this problem by providing a strict, minimal
49+
implementation of the array API standard. Only those functions and behaviors
50+
that are explicitly *required* by the standard are implemented. For example,
51+
most NumPy functions accept Python scalars as inputs:
52+
53+
```py
54+
>>> import numpy as np
55+
>>> np.sin(0.0)
56+
0.0
57+
```
58+
59+
However, the standard only specifies function inputs on `Array` objects. And
60+
indeed, some libraries, such as PyTorch, do not allow this:
61+
62+
```py
63+
>>> import torch
64+
>>> torch.sin(0.0)
65+
Traceback (most recent call last):
66+
File "<stdin>", line 1, in <module>
67+
TypeError: sin(): argument 'input' (position 1) must be Tensor, not float
68+
```
69+
70+
In array-api-strict, this is also an error:
71+
72+
```py
73+
>>> import array_api_strict as xp
74+
>>> xp.sin(0.0)
75+
Traceback (most recent call last):
76+
...
77+
AttributeError: 'float' object has no attribute 'dtype'
78+
```
79+
80+
Here is an (incomplete) list of the sorts of ways that array-api-strict is
81+
strict/minimal:
82+
83+
- Only those functions and methods that are [defined in the
84+
standard](https://data-apis.org/array-api/latest/API_specification/index.html)
85+
are included.
86+
87+
- In those functions, only the keyword-arguments that are defined by the
88+
standard are included. All signatures in array-api-strict use
89+
[positional-only
90+
arguments](https://data-apis.org/array-api/latest/API_specification/function_and_method_signatures.html#function-and-method-signatures).
91+
As noted above, only `array_api_strict` array objects are accepted by
92+
functions, except in the places where the standard allows Python scalars
93+
(i.e., functions do not automatically call `asarray` on their inputs).
94+
95+
- Only those [dtypes that are defined in the
96+
standard](https://data-apis.org/array-api/latest/API_specification/data_types.html)
97+
are included.
98+
99+
- All functions and methods reject inputs if the standard does not *require*
100+
the input dtype(s) to be supported. This is one of the most restrictive
101+
aspects of the library. For example, in NumPy, most transcendental functions
102+
like `sin` will accept integer array inputs, but the [standard only requires
103+
them to accept floating-point
104+
inputs](https://data-apis.org/array-api/latest/API_specification/generated/array_api.sin.html#array_api.sin),
105+
so in array-api-strict, `sin(integer_array)` will raise an exception.
106+
107+
- The
108+
[indexing](https://data-apis.org/array-api/latest/API_specification/indexing.html)
109+
semantics required by the standard are limited compared to those implemented
110+
by NumPy (e.g., out-of-bounds slices are not supported, integer array
111+
indexing is not supported, only a single boolean array index is supported).
112+
113+
- There are no distinct "scalar" objects as in NumPy. There are only 0-D
114+
arrays.
115+
116+
- Dtype objects are just empty objects that only implement [equality
117+
comparison](https://data-apis.org/array-api/latest/API_specification/generated/array_api.data_types.__eq__.html).
118+
The way to access dtype objects in the standard is by name, like
119+
`xp.float32`.
120+
121+
- The array object type itself is private and should not be accessed.
122+
Subclassing or otherwise trying to directly initialize this object is not
123+
supported. Arrays should be created with one of the [array creation
124+
functions](https://data-apis.org/array-api/latest/API_specification/creation_functions.html)
125+
such as `asarray`.
126+
127+
## Caveats
128+
129+
array-api-strict is a thin pure Python wrapper around NumPy. NumPy 2.0 fully
130+
supports the array API but NumPy 1.26 does not, so many behaviors are wrapped
131+
in NumPy 1.26 to provide array API compatible behavior. Although it is based
132+
on NumPy, mixing NumPy arrays with array-api-strict arrays is not supported.
133+
This should generally raise an error, as it indicates a potential portability
134+
issue, but this hasn't necessarily been tested thoroughly.
135+
136+
1. array-api-strict is validated against the [array API test
137+
suite](https://github.com/data-apis/array-api-tests). However, there may be
138+
a few minor instances where NumPy deviates from the standard in a way that
139+
is inconvenient to workaround in array-api-strict, since it aims to remain
140+
pure Python. You can see the full list of tests that are known to fail in
141+
the [xfails
142+
file](https://github.com/data-apis/array-api-strict/blob/main/array-api-tests-xfails.txt).
143+
144+
The most notable of these is that in NumPy 1.26, the `copy=False` flag is
145+
not implemented for `asarray` and therefore `array_api_strict` raises
146+
`NotImplementedError` in that case.
147+
148+
2. Since NumPy is a CPU-only library, the [device
149+
support](https://data-apis.org/array-api/latest/design_topics/device_support.html)
150+
in array-api-strict is superficial only. `x.device` is always a (private)
151+
`CPU_DEVICE` object, and `device` keywords to creation functions only
152+
accept either this object or `None`. A future version of array-api-strict
153+
[may add support for a CuPy
154+
backend](https://github.com/data-apis/array-api-strict/issues/5) so that
155+
more significant device support can be tested.
156+
157+
3. Although only array types are expected in array-api-strict functions,
158+
currently most functions do not do extensive type checking on their inputs,
159+
so a sufficiently duck-typed object may pass through silently (or at best,
160+
you may get `AttributeError` instead of `TypeError`). However, all type
161+
signatures have type annotations (based on those from the standard), so
162+
this deviation may be tested with type checking. This [behavior may improve
163+
in the future](https://github.com/data-apis/array-api-strict/issues/6).
164+
165+
4. There are some behaviors in the standard that are not required to be
166+
implemented by libraries that cannot support [data dependent
167+
shapes](https://data-apis.org/array-api/latest/design_topics/data_dependent_output_shapes.html).
168+
This includes [the `unique_*`
169+
functions](https://data-apis.org/array-api/latest/API_specification/set_functions.html),
170+
[boolean array
171+
indexing](https://data-apis.org/array-api/latest/API_specification/indexing.html#boolean-array-indexing),
172+
and the
173+
[`nonzero`](https://data-apis.org/array-api/latest/API_specification/generated/array_api.nonzero.html)
174+
function. array-api-strict currently implements all of these. In the
175+
future, [there may be a way to disable them](https://github.com/data-apis/array-api-strict/issues/7).
176+
177+
5. array-api-strict currently only supports the latest version of the array
178+
API standard. [This may change in the future depending on
179+
need](https://github.com/data-apis/array-api-strict/issues/8).
180+
181+
## Usage
182+
183+
TODO: Add a sample CI script here.
184+
185+
## Relationship to `numpy.array_api`
186+
7187
Previously this implementation was available as `numpy.array_api`, but it was
8188
moved to a separate package for NumPy 2.0.
9189

10-
Note: the history of this repo prior to commit
190+
Note that the history of this repo prior to commit
11191
fbefd42e4d11e9be20e0a4785f2619fc1aef1e7c was generated automatically
12192
from the numpy git history, using the following
13193
[git-filter-repo](https://github.com/newren/git-filter-repo) command:

array_api_strict/__init__.py

+14-113
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,18 @@
11
"""
2-
A NumPy sub-namespace that conforms to the Python array API standard.
3-
4-
This submodule accompanies NEP 47, which proposes its inclusion in NumPy. It
5-
is still considered experimental, and will issue a warning when imported.
6-
7-
This is a proof-of-concept namespace that wraps the corresponding NumPy
8-
functions to give a conforming implementation of the Python array API standard
9-
(https://data-apis.github.io/array-api/latest/). The standard is currently in
10-
an RFC phase and comments on it are both welcome and encouraged. Comments
11-
should be made either at https://github.com/data-apis/array-api or at
12-
https://github.com/data-apis/consortium-feedback/discussions.
13-
14-
NumPy already follows the proposed spec for the most part, so this module
15-
serves mostly as a thin wrapper around it. However, NumPy also implements a
16-
lot of behavior that is not included in the spec, so this serves as a
17-
restricted subset of the API. Only those functions that are part of the spec
18-
are included in this namespace, and all functions are given with the exact
19-
signature given in the spec, including the use of position-only arguments, and
20-
omitting any extra keyword arguments implemented by NumPy but not part of the
21-
spec. The behavior of some functions is also modified from the NumPy behavior
22-
to conform to the standard. Note that the underlying array object itself is
23-
wrapped in a wrapper Array() class, but is otherwise unchanged. This submodule
24-
is implemented in pure Python with no C extensions.
25-
26-
The array API spec is designed as a "minimal API subset" and explicitly allows
27-
libraries to include behaviors not specified by it. But users of this module
28-
that intend to write portable code should be aware that only those behaviors
29-
that are listed in the spec are guaranteed to be implemented across libraries.
30-
Consequently, the NumPy implementation was chosen to be both conforming and
31-
minimal, so that users can use this implementation of the array API namespace
32-
and be sure that behaviors that it defines will be available in conforming
33-
namespaces from other libraries.
34-
35-
A few notes about the current state of this submodule:
36-
37-
- There is a test suite that tests modules against the array API standard at
38-
https://github.com/data-apis/array-api-tests. The test suite is still a work
39-
in progress, but the existing tests pass on this module, with a few
40-
exceptions:
41-
42-
- DLPack support (see https://github.com/data-apis/array-api/pull/106) is
43-
not included here, as it requires a full implementation in NumPy proper
44-
first.
45-
46-
The test suite is not yet complete, and even the tests that exist are not
47-
guaranteed to give a comprehensive coverage of the spec. Therefore, when
48-
reviewing and using this submodule, you should refer to the standard
49-
documents themselves. There are some tests in array_api_strict.tests, but
50-
they primarily focus on things that are not tested by the official array API
51-
test suite.
52-
53-
- There is a custom array object, array_api_strict.Array, which is returned by
54-
all functions in this module. All functions in the array API namespace
55-
implicitly assume that they will only receive this object as input. The only
56-
way to create instances of this object is to use one of the array creation
57-
functions. It does not have a public constructor on the object itself. The
58-
object is a small wrapper class around numpy.ndarray. The main purpose of it
59-
is to restrict the namespace of the array object to only those dtypes and
60-
only those methods that are required by the spec, as well as to limit/change
61-
certain behavior that differs in the spec. In particular:
62-
63-
- The array API namespace does not have scalar objects, only 0-D arrays.
64-
Operations on Array that would create a scalar in NumPy create a 0-D
65-
array.
66-
67-
- Indexing: Only a subset of indices supported by NumPy are required by the
68-
spec. The Array object restricts indexing to only allow those types of
69-
indices that are required by the spec. See the docstring of the
70-
array_api_strict.Array._validate_indices helper function for more
71-
information.
72-
73-
- Type promotion: Some type promotion rules are different in the spec. In
74-
particular, the spec does not have any value-based casting. The spec also
75-
does not require cross-kind casting, like integer -> floating-point. Only
76-
those promotions that are explicitly required by the array API
77-
specification are allowed in this module. See NEP 47 for more info.
78-
79-
- Functions do not automatically call asarray() on their input, and will not
80-
work if the input type is not Array. The exception is array creation
81-
functions, and Python operators on the Array object, which accept Python
82-
scalars of the same type as the array dtype.
83-
84-
- All functions include type annotations, corresponding to those given in the
85-
spec (see _typing.py for definitions of some custom types). These do not
86-
currently fully pass mypy due to some limitations in mypy.
87-
88-
- Dtype objects are just the NumPy dtype objects, e.g., float64 =
89-
np.dtype('float64'). The spec does not require any behavior on these dtype
90-
objects other than that they be accessible by name and be comparable by
91-
equality, but it was considered too much extra complexity to create custom
92-
objects to represent dtypes.
93-
94-
- All places where the implementations in this submodule are known to deviate
95-
from their corresponding functions in NumPy are marked with "# Note:"
96-
comments.
97-
98-
Still TODO in this module are:
99-
100-
- DLPack support for numpy.ndarray is still in progress. See
101-
https://github.com/numpy/numpy/pull/19083.
102-
103-
- The copy=False keyword argument to asarray() is not yet implemented. This
104-
requires support in numpy.asarray() first.
105-
106-
- Some functions are not yet fully tested in the array API test suite, and may
107-
require updates that are not yet known until the tests are written.
108-
109-
- The spec is still in an RFC phase and may still have minor updates, which
110-
will need to be reflected here.
111-
112-
- Complex number support in array API spec is planned but not yet finalized,
113-
as are the fft extension and certain linear algebra functions such as eig
114-
that require complex dtypes.
2+
array_api_strict is a strict, minimal implementation of the Python array
3+
API (https://data-apis.org/array-api/latest/)
4+
5+
The purpose of array-api-strict is to provide an implementation of the array
6+
API for consuming libraries to test against so they can be completely sure
7+
their usage of the array API is portable.
8+
9+
It is *not* intended to be used by end-users. End-users of the array API
10+
should just use their favorite array library (NumPy, CuPy, PyTorch, etc.) as
11+
usual. It is also not intended to be used as a dependency by consuming
12+
libraries. Consuming library code should use the
13+
array-api-compat (https://github.com/data-apis/array-api-compat) package to
14+
support the array API. Rather, it is intended to be used in the test suites of
15+
consuming libraries to test their array API usage.
11516
11617
"""
11718

0 commit comments

Comments
 (0)