Skip to content

Commit cc11aa3

Browse files
authored
Add Array API inspection utilities (#689)
1 parent 71b01c1 commit cc11aa3

File tree

7 files changed

+290
-14
lines changed

7 files changed

+290
-14
lines changed

spec/draft/API_specification/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ A conforming implementation of the array API standard must provide and support t
2929
function_and_method_signatures
3030
indexing
3131
indexing_functions
32+
inspection
3233
linear_algebra_functions
3334
manipulation_functions
3435
searching_functions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
Inspection
2+
==========
3+
4+
Array API specification for namespace inspection utilities.
5+
6+
A conforming implementation of the array API standard must provide and support the following functions and associated inspection APIs.
7+
8+
9+
Objects in API
10+
--------------
11+
12+
.. currentmodule:: array_api.info
13+
14+
..
15+
NOTE: please keep the functions in alphabetical order
16+
17+
.. autosummary::
18+
:toctree: generated
19+
:template: method.rst
20+
21+
__array_namespace_info__
22+
23+
24+
Inspection APIs
25+
---------------
26+
27+
In the namespace (or class) returned by ``__array_namespace_info__``, a conforming implementation of the array API standard must provide and support the following functions (or methods) for programmatically querying data type and device support, capabilities, and other specification-defined implementation-specific behavior, as documented in the functions described below.
28+
29+
..
30+
NOTE: please keep the functions in alphabetical order
31+
32+
.. autosummary::
33+
:toctree: generated
34+
:template: method.rst
35+
36+
capabilities
37+
default_device
38+
default_dtypes
39+
devices
40+
dtypes

spec/draft/design_topics/device_support.rst

+15-14
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ into library code which may have to do the following:
3939
Syntax for device assignment
4040
----------------------------
4141

42-
The array API will offer the following syntax for device assignment and
42+
The array API provides the following syntax for device assignment and
4343
cross-device data transfer:
4444

4545
1. A ``.device`` property on the array object, which returns a ``Device`` object
@@ -52,19 +52,20 @@ cross-device data transfer:
5252
3. A ``.to_device`` method on the array object to copy an array to a different device.
5353

5454
.. note::
55-
In the current API standard, the only way to obtain a ``Device`` object is from the
56-
``.device`` property on the array object. The standard does **not** include a universal
57-
``Device`` object recognized by all compliant libraries. Accordingly, the standard does
58-
not provide a means of instantiating a ``Device`` object to point to a specific physical or
59-
logical device.
60-
61-
The choice to not include a standardized ``Device`` object may be revisited in a future revision of this standard.
62-
63-
For array libraries which concern themselves with multi-device support, including CPU and GPU,
64-
they are free to expose a library-specific device object (e.g., for creating an
65-
array on a particular device). While a library-specific device object can be used as input to
66-
``to_device``, beware that this will mean non-portability as code will be specific to
67-
that library.
55+
The current API standard does **not** include a universal ``Device`` object
56+
recognized by all compliant libraries. Accordingly, the standard does not
57+
provide a means of instantiating a ``Device`` object to point to a specific
58+
physical or logical device.
59+
60+
The choice to not include a standardized ``Device`` object may be revisited
61+
in a future revision of this standard.
62+
63+
For array libraries which concern themselves with multi-device support,
64+
including CPU and GPU, they are free to expose a library-specific device
65+
object (e.g., for creating an array on a particular device). While a
66+
library-specific device object can be used as input to ``to_device``, beware
67+
that this will mean non-portability as code will be specific to that
68+
library.
6869

6970
Semantics
7071
---------

src/_array_api_conf.py

+4
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@
7272
("py:class", ".*PyCapsule"),
7373
("py:class", ".*finfo_object"),
7474
("py:class", ".*iinfo_object"),
75+
("py:class", ".*Info"),
76+
("py:class", ".*Capabilities"),
77+
("py:class", ".*DefaultDataTypes"),
78+
("py:class", ".*DataTypes"),
7579
]
7680
# In array_object.py we have to use aliased names for some types because they
7781
# would otherwise refer back to method objects of array

src/array_api_stubs/_draft/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from .utility_functions import *
1717
from . import linalg
1818
from . import fft
19+
from . import info
1920

2021

2122
__array_api_version__: str = "YYYY.MM"

src/array_api_stubs/_draft/_types.py

+59
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525
"finfo_object",
2626
"iinfo_object",
2727
"Enum",
28+
"DefaultDataTypes",
29+
"DataTypes",
30+
"Capabilities",
31+
"Info",
2832
]
2933

3034
from dataclasses import dataclass
@@ -35,6 +39,7 @@
3539
Optional,
3640
Sequence,
3741
Tuple,
42+
TypedDict,
3843
TypeVar,
3944
Union,
4045
Protocol,
@@ -83,3 +88,57 @@ def __getitem__(self, key: int, /) -> Union[_T_co, NestedSequence[_T_co]]:
8388

8489
def __len__(self, /) -> int:
8590
...
91+
92+
93+
class Info(Protocol):
94+
"""Namespace returned by `__array_namespace_info__`."""
95+
96+
def capabilities(self) -> Capabilities:
97+
...
98+
99+
def default_device(self) -> device:
100+
...
101+
102+
def default_dtypes(self, *, device: Optional[device]) -> DefaultDataTypes:
103+
...
104+
105+
def devices(self) -> List[device]:
106+
...
107+
108+
def dtypes(
109+
self, *, device: Optional[device], kind: Optional[Union[str, Tuple[str, ...]]]
110+
) -> DataTypes:
111+
...
112+
113+
114+
DefaultDataTypes = TypedDict(
115+
"DefaultDataTypes",
116+
{
117+
"real floating": dtype,
118+
"complex floating": dtype,
119+
"integral": dtype,
120+
"indexing": dtype,
121+
},
122+
)
123+
DataTypes = TypedDict(
124+
"DataTypes",
125+
{
126+
"bool": dtype,
127+
"float32": dtype,
128+
"float64": dtype,
129+
"complex64": dtype,
130+
"complex128": dtype,
131+
"int8": dtype,
132+
"int16": dtype,
133+
"int32": dtype,
134+
"int64": dtype,
135+
"uint8": dtype,
136+
"uint16": dtype,
137+
"uint32": dtype,
138+
"uint64": dtype,
139+
},
140+
total=False,
141+
)
142+
Capabilities = TypedDict(
143+
"Capabilities", {"boolean indexing": bool, "data-dependent shapes": bool}
144+
)

src/array_api_stubs/_draft/info.py

+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
__all__ = [
2+
"__array_namespace_info__",
3+
"capabilities",
4+
"default_device",
5+
"default_dtypes",
6+
"devices",
7+
"dtypes",
8+
]
9+
10+
from ._types import (
11+
Optional,
12+
Union,
13+
Tuple,
14+
List,
15+
device,
16+
dtype,
17+
DefaultDataTypes,
18+
DataTypes,
19+
Capabilities,
20+
Info,
21+
)
22+
23+
24+
def __array_namespace_info__() -> Info:
25+
"""
26+
Returns a namespace with Array API namespace inspection utilities.
27+
28+
Returns
29+
-------
30+
out: Info
31+
An object containing Array API namespace inspection utilities.
32+
33+
Notes
34+
-----
35+
36+
The returned object may be either a namespace or a class, so long as an Array API user can access inspection utilities as follows:
37+
38+
::
39+
40+
info = xp.__array_namespace_info__()
41+
info.capabilities()
42+
info.devices()
43+
info.dtypes()
44+
info.default_dtypes()
45+
# ...
46+
"""
47+
48+
49+
def capabilities() -> Capabilities:
50+
"""
51+
Returns a dictionary of array library capabilities.
52+
53+
The dictionary must contain the following keys:
54+
55+
- `"boolean indexing"`: boolean indicating whether an array library supports boolean indexing. If a conforming implementation fully supports boolean indexing in compliance with this specification (see :ref:`indexing`), the corresponding dictionary value must be ``True``; otherwise, the value must be ``False``.
56+
- `"data-dependent shapes"`: boolean indicating whether an array library supports data-dependent output shapes. If a conforming implementation fully supports all APIs included in this specification (excluding boolean indexing) which have data-dependent output shapes, as explicitly demarcated throughout the specification, the corresponding dictionary value must be ``True``; otherwise, the value must be ``False``.
57+
58+
Returns
59+
-------
60+
out: Capabilities
61+
a dictionary of array library capabilities.
62+
"""
63+
64+
65+
def default_device() -> device:
66+
"""
67+
Returns the default device.
68+
69+
Returns
70+
-------
71+
out: device
72+
an object corresponding to the default device.
73+
"""
74+
75+
76+
def default_dtypes(
77+
*,
78+
device: Optional[device] = None,
79+
) -> DefaultDataTypes:
80+
"""
81+
Returns a dictionary containing default data types.
82+
83+
The dictionary must have the following keys:
84+
85+
- `"real floating"`: default real floating-point data type.
86+
- `"complex floating"`: default complex floating-point data type.
87+
- `"integral"`: default integral data type.
88+
- `"indexing"`: default array index data type.
89+
90+
Dictionary values must be the corresponding data type object.
91+
92+
Parameters
93+
----------
94+
device: Optional[device]
95+
device for which to return default data types. If ``device`` is ``None``, the returned data types must be the default data types for the current device; otherwise, the returned data types must be default data types specific to the specified device. Default: ``None``.
96+
97+
.. note::
98+
Some array libraries have the concept of a device context manager, allowing library consumers to manage the current device context. When ``device`` is ``None``, libraries supporting a device context should return the default data types for the current device. For libraries without a context manager or supporting only a single device, those libraries should return the default data types for the default device.
99+
100+
Returns
101+
-------
102+
out: DefaultDataTypes
103+
a dictionary containing the default data type for respective data type kinds.
104+
"""
105+
106+
107+
def dtypes(
108+
*,
109+
device: Optional[device] = None,
110+
kind: Optional[Union[str, Tuple[str, ...]]] = None,
111+
) -> DataTypes:
112+
"""
113+
Returns a dictionary of supported *Array API* data types.
114+
115+
.. note::
116+
While specification-conforming array libraries may support additional data types which are not present in this specification, data types which are not present in this specification should not be included in the returned dictionary.
117+
118+
.. note::
119+
Specification-conforming array libraries must only return supported data types having expected properties as described in :ref:`data-types`. For example, if a library decides to alias ``float32`` as ``float64``, that library must not include ``float64`` in the dictionary of supported data types.
120+
121+
Parameters
122+
----------
123+
kind: Optional[Union[str, Tuple[str, ...]]]
124+
data type kind.
125+
126+
- If ``kind`` is ``None``, the function must return a dictionary containing all supported Array API data types.
127+
128+
- If ``kind`` is a string, the function must return a dictionary containing the data types belonging to the specified data type kind. The following data type kinds must be supported:
129+
130+
- ``'bool'``: boolean data types (e.g., ``bool``).
131+
- ``'signed integer'``: signed integer data types (e.g., ``int8``, ``int16``, ``int32``, ``int64``).
132+
- ``'unsigned integer'``: unsigned integer data types (e.g., ``uint8``, ``uint16``, ``uint32``, ``uint64``).
133+
- ``'integral'``: integer data types. Shorthand for ``('signed integer', 'unsigned integer')``.
134+
- ``'real floating'``: real-valued floating-point data types (e.g., ``float32``, ``float64``).
135+
- ``'complex floating'``: complex floating-point data types (e.g., ``complex64``, ``complex128``).
136+
- ``'numeric'``: numeric data types. Shorthand for ``('integral', 'real floating', 'complex floating')``.
137+
138+
- If ``kind`` is a tuple, the tuple specifies a union of data type kinds, and the function must return a dictionary containing the data types belonging to at least one of the specified data type kinds.
139+
140+
Default: ``None``.
141+
device: Optional[device]
142+
device for which to return supported data types. If ``device`` is ``None``, the returned data types must be the supported data types for the current device; otherwise, the returned data types must be supported data types specific to the specified device. Default: ``None``.
143+
144+
.. note::
145+
Some array libraries have the concept of a device context manager, allowing library consumers to manage the current device context. When ``device`` is ``None``, libraries supporting a device context should return the supported data types for the current device. For libraries without a context manager or supporting only a single device, those libraries should return the supported data types for the default device.
146+
147+
Returns
148+
-------
149+
out: DataTypes
150+
a dictionary containing supported data types.
151+
152+
.. note::
153+
Dictionary keys must only consist of canonical names as defined in :ref:`data-types`.
154+
"""
155+
156+
157+
def devices() -> List[device]:
158+
"""
159+
Returns a list of supported devices which are available at runtime.
160+
161+
Returns
162+
-------
163+
out: List[device]
164+
a list of supported devices.
165+
166+
Notes
167+
-----
168+
169+
Each device object (see :ref:`device-support`) in the list of returned devices must be an object which can be provided as a valid keyword-argument to array creation functions.
170+
"""

0 commit comments

Comments
 (0)