From 5c2423a96c9fd5113e405df88f2422f14cd978b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Sok=C3=B3=C5=82?= <8431159+mtsokol@users.noreply.github.com> Date: Mon, 18 Dec 2023 21:27:33 +0100 Subject: [PATCH 01/57] Fix definition of complex inner product in `vecdot` (#723) PR-URL: https://github.com/data-apis/array-api/pull/723 Reviewed-by: Athan Reines --- src/array_api_stubs/_2022_12/linear_algebra_functions.py | 4 ++-- src/array_api_stubs/_draft/linear_algebra_functions.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/array_api_stubs/_2022_12/linear_algebra_functions.py b/src/array_api_stubs/_2022_12/linear_algebra_functions.py index 110a19ce1..d5329db76 100644 --- a/src/array_api_stubs/_2022_12/linear_algebra_functions.py +++ b/src/array_api_stubs/_2022_12/linear_algebra_functions.py @@ -122,9 +122,9 @@ def vecdot(x1: array, x2: array, /, *, axis: int = -1) -> array: Let :math:`\mathbf{a}` be a vector in ``x1`` and :math:`\mathbf{b}` be a corresponding vector in ``x2``. The dot product is defined as .. math:: - \mathbf{a} \cdot \mathbf{b} = \sum_{i=0}^{n-1} a_i\overline{b_i} + \mathbf{a} \cdot \mathbf{b} = \sum_{i=0}^{n-1} \overline{a_i}b_i - over the dimension specified by ``axis`` and where :math:`n` is the dimension size and :math:`\overline{b_i}` denotes the complex conjugate if :math:`b_i` is complex and the identity if :math:`b_i` is real-valued. + over the dimension specified by ``axis`` and where :math:`n` is the dimension size and :math:`\overline{a_i}` denotes the complex conjugate if :math:`a_i` is complex and the identity if :math:`a_i` is real-valued. Parameters ---------- diff --git a/src/array_api_stubs/_draft/linear_algebra_functions.py b/src/array_api_stubs/_draft/linear_algebra_functions.py index 079a90ee6..96f082bd5 100644 --- a/src/array_api_stubs/_draft/linear_algebra_functions.py +++ b/src/array_api_stubs/_draft/linear_algebra_functions.py @@ -126,9 +126,9 @@ def vecdot(x1: array, x2: array, /, *, axis: int = -1) -> array: Let :math:`\mathbf{a}` be a vector in ``x1`` and :math:`\mathbf{b}` be a corresponding vector in ``x2``. The dot product is defined as .. math:: - \mathbf{a} \cdot \mathbf{b} = \sum_{i=0}^{n-1} a_i\overline{b_i} + \mathbf{a} \cdot \mathbf{b} = \sum_{i=0}^{n-1} \overline{a_i}b_i - over the dimension specified by ``axis`` and where :math:`n` is the dimension size and :math:`\overline{b_i}` denotes the complex conjugate if :math:`b_i` is complex and the identity if :math:`b_i` is real-valued. + over the dimension specified by ``axis`` and where :math:`n` is the dimension size and :math:`\overline{a_i}` denotes the complex conjugate if :math:`a_i` is complex and the identity if :math:`a_i` is real-valued. Parameters ---------- From 72f86f37e51cd28183c6b25c6b0b6d81ff497856 Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 11 Jan 2024 13:47:26 -0800 Subject: [PATCH 02/57] Add `searchsorted` to the specification (#699) --- .../API_specification/searching_functions.rst | 1 + .../_draft/searching_functions.py | 54 ++++++++++++++++++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/spec/draft/API_specification/searching_functions.rst b/spec/draft/API_specification/searching_functions.rst index 01ab4e82a..c952f1aad 100644 --- a/spec/draft/API_specification/searching_functions.rst +++ b/spec/draft/API_specification/searching_functions.rst @@ -23,4 +23,5 @@ Objects in API argmax argmin nonzero + searchsorted where diff --git a/src/array_api_stubs/_draft/searching_functions.py b/src/array_api_stubs/_draft/searching_functions.py index e586a7656..dda000e74 100644 --- a/src/array_api_stubs/_draft/searching_functions.py +++ b/src/array_api_stubs/_draft/searching_functions.py @@ -1,7 +1,7 @@ -__all__ = ["argmax", "argmin", "nonzero", "where"] +__all__ = ["argmax", "argmin", "nonzero", "searchsorted", "where"] -from ._types import Optional, Tuple, array +from ._types import Optional, Tuple, Literal, array def argmax(x: array, /, *, axis: Optional[int] = None, keepdims: bool = False) -> array: @@ -87,6 +87,56 @@ def nonzero(x: array, /) -> Tuple[array, ...]: """ +def searchsorted( + x1: array, + x2: array, + /, + *, + side: Literal["left", "right"] = "left", + sorter: Optional[array] = None, +) -> array: + """ + Finds the indices into ``x1`` such that, if the corresponding elements in ``x2`` were inserted before the indices, the order of ``x1``, when sorted in ascending order, would be preserved. + + Parameters + ---------- + x1: array + input array. Must be a one-dimensional array. Should have a real-valued data type. If ``sorter`` is ``None``, must be sorted in ascending order; otherwise, ``sorter`` must be an array of indices that sort ``x1`` in ascending order. + x2: array + array containing search values. Should have a real-valued data type. + side: Literal['left', 'right'] + argument controlling which index is returned if a value lands exactly on an edge. + + Let ``x`` be an array of rank ``N`` where ``v`` is an individual element given by ``v = x2[n,m,...,j]``. + + If ``side == 'left'``, then + + - each returned index ``i`` must satisfy the index condition ``x1[i-1] < v <= x1[i]``. + - if no index satisfies the index condition, then the returned index for that element must be ``0``. + + Otherwise, if ``side == 'right'``, then + + - each returned index ``i`` must satisfy the index condition ``x1[i-1] <= v < x1[i]``. + - if no index satisfies the index condition, then the returned index for that element must be ``N``, where ``N`` is the number of elements in ``x1``. + + Default: ``'left'``. + sorter: Optional[array] + array of indices that sort ``x1`` in ascending order. The array must have the same shape as ``x1`` and have an integer data type. Default: ``None``. + + Returns + ------- + out: array + an array of indices with the same shape as ``x2``. The returned array must have the default array index data type. + + Notes + ----- + + For real-valued floating-point arrays, the sort order of NaNs and signed zeros is unspecified and thus implementation-dependent. Accordingly, when a real-valued floating-point array contains NaNs and signed zeros, what constitutes ascending order may vary among specification-conforming array libraries. + + While behavior for arrays containing NaNs and signed zeros is implementation-dependent, specification-conforming libraries should, however, ensure consistency with ``sort`` and ``argsort`` (i.e., if a value in ``x2`` is inserted into ``x1`` according to the corresponding index in the output array and ``sort`` is invoked on the resultant array, the sorted result should be an array in the same order). + """ + + def where(condition: array, x1: array, x2: array, /) -> array: """ Returns elements chosen from ``x1`` or ``x2`` depending on ``condition``. From 8fe8b0864ce0dd48cca1006accb684c34513783d Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Thu, 11 Jan 2024 23:26:14 +0100 Subject: [PATCH 03/57] Fix sidebar contents under Extensions (#729) The problem was that the sidebar doesn't show the named pages in the toctree if that toctree is under a subheading in index.rst Closes gh-716 --- spec/draft/extensions/index.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/draft/extensions/index.rst b/spec/draft/extensions/index.rst index 30d9cfa90..3b9409954 100644 --- a/spec/draft/extensions/index.rst +++ b/spec/draft/extensions/index.rst @@ -24,11 +24,10 @@ the implementer, e.g. via a regular submodule that is imported under the The functions in an extension must adhere to the same conventions as those in the array API standard. See :ref:`api-specification`. - -Extensions ----------- +------------------------------------------------------------------------------ .. toctree:: + :caption: Extension modules: :maxdepth: 1 fourier_transform_functions From 7a56675105864d98cf43b65656f6c2edf93bd2d4 Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Fri, 12 Jan 2024 10:41:22 +0100 Subject: [PATCH 04/57] Update DLPack content to address unsupported use cases (#709) Also extend the description of errors when DLPack cannot be supported --- spec/draft/design_topics/data_interchange.rst | 19 +++++++++++++++++++ .../_draft/creation_functions.py | 14 ++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/spec/draft/design_topics/data_interchange.rst b/spec/draft/design_topics/data_interchange.rst index 8686042c8..3b3040672 100644 --- a/spec/draft/design_topics/data_interchange.rst +++ b/spec/draft/design_topics/data_interchange.rst @@ -84,3 +84,22 @@ page gives a high-level specification for data exchange in Python using DLPack. are recommended to do so using the same syntax and semantics as outlined below. They are not required to return an array object from ``from_dlpack`` which conforms to this standard. + +Non-supported use cases +----------------------- + +Use of DLPack requires that the data can be represented by a strided, in-memory +layout on a single device. This covers usage by a large range of, but not all, +known and possible array libraries. Use cases that are not supported by DLPack +include: + +- Distributed arrays, i.e., the data residing on multiple nodes or devices, +- Sparse arrays, i.e., sparse representations where a data value (typically + zero) is implicit. + +There may be other reasons why it is not possible or desirable for an +implementation to materialize the array as strided data in memory. In such +cases, the implementation may raise a `BufferError` in the `__dlpack__` or +`__dlpack_device__` method. In case an implementation is never able to export +its array data via DLPack, it may omit `__dlpack__` and `__dlpack_device__` +completely, and hence `from_dlpack` may raise an `AttributeError`. diff --git a/src/array_api_stubs/_draft/creation_functions.py b/src/array_api_stubs/_draft/creation_functions.py index 5e2bb44e6..69733a646 100644 --- a/src/array_api_stubs/_draft/creation_functions.py +++ b/src/array_api_stubs/_draft/creation_functions.py @@ -232,6 +232,20 @@ def from_dlpack(x: object, /) -> array: :class: note The returned array may be either a copy or a view. See :ref:`data-interchange` for details. + + Raises + ------ + BufferError + The ``__dlpack__`` and ``__dlpack_device__`` methods on the input array + may raise ``BufferError`` when the data cannot be exported as DLPack + (e.g., incompatible dtype or strides). It may also raise other errors + when export fails for other reasons (e.g., not enough memory available + to materialize the data). ``from_dlpack`` must propagate such + exceptions. + AttributeError + If the ``__dlpack__`` and ``__dlpack_device__`` methods are not present + on the input array. This may happen for libraries that are never able + to export their data with DLPack. """ From 6e320d01b60dc38e7119ed3e9d409a00818bbe0b Mon Sep 17 00:00:00 2001 From: Athan Date: Mon, 22 Jan 2024 12:07:38 -0800 Subject: [PATCH 05/57] Add `clip` to the specification PR-URL: https://github.com/data-apis/array-api/pull/715 --- .../elementwise_functions.rst | 1 + .../_draft/elementwise_functions.py | 35 ++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/spec/draft/API_specification/elementwise_functions.rst b/spec/draft/API_specification/elementwise_functions.rst index 7984d8e02..f0b69cfcb 100644 --- a/spec/draft/API_specification/elementwise_functions.rst +++ b/spec/draft/API_specification/elementwise_functions.rst @@ -33,6 +33,7 @@ Objects in API bitwise_right_shift bitwise_xor ceil + clip conj copysign cos diff --git a/src/array_api_stubs/_draft/elementwise_functions.py b/src/array_api_stubs/_draft/elementwise_functions.py index 3da5412ca..c71b6674b 100644 --- a/src/array_api_stubs/_draft/elementwise_functions.py +++ b/src/array_api_stubs/_draft/elementwise_functions.py @@ -15,6 +15,7 @@ "bitwise_right_shift", "bitwise_xor", "ceil", + "clip", "conj", "copysign", "cos", @@ -65,7 +66,7 @@ ] -from ._types import array +from ._types import Optional, Union, array def abs(x: array, /) -> array: @@ -775,6 +776,38 @@ def ceil(x: array, /) -> array: """ +def clip( + x: array, + /, + min: Optional[Union[int, float, array]] = None, + max: Optional[Union[int, float, array]] = None, +) -> array: + r""" + Clamps each element ``x_i`` of the input array ``x`` to the range ``[min, max]``. + + Parameters + ---------- + x: array + input array. Should have a real-valued data type. + min: Optional[Union[int, float, array]] + lower-bound of the range to which to clamp. If ``None``, no lower bound must be applied. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a real-valued data type. Default: ``None``. + max: Optional[Union[int, float, array]] + upper-bound of the range to which to clamp. If ``None``, no upper bound must be applied. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a real-valued data type. Default: ``None``. + + Returns + ------- + out: array + an array containing element-wise results. The returned array must have the same data type as ``x``. + + Notes + ----- + + - If both ``min`` and ``max`` are ``None``, the elements of the returned array must equal the respective elements in ``x``. + - If a broadcasted element in ``min`` is greater than a corresponding broadcasted element in ``max``, behavior is unspecified and thus implementation-dependent. + - If ``x`` and either ``min`` or ``max`` have different data type kinds (e.g., integer versus floating-point), behavior is unspecified and thus implementation-dependent. + """ + + def conj(x: array, /) -> array: """ Returns the complex conjugate for each element ``x_i`` of the input array ``x``. From 0cde841671d99fc2e028eea87cd13d6eea49704e Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Mon, 22 Jan 2024 21:30:09 +0100 Subject: [PATCH 06/57] Clarify guidance in the "function and method signatures" section The type annotations part was a little outdated. For the positional and keyword-only descriptions, improve the language to account for exceptions to the default design rules. Closes: #475 PR-URL: https://github.com/data-apis/array-api/pull/730 Reviewed-by: Athan Reines --- .../function_and_method_signatures.rst | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/spec/draft/API_specification/function_and_method_signatures.rst b/spec/draft/API_specification/function_and_method_signatures.rst index 86d0819a6..0eca2ac69 100644 --- a/spec/draft/API_specification/function_and_method_signatures.rst +++ b/spec/draft/API_specification/function_and_method_signatures.rst @@ -5,7 +5,7 @@ Function and method signatures Function signatures in this standard adhere to the following: -1. Positional parameters must be `positional-only `_ parameters. +1. Positional parameters should be `positional-only `_ parameters. Positional-only parameters have no externally-usable name. When a function accepting positional-only parameters is called, positional arguments are mapped to these parameters based solely on their order. @@ -20,7 +20,7 @@ Function signatures in this standard adhere to the following: namespace >= 3.8. Alternatively, they can add guidance to their users in the documentation to use the functions as if they were positional-only. -2. Optional parameters must be `keyword-only `_ arguments. +2. Optional parameters should be `keyword-only `_ arguments. *Rationale: this leads to more readable code, and it makes it easier to evolve an API over time by adding keywords without having to worry about @@ -30,8 +30,8 @@ Function signatures in this standard adhere to the following: is called ``x``. For functions that have multiple array parameters, those parameters are called ``xi`` with ``i = 1, 2, ...`` (i.e., ``x1``, ``x2``). -4. Type annotations are left out of the signatures themselves for readability; however, - they are added to individual parameter descriptions. For code which aims to +4. Signatures include type annotations. The type annotations are also added to + individual parameter and return value descriptions. For code which aims to adhere to the standard, adding type annotations is strongly recommended. A function signature and description will look like: @@ -57,3 +57,7 @@ A function signature and description will look like: Method signatures will follow the same conventions modulo the addition of ``self``. + +Note that there are a few exceptions to rules (1) and (2), in cases where +it enhances readability or use of the non-default form of the parameter in +question is commonly used in code written for existing array libraries. From 293512d35f8acccb55b184462505cdb1ab5d1a14 Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Mon, 22 Jan 2024 23:22:28 +0100 Subject: [PATCH 07/57] Upgrade Sphinx version used in doc deployment (#735) * Upgrade Sphinx version used in doc deployment It was breaking with: ``` sphinx-build "spec/2021.12" "_site/2021.12" -W --keep-going Running Sphinx v4.3.0 Sphinx version error: The sphinxcontrib.applehelp extension used by this project needs at least Sphinx v5.0; it therefore cannot be built with this version. ``` * Fix some doc build warnings that broke in CI --- doc-requirements.txt | 3 +-- spec/2021.12/assumptions.md | 4 ++-- spec/2021.12/purpose_and_scope.md | 2 +- spec/2021.12/use_cases.md | 2 +- spec/2022.12/assumptions.md | 4 ++-- spec/2022.12/purpose_and_scope.md | 2 +- spec/2022.12/use_cases.md | 2 +- spec/draft/assumptions.md | 4 ++-- spec/draft/purpose_and_scope.md | 2 +- spec/draft/use_cases.md | 2 +- src/_array_api_conf.py | 2 ++ 11 files changed, 15 insertions(+), 14 deletions(-) diff --git a/doc-requirements.txt b/doc-requirements.txt index 488aa3e81..08ced9aa2 100644 --- a/doc-requirements.txt +++ b/doc-requirements.txt @@ -1,8 +1,7 @@ -sphinx==4.3.0 +sphinx==6.2.1 sphinx-material==0.0.30 myst-parser sphinx_markdown_tables sphinx_copybutton sphinx_favicon -docutils<0.18 sphinx-math-dollar diff --git a/spec/2021.12/assumptions.md b/spec/2021.12/assumptions.md index 3a90710ea..b11482c5a 100644 --- a/spec/2021.12/assumptions.md +++ b/spec/2021.12/assumptions.md @@ -26,7 +26,7 @@ of functions to be predictable from input dtypes only rather than input values. The only dependency that's assumed in this standard is that on Python itself. Python >= 3.8 is assumed, motivated by the use of positional-only parameters -(see [function and method signatures](API_specification/function_and_method_signatures.md)). +(see [function and method signatures](API_specification/function_and_method_signatures.rst)). Importantly, array libraries are not assumed to be aware of each other, or of a common array-specific layer. The [use cases](use_cases.md) do not require @@ -39,7 +39,7 @@ for that is: Array libraries may know how to interoperate with each other, for example by constructing their own array type from that of another library or by shared -memory use of an array (see [Data interchange mechanisms](design_topics/data_interchange.md)). +memory use of an array (see [Data interchange mechanisms](design_topics/data_interchange.rst)). This can be done without a dependency though - only adherence to a protocol is enough. diff --git a/spec/2021.12/purpose_and_scope.md b/spec/2021.12/purpose_and_scope.md index 4678ba064..0debbc08a 100644 --- a/spec/2021.12/purpose_and_scope.md +++ b/spec/2021.12/purpose_and_scope.md @@ -151,7 +151,7 @@ standard is shown in this diagram: _Rationale: this is an important topic for some array-consuming libraries, but there is no widely shared C/Cython API and hence it doesn't make sense at this point in time to standardize anything. See - the [C API section](design_topics/C_API.md) for more details._ + the [C API section](design_topics/C_API.rst) for more details._ 4. Standardization of these dtypes is out of scope: bfloat16, complex, extended precision floating point, datetime, string, object and void dtypes. diff --git a/spec/2021.12/use_cases.md b/spec/2021.12/use_cases.md index 26f7a529c..e24aa50db 100644 --- a/spec/2021.12/use_cases.md +++ b/spec/2021.12/use_cases.md @@ -59,7 +59,7 @@ array implementation as a dependency. It's clear that SciPy functionality that relies on compiled extensions (C, C++, Cython, Fortran) directly can't easily be run on another array library -than NumPy (see [C API](design_topics/C_API.md) for more details about this topic). Pure Python +than NumPy (see [C API](design_topics/C_API.rst) for more details about this topic). Pure Python code can work though. There's two main possibilities: 1. Testing with another package, manually or in CI, and simply provide a list diff --git a/spec/2022.12/assumptions.md b/spec/2022.12/assumptions.md index 3a90710ea..b11482c5a 100644 --- a/spec/2022.12/assumptions.md +++ b/spec/2022.12/assumptions.md @@ -26,7 +26,7 @@ of functions to be predictable from input dtypes only rather than input values. The only dependency that's assumed in this standard is that on Python itself. Python >= 3.8 is assumed, motivated by the use of positional-only parameters -(see [function and method signatures](API_specification/function_and_method_signatures.md)). +(see [function and method signatures](API_specification/function_and_method_signatures.rst)). Importantly, array libraries are not assumed to be aware of each other, or of a common array-specific layer. The [use cases](use_cases.md) do not require @@ -39,7 +39,7 @@ for that is: Array libraries may know how to interoperate with each other, for example by constructing their own array type from that of another library or by shared -memory use of an array (see [Data interchange mechanisms](design_topics/data_interchange.md)). +memory use of an array (see [Data interchange mechanisms](design_topics/data_interchange.rst)). This can be done without a dependency though - only adherence to a protocol is enough. diff --git a/spec/2022.12/purpose_and_scope.md b/spec/2022.12/purpose_and_scope.md index 38287f270..f375c9512 100644 --- a/spec/2022.12/purpose_and_scope.md +++ b/spec/2022.12/purpose_and_scope.md @@ -151,7 +151,7 @@ standard is shown in this diagram: _Rationale: this is an important topic for some array-consuming libraries, but there is no widely shared C/Cython API and hence it doesn't make sense at this point in time to standardize anything. See - the [C API section](design_topics/C_API.md) for more details._ + the [C API section](design_topics/C_API.rst) for more details._ 4. Standardization of these dtypes is out of scope: bfloat16, extended precision floating point, datetime, string, object and void dtypes. diff --git a/spec/2022.12/use_cases.md b/spec/2022.12/use_cases.md index 26f7a529c..e24aa50db 100644 --- a/spec/2022.12/use_cases.md +++ b/spec/2022.12/use_cases.md @@ -59,7 +59,7 @@ array implementation as a dependency. It's clear that SciPy functionality that relies on compiled extensions (C, C++, Cython, Fortran) directly can't easily be run on another array library -than NumPy (see [C API](design_topics/C_API.md) for more details about this topic). Pure Python +than NumPy (see [C API](design_topics/C_API.rst) for more details about this topic). Pure Python code can work though. There's two main possibilities: 1. Testing with another package, manually or in CI, and simply provide a list diff --git a/spec/draft/assumptions.md b/spec/draft/assumptions.md index 3a90710ea..b11482c5a 100644 --- a/spec/draft/assumptions.md +++ b/spec/draft/assumptions.md @@ -26,7 +26,7 @@ of functions to be predictable from input dtypes only rather than input values. The only dependency that's assumed in this standard is that on Python itself. Python >= 3.8 is assumed, motivated by the use of positional-only parameters -(see [function and method signatures](API_specification/function_and_method_signatures.md)). +(see [function and method signatures](API_specification/function_and_method_signatures.rst)). Importantly, array libraries are not assumed to be aware of each other, or of a common array-specific layer. The [use cases](use_cases.md) do not require @@ -39,7 +39,7 @@ for that is: Array libraries may know how to interoperate with each other, for example by constructing their own array type from that of another library or by shared -memory use of an array (see [Data interchange mechanisms](design_topics/data_interchange.md)). +memory use of an array (see [Data interchange mechanisms](design_topics/data_interchange.rst)). This can be done without a dependency though - only adherence to a protocol is enough. diff --git a/spec/draft/purpose_and_scope.md b/spec/draft/purpose_and_scope.md index 38287f270..f375c9512 100644 --- a/spec/draft/purpose_and_scope.md +++ b/spec/draft/purpose_and_scope.md @@ -151,7 +151,7 @@ standard is shown in this diagram: _Rationale: this is an important topic for some array-consuming libraries, but there is no widely shared C/Cython API and hence it doesn't make sense at this point in time to standardize anything. See - the [C API section](design_topics/C_API.md) for more details._ + the [C API section](design_topics/C_API.rst) for more details._ 4. Standardization of these dtypes is out of scope: bfloat16, extended precision floating point, datetime, string, object and void dtypes. diff --git a/spec/draft/use_cases.md b/spec/draft/use_cases.md index 26f7a529c..e24aa50db 100644 --- a/spec/draft/use_cases.md +++ b/spec/draft/use_cases.md @@ -59,7 +59,7 @@ array implementation as a dependency. It's clear that SciPy functionality that relies on compiled extensions (C, C++, Cython, Fortran) directly can't easily be run on another array library -than NumPy (see [C API](design_topics/C_API.md) for more details about this topic). Pure Python +than NumPy (see [C API](design_topics/C_API.rst) for more details about this topic). Pure Python code can work though. There's two main possibilities: 1. Testing with another package, manually or in CI, and simply provide a list diff --git a/src/_array_api_conf.py b/src/_array_api_conf.py index d3a136eaa..9c5722f94 100644 --- a/src/_array_api_conf.py +++ b/src/_array_api_conf.py @@ -52,12 +52,14 @@ nitpick_ignore = [ ("py:class", "collections.abc.Sequence"), ("py:class", "Optional[Union[int, float, Literal[inf, - inf, 'fro', 'nuc']]]"), + ("py:class", "int | float | ~typing.Literal[inf, -inf, 'fro', 'nuc'] | None"), ("py:class", "Union[int, float, Literal[inf, - inf]]"), ( "py:obj", "typing.Optional[typing.Union[int, float, typing.Literal[inf, - inf, 'fro', 'nuc']]]", ), ("py:obj", "typing.Union[int, float, typing.Literal[inf, - inf]]"), + ("py:class", "int | float | ~typing.Literal[inf, -inf]"), ("py:class", "enum.Enum"), ("py:class", "ellipsis"), ] From 76873d83c57041c9a1d2e0b8b7aabb80c014cdbd Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 25 Jan 2024 03:42:09 -0800 Subject: [PATCH 08/57] Remove whitespace --- src/array_api_stubs/_draft/searching_functions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/array_api_stubs/_draft/searching_functions.py b/src/array_api_stubs/_draft/searching_functions.py index 58157f20d..62afd235a 100644 --- a/src/array_api_stubs/_draft/searching_functions.py +++ b/src/array_api_stubs/_draft/searching_functions.py @@ -257,7 +257,7 @@ def top_k_values( - ``'smallest'``: return the indices of the ``k`` smallest elements. Default: ``'largest'``. - + Returns ------- out: array @@ -271,7 +271,7 @@ def top_k_values( - Conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-dependent (see :ref:`complex-number-ordering`). """ - + def where(condition: array, x1: array, x2: array, /) -> array: """ Returns elements chosen from ``x1`` or ``x2`` depending on ``condition``. From 95332bb71159cea68e15e7d20364a3e07060a402 Mon Sep 17 00:00:00 2001 From: Hameer Abbasi <2190658+hameerabbasi@users.noreply.github.com> Date: Thu, 25 Jan 2024 13:59:59 +0100 Subject: [PATCH 09/57] Fix wording for comparison operators PR-URL: https://github.com/data-apis/array-api/pull/736 Co-authored-by: Athan Reines Reviewed-by: Athan Reines --- spec/2022.12/API_specification/array_object.rst | 3 ++- spec/draft/API_specification/array_object.rst | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/2022.12/API_specification/array_object.rst b/spec/2022.12/API_specification/array_object.rst index b15bbdc43..45aec9b34 100644 --- a/spec/2022.12/API_specification/array_object.rst +++ b/spec/2022.12/API_specification/array_object.rst @@ -163,7 +163,8 @@ A conforming implementation of the array API standard must provide and support a - `operator.ne(x1, x2) `_ - `operator.__ne__(x1, x2) `_ -Comparison operators should be defined for arrays having any data type. +:meth:`.array.__lt__`, :meth:`.array.__le__`, :meth:`.array.__gt__`, :meth:`.array.__ge__` are only defined for arrays having real-valued data types. Other comparison operators should be defined for arrays having any data type. +For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-dependent (see :ref:`complex-number-ordering`). In-place Operators ~~~~~~~~~~~~~~~~~~ diff --git a/spec/draft/API_specification/array_object.rst b/spec/draft/API_specification/array_object.rst index 0fadf9fc5..f8a586ade 100644 --- a/spec/draft/API_specification/array_object.rst +++ b/spec/draft/API_specification/array_object.rst @@ -163,7 +163,8 @@ A conforming implementation of the array API standard must provide and support a - `operator.ne(x1, x2) `_ - `operator.__ne__(x1, x2) `_ -Comparison operators should be defined for arrays having any data type. +:meth:`.array.__lt__`, :meth:`.array.__le__`, :meth:`.array.__gt__`, :meth:`.array.__ge__` are only defined for arrays having real-valued data types. Other comparison operators should be defined for arrays having any data type. +For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-dependent (see :ref:`complex-number-ordering`). In-place Operators ~~~~~~~~~~~~~~~~~~ From 937d3b8e22ef42d2420a951cefe6ec28107f31bf Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 8 Feb 2024 06:14:02 -0700 Subject: [PATCH 10/57] Clarify specification guidance in FFT functions PR-URL: https://github.com/data-apis/array-api/pull/720 Closes: https://github.com/data-apis/array-api/issues/717 Closes: https://github.com/data-apis/array-api/issues/718 Ref: https://github.com/data-apis/array-api/pull/189 Co-authored-by: Leo Fang Co-authored-by: Athan Reines Reviewed-by: Leo Fang Reviewed-by: Athan Reines Reviewed-by: Ralf Gommers --- src/array_api_stubs/_2022_12/fft.py | 297 +++++++++++++--------------- src/array_api_stubs/_draft/fft.py | 262 +++++++++++------------- 2 files changed, 253 insertions(+), 306 deletions(-) diff --git a/src/array_api_stubs/_2022_12/fft.py b/src/array_api_stubs/_2022_12/fft.py index 7979095fe..f79aa155e 100644 --- a/src/array_api_stubs/_2022_12/fft.py +++ b/src/array_api_stubs/_2022_12/fft.py @@ -1,3 +1,20 @@ +__all__ = [ + "fft", + "ifft", + "fftn", + "ifftn", + "rfft", + "irfft", + "rfftn", + "irfftn", + "hfft", + "ihfft", + "fftfreq", + "rfftfreq", + "fftshift", + "ifftshift", +] + from ._types import Tuple, Union, Sequence, array, Optional, Literal, device @@ -13,24 +30,22 @@ def fft( Computes the one-dimensional discrete Fourier transform. .. note:: - Applying the one-dimensional inverse discrete Fourier transform to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``ifft(fft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (length, axis, and normalization mode). + Applying the one-dimensional inverse discrete Fourier transform to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``ifft(fft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (number of elements, axis, and normalization mode). Parameters ---------- x: array - input array. Should have a floating-point data type. - n: int - length of the transformed axis of the output. If + input array. Should have a complex-valued floating-point data type. + n: Optional[int] + number of elements over which to compute the transform along the axis (dimension) specified by ``axis``. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``M``. - - ``n`` is greater than the length of the input array, the input array is zero-padded to length ``n``. - - ``n`` is less than the length of the input array, the input array is trimmed to length ``n``. - - ``n`` is not provided, the length of the transformed axis of the output must equal the length of the input along the axis specified by ``axis``. + - If ``n`` is greater than ``M``, the axis specified by ``axis`` must be zero-padded to size ``n``. + - If ``n`` is less than ``M``, the axis specified by ``axis`` must be trimmed to size ``n``. + - If ``n`` equals ``M``, all elements along the axis specified by ``axis`` must be used when computing the transform. Default: ``None``. axis: int - axis (dimension) over which to compute the Fourier transform. If not set, the last axis (dimension) is used. - - Default: ``-1``. + axis (dimension) of the input array over which to compute the transform. A valid ``axis`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an ``axis`` is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). Default: ``-1``. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -43,7 +58,7 @@ def fft( Returns ------- out: array - an array transformed along the axis (dimension) indicated by ``axis``. The returned array must have a complex floating-point data type determined by :ref:`type-promotion`. + an array transformed along the axis (dimension) specified by ``axis``. The returned array must have the same data type as ``x`` and must have the same shape as ``x``, except for the axis specified by ``axis`` which must have size ``n``. Notes ----- @@ -64,24 +79,22 @@ def ifft( Computes the one-dimensional inverse discrete Fourier transform. .. note:: - Applying the one-dimensional inverse discrete Fourier transform to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``ifft(fft(x)) == x``), provided that the transform and inverse transform are performed with the same (length, axis, and normalization mode). + Applying the one-dimensional inverse discrete Fourier transform to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``ifft(fft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (number of elements, axis, and normalization mode). Parameters ---------- x: array - input array. Should have a floating-point data type. - n: int - length of the transformed axis of the output. If + input array. Should have a complex-valued floating-point data type. + n: Optional[int] + number of elements over which to compute the transform along the axis (dimension) specified by ``axis``. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``M``. - - ``n`` is greater than the length of the input array, the input array is zero-padded to length ``n``. - - ``n`` is less than the length of the input array, the input array is trimmed to length ``n``. - - ``n`` is not provided, the length of the transformed axis of the output must equal the length of the input along the axis specified by ``axis``. + - If ``n`` is greater than ``M``, the axis specified by ``axis`` must be zero-padded to size ``n``. + - If ``n`` is less than ``M``, the axis specified by ``axis`` must be trimmed to size ``n``. + - If ``n`` equals ``M``, all elements along the axis specified by ``axis`` must be used when computing the transform. Default: ``None``. axis: int - axis (dimension) over which to compute the inverse Fourier transform. If not set, the last axis (dimension) is used. - - Default: ``-1``. + axis (dimension) of the input array over which to compute the transform. A valid ``axis`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an ``axis`` is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). Default: ``-1``. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -94,7 +107,7 @@ def ifft( Returns ------- out: array - an array transformed along the axis (dimension) indicated by ``axis``. The returned array must have a complex floating-point data type determined by :ref:`type-promotion`. + an array transformed along the axis (dimension) specified by ``axis``. The returned array must have the same data type as ``x`` and must have the same shape as ``x``, except for the axis specified by ``axis`` which must have size ``n``. Notes ----- @@ -107,8 +120,8 @@ def fftn( x: array, /, *, - s: Sequence[int] = None, - axes: Sequence[int] = None, + s: Optional[Sequence[int]] = None, + axes: Optional[Sequence[int]] = None, norm: Literal["backward", "ortho", "forward"] = "backward", ) -> array: """ @@ -120,24 +133,19 @@ def fftn( Parameters ---------- x: array - input array. Should have a floating-point data type. - s: Sequence[int] - size of each transformed axis of the output. If - - - ``s[i]`` is greater than the size of the input array along the corresponding axis (dimension) ``i``, the input array along the axis ``i`` is zero-padded to size ``s[i]``. - - ``s[i]`` is less than the size of the input array along a corresponding axis (dimension) ``i``, the input array along the axis ``i`` is trimmed to size ``s[i]``. - - ``s[i]`` is ``-1``, the whole input array along the axis ``i`` is used (no padding/trimming). - - ``s`` is not provided, the size of each transformed axis (dimension) in the output array must equal the size of the corresponding axis in the input array. + input array. Should have a complex-valued floating-point data type. + s: Optional[Sequence[int]] + number of elements over which to compute the transform along the axes (dimensions) specified by ``axes``. Let ``i`` be the index of the nth axis specified by ``axes`` and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers, such that, for all ``i``, ``s[i]`` equals ``M[i]``. - If ``s`` is not ``None``, ``axes`` must not be ``None`` either, and ``s[i]`` corresponds to the size along the transformed axis specified by ``axes[i]``. + - If ``s[i]`` is greater than ``M[i]``, axis ``i`` must be zero-padded to size ``s[i]``. + - If ``s[i]`` is less than ``M[i]``, axis ``i`` must be trimmed to size ``s[i]``. + - If ``s[i]`` equals ``M[i]`` or ``-1``, all elements along axis ``i`` must be used when computing the transform. - Default: ``None``. - axes: Sequence[int] - axes (dimensions) over which to compute the Fourier transform. If ``None``, all axes must be transformed. + If ``s`` is not ``None``, ``axes`` must not be ``None``. Default: ``None``. + axes: Optional[Sequence[int]] + axes (dimensions) over which to compute the transform. A valid axis must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). - If ``s`` is specified, the corresponding ``axes`` to be transformed must be explicitly specified too. - - Default: ``None``. + If ``s`` is provided, the corresponding ``axes`` to be transformed must also be provided. If ``axes`` is ``None``, the function must compute the transform over all axes. Default: ``None``. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -152,7 +160,7 @@ def fftn( Returns ------- out: array - an array transformed along the axes (dimension) indicated by ``axes``. The returned array must have a complex floating-point data type determined by :ref:`type-promotion`. + an array transformed along the axes (dimensions) specified by ``axes``. The returned array must have the same data type as ``x`` and must have the same shape as ``x``, except for the axes specified by ``axes`` which must have size ``s[i]``. Notes ----- @@ -165,8 +173,8 @@ def ifftn( x: array, /, *, - s: Sequence[int] = None, - axes: Sequence[int] = None, + s: Optional[Sequence[int]] = None, + axes: Optional[Sequence[int]] = None, norm: Literal["backward", "ortho", "forward"] = "backward", ) -> array: """ @@ -178,24 +186,19 @@ def ifftn( Parameters ---------- x: array - input array. Should have a floating-point data type. - s: Sequence[int] - size of each transformed axis of the output. If - - - ``s[i]`` is greater than the size of the input array along the corresponding axis (dimension) ``i``, the input array along the axis ``i`` is zero-padded to size ``s[i]``. - - ``s[i]`` is less than the size of the input array along a corresponding axis (dimension) ``i``, the input array along the axis ``i`` is trimmed to size ``s[i]``. - - ``s[i]`` is ``-1``, the whole input array along the axis ``i`` is used (no padding/trimming). - - ``s`` is not provided, the size of each transformed axis (dimension) in the output array must equal the size of the corresponding axis in the input array. - - If ``s`` is not ``None``, ``axes`` must not be ``None`` either, and ``s[i]`` corresponds to the size along the transformed axis specified by ``axes[i]``. + input array. Should have a complex-valued floating-point data type. + s: Optional[Sequence[int]] + number of elements over which to compute the transform along the axes (dimensions) specified by ``axes``. Let ``i`` be the index of the nth axis specified by ``axes`` and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers, such that, for all ``i``, ``s[i]`` equals ``M[i]``. - Default: ``None``. - axes: Sequence[int] - axes (dimensions) over which to compute the Fourier transform. If ``None``, all axes must be transformed. + - If ``s[i]`` is greater than ``M[i]``, axis ``i`` must be zero-padded to size ``s[i]``. + - If ``s[i]`` is less than ``M[i]``, axis ``i`` must be trimmed to size ``s[i]``. + - If ``s[i]`` equals ``M[i]`` or ``-1``, all elements along axis ``i`` must be used when computing the transform. - If ``s`` is specified, the corresponding ``axes`` to be transformed must be explicitly specified too. + If ``s`` is not ``None``, ``axes`` must not be ``None``. Default: ``None``. + axes: Optional[Sequence[int]] + axes (dimensions) over which to compute the transform. A valid axis must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). - Default: ``None``. + If ``s`` is provided, the corresponding ``axes`` to be transformed must also be provided. If ``axes`` is ``None``, the function must compute the transform over all axes. Default: ``None``. norm: Literal['backward', 'ortho', 'forward'] specify the normalization mode. Should be one of the following modes: @@ -210,7 +213,7 @@ def ifftn( Returns ------- out: array - an array transformed along the axes (dimension) indicated by ``axes``. The returned array must have a complex floating-point data type determined by :ref:`type-promotion`. + an array transformed along the axes (dimensions) specified by ``axes``. The returned array must have the same data type as ``x`` and must have the same shape as ``x``, except for the axes specified by ``axes`` which must have size ``s[i]``. Notes ----- @@ -231,24 +234,22 @@ def rfft( Computes the one-dimensional discrete Fourier transform for real-valued input. .. note:: - Applying the one-dimensional inverse discrete Fourier transform for real-valued input to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``irfft(rfft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (axis and normalization mode) and consistent length. + Applying the one-dimensional inverse discrete Fourier transform for real-valued input to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``irfft(rfft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (axis and and normalization mode) and consistent values for the number of elements over which to compute the transforms. Parameters ---------- x: array input array. Must have a real-valued floating-point data type. - n: int - length of the transformed axis of the **input**. If + n: Optional[int] + number of elements over which to compute the transform along the axis (dimension) specified by ``axis``. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``M``. - - ``n`` is greater than the length of the input array, the input array is zero-padded to length ``n``. - - ``n`` is less than the length of the input array, the input array is trimmed to length ``n``. - - ``n`` is not provided, the length of the transformed axis of the output must equal the length of the input along the axis specified by ``axis``. + - If ``n`` is greater than ``M``, the axis specified by ``axis`` must be zero-padded to size ``n``. + - If ``n`` is less than ``M``, the axis specified by ``axis`` must be trimmed to size ``n``. + - If ``n`` equals ``M``, all elements along the axis specified by ``axis`` must be used when computing the transform. Default: ``None``. axis: int - axis (dimension) over which to compute the Fourier transform. If not set, the last axis (dimension) is used. - - Default: ``-1``. + axis (dimension) of the input array over which to compute the transform. A valid ``axis`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an ``axis`` is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). Default: ``-1``. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -261,7 +262,7 @@ def rfft( Returns ------- out: array - an array transformed along the axis (dimension) indicated by ``axis``. The returned array must have a complex-valued floating-point data type determined by :ref:`type-promotion`. + an array transformed along the axis (dimension) specified by ``axis``. The returned array must have a complex floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``float64``, then the returned array must have a ``complex128`` data type). The returned array must have the same shape as ``x``, except for the axis specified by ``axis`` which must have size ``n//2 + 1``. Notes ----- @@ -282,24 +283,22 @@ def irfft( Computes the one-dimensional inverse of ``rfft`` for complex-valued input. .. note:: - Applying the one-dimensional inverse discrete Fourier transform for real-valued input to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``irfft(rfft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (axis and normalization mode) and consistent length. + Applying the one-dimensional inverse discrete Fourier transform for real-valued input to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``irfft(rfft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (axis and and normalization mode) and consistent values for the number of elements over which to compute the transforms. Parameters ---------- x: array input array. Should have a complex-valued floating-point data type. - n: int - length of the transformed axis of the **output**. If + n: Optional[int] + number of elements along the transformed axis (dimension) specified by ``axis`` in the **output array**. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``2*(M-1)``. - - ``n//2+1`` is greater than the length of the input array, the input array is zero-padded to length ``n//2+1``. - - ``n//2+1`` is less than the length of the input array, the input array is trimmed to length ``n//2+1``. - - ``n`` is not provided, the length of the transformed axis of the output must equal the length ``2*(m-1)``, where ``m`` is the length of the input along the axis specified by ``axis``. + - If ``n//2+1`` is greater than ``M``, the axis of the input array specified by ``axis`` must be zero-padded to size ``n//2+1``. + - If ``n//2+1`` is less than ``M``, the axis of the input array specified by ``axis`` must be trimmed to size ``n//2+1``. + - If ``n//2+1`` equals ``M``, all elements along the axis of the input array specified by ``axis`` must be used when computing the transform. Default: ``None``. axis: int - axis (dimension) over which to compute the inverse Fourier transform. If not set, the last axis (dimension) is used. - - Default: ``-1``. + axis (dimension) of the input array over which to compute the transform. A valid ``axis`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an ``axis`` is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). Default: ``-1``. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -312,11 +311,13 @@ def irfft( Returns ------- out: array - an array transformed along the axis (dimension) indicated by ``axis``. The returned array must have a real-valued floating-point data type determined by :ref:`type-promotion`. The length along the transformed axis is ``n`` (if given) or ``2*(m-1)`` (otherwise). + an array transformed along the axis (dimension) specified by ``axis``. The returned array must have a real-valued floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``complex128``, then the returned array must have a ``float64`` data type). The returned array must have the same shape as ``x``, except for the axis specified by ``axis`` which must have size ``n``. Notes ----- + - In order to return an array having an odd number of elements along the transformed axis, the function must be provided an odd integer for ``n``. + .. versionadded:: 2022.12 """ @@ -325,8 +326,8 @@ def rfftn( x: array, /, *, - s: Sequence[int] = None, - axes: Sequence[int] = None, + s: Optional[Sequence[int]] = None, + axes: Optional[Sequence[int]] = None, norm: Literal["backward", "ortho", "forward"] = "backward", ) -> array: """ @@ -339,23 +340,18 @@ def rfftn( ---------- x: array input array. Must have a real-valued floating-point data type. - s: Sequence[int] - size of each transformed axis of the **input**. If - - - ``s[i]`` is greater than the size of the input array along the corresponding axis (dimension) ``i``, the input array along the axis ``i`` is zero-padded to size ``s[i]``. - - ``s[i]`` is less than the size of the input array along a corresponding axis (dimension) ``i``, the input array along the axis ``i`` is trimmed to size ``s[i]``. - - ``s[i]`` is ``-1``, the whole input array along the axis ``i`` is used (no padding/trimming). - - ``s`` is not provided, the size of each transformed axis (dimension) in the output array must equal the size of the corresponding axis in the input array. + s: Optional[Sequence[int]] + number of elements over which to compute the transform along axes (dimensions) specified by ``axes``. Let ``i`` be the index of the nth axis specified by ``axes`` and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers, such that, for all ``i``, ``s[i]`` equals ``M[i]``. - If ``s`` is not ``None``, ``axes`` must not be ``None`` either, and ``s[i]`` corresponds to the size along the transformed axis specified by ``axes[i]``. + - If ``s[i]`` is greater than ``M[i]``, axis ``i`` must be zero-padded to size ``s[i]``. + - If ``s[i]`` is less than ``M[i]``, axis ``i`` must be trimmed to size ``s[i]``. + - If ``s[i]`` equals ``M[i]`` or ``-1``, all elements along axis ``i`` must be used when computing the transform. - Default: ``None``. - axes: Sequence[int] - axes (dimensions) over which to compute the Fourier transform. If ``None``, all axes must be transformed. - - If ``s`` is specified, the corresponding ``axes`` to be transformed must be explicitly specified too. + If ``s`` is not ``None``, ``axes`` must not be ``None``. Default: ``None``. + axes: Optional[Sequence[int]] + axes (dimensions) over which to compute the transform. A valid axis must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). - Default: ``None``. + If ``s`` is provided, the corresponding ``axes`` to be transformed must also be provided. If ``axes`` is ``None``, the function must compute the transform over all axes. Default: ``None``. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -370,7 +366,7 @@ def rfftn( Returns ------- out: array - an array transformed along the axes (dimension) indicated by ``axes``. The returned array must have a complex-valued floating-point data type determined by :ref:`type-promotion`. + an array transformed along the axes (dimension) specified by ``axes``. The returned array must have a complex floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``float64``, then the returned array must have a ``complex128`` data type). The returned array must have the same shape as ``x``, except for the last transformed axis which must have size ``s[-1]//2 + 1`` and the remaining transformed axes which must have size ``s[i]``. Notes ----- @@ -383,8 +379,8 @@ def irfftn( x: array, /, *, - s: Sequence[int] = None, - axes: Sequence[int] = None, + s: Optional[Sequence[int]] = None, + axes: Optional[Sequence[int]] = None, norm: Literal["backward", "ortho", "forward"] = "backward", ) -> array: """ @@ -397,23 +393,18 @@ def irfftn( ---------- x: array input array. Should have a complex-valued floating-point data type. - s: Sequence[int] - size of each transformed axis of the **output**. ``n=s[i]`` is also the number of input points used along the axis (dimension) ``i``, except for the last axis, where ``n=s[-1]//2+1`` points of the input are used. If + s: Optional[Sequence[int]] + number of elements along the transformed axes (dimensions) specified by ``axes`` in the **output array**. Let ``i`` be the index of the nth axis specified by ``axes`` and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers, such that, for all ``i``, ``s[i]`` equals ``M[i]``, except for the last transformed axis in which ``s[i]`` equals ``2*(M[i]-1)``. For each ``i``, let ``n`` equal ``s[i]``, except for the last transformed axis in which ``n`` equals ``s[i]//2+1``. - - ``n`` is greater than the size of the input array along the corresponding axis (dimension) ``i``, the input array along the axis ``i`` is zero-padded to size ``n``. - - ``n`` is less than the size of the input array along the corresponding axis (dimension) ``i``, the input array along the axis ``i`` is trimmed to size ``n``. - - ``s[i]`` is ``-1``, the whole input array along the axis ``i`` is used (no padding/trimming). - - ``s`` is not provided, the size of each transformed axis (dimension) in the output array must equal the size of the corresponding axis in the input array, except for the last axis which is trimmed to ``2*(m-1)``, where ``m`` is the length of the input along the axis. + - If ``n`` is greater than ``M[i]``, axis ``i`` of the input array must be zero-padded to size ``n``. + - If ``n`` is less than ``M[i]``, axis ``i`` of the input array must be trimmed to size ``n``. + - If ``n`` equals ``M[i]`` or ``-1``, all elements along axis ``i`` of the input array must be used when computing the transform. - If ``s`` is not ``None``, ``axes`` must not be ``None`` either, and ``s[i]`` corresponds to the size along the transformed axis specified by ``axes[i]``. - - Default: ``None``. - axes: Sequence[int] - axes (dimensions) over which to compute the Fourier transform. If ``None``, all axes must be transformed. + If ``s`` is not ``None``, ``axes`` must not be ``None``. Default: ``None``. + axes: Optional[Sequence[int]] + axes (dimensions) over which to compute the transform. A valid axis must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). - If ``s`` is specified, the corresponding ``axes`` to be transformed must be explicitly specified too. - - Default: ``None``. + If ``s`` is provided, the corresponding ``axes`` to be transformed must also be provided. If ``axes`` is ``None``, the function must compute the transform over all axes. Default: ``None``. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -428,11 +419,13 @@ def irfftn( Returns ------- out: array - an array transformed along the axes (dimension) indicated by ``axes``. The returned array must have a real-valued floating-point data type determined by :ref:`type-promotion`. The length along the last transformed axis is ``s[-1]`` (if given) or ``2*(m - 1)`` (otherwise), and all other axes ``s[i]``. + an array transformed along the axes (dimension) specified by ``axes``. The returned array must have a real-valued floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``complex128``, then the returned array must have a ``float64`` data type). The returned array must have the same shape as ``x``, except for the transformed axes which must have size ``s[i]``. Notes ----- + - In order to return an array having an odd number of elements along the last transformed axis, the function must be provided an odd integer for ``s[-1]``. + .. versionadded:: 2022.12 """ @@ -451,19 +444,17 @@ def hfft( Parameters ---------- x: array - input array. Should have a floating-point data type. - n: int - length of the transformed axis of the **output**. If + input array. Should have a complex-valued floating-point data type. + n: Optional[int] + number of elements along the transformed axis (dimension) specified by ``axis`` in the **output array**. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``2*(M-1)``. - - ``n//2+1`` is greater than the length of the input array, the input array is zero-padded to length ``n//2+1``. - - ``n//2+1`` is less than the length of the input array, the input array is trimmed to length ``n//2+1``. - - ``n`` is not provided, the length of the transformed axis of the output must equal the length ``2*(m-1)``, where ``m`` is the length of the input along the axis specified by ``axis``. + - If ``n//2+1`` is greater than ``M``, the axis of the input array specified by ``axis`` must be zero-padded to length ``n//2+1``. + - If ``n//2+1`` is less than ``M``, the axis of the input array specified by ``axis`` must be trimmed to size ``n//2+1``. + - If ``n//2+1`` equals ``M``, all elements along the axis of the input array specified by ``axis`` must be used when computing the transform. Default: ``None``. axis: int - axis (dimension) over which to compute the Fourier transform. If not set, the last axis (dimension) is used. - - Default: ``-1``. + axis (dimension) of the input array over which to compute the transform. A valid ``axis`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an ``axis`` is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). Default: ``-1``. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -476,7 +467,7 @@ def hfft( Returns ------- out: array - an array transformed along the axis (dimension) indicated by ``axis``. The returned array must have a real-valued floating-point data type determined by :ref:`type-promotion`. + an array transformed along the axis (dimension) specified by ``axis``. The returned array must have a real-valued floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``complex128``, then the returned array must have a ``float64`` data type). The returned array must have the same shape as ``x``, except for the axis specified by ``axis`` which must have size ``n``. Notes ----- @@ -500,18 +491,16 @@ def ihfft( ---------- x: array input array. Must have a real-valued floating-point data type. - n: int - length of the transformed axis of the **input**. If + n: Optional[int] + number of elements over which to compute the transform along the axis (dimension) specified by ``axis``. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``M``. - - ``n`` is greater than the length of the input array, the input array is zero-padded to length ``n``. - - ``n`` is less than the length of the input array, the input array is trimmed to length ``n``. - - ``n`` is not provided, the length of the transformed axis of the output must equal the length of the input along the axis specified by ``axis``. + - If ``n`` is greater than ``M``, the axis specified by ``axis`` must be zero-padded to size ``n``. + - If ``n`` is less than ``M``, the axis specified by ``axis`` must be trimmed to size ``n``. + - If ``n`` equals ``M``, all elements along the axis specified by ``axis`` must be used when computing the transform. Default: ``None``. axis: int - axis (dimension) over which to compute the Fourier transform. If not set, the last axis (dimension) is used. - - Default: ``-1``. + axis (dimension) of the input array over which to compute the transform. A valid ``axis`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an ``axis`` is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). Default: ``-1``. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -524,7 +513,7 @@ def ihfft( Returns ------- out: array - an array transformed along the axis (dimension) indicated by ``axis``. The returned array must have a complex-valued floating-point data type determined by :ref:`type-promotion`. + an array transformed along the axis (dimension) specified by ``axis``. The returned array must have a complex floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``float64``, then the returned array must have a ``complex128`` data type). The returned array must have the same shape as ``x``, except for the axis specified by ``axis`` which must have size ``n//2 + 1``. Notes ----- @@ -535,9 +524,9 @@ def ihfft( def fftfreq(n: int, /, *, d: float = 1.0, device: Optional[device] = None) -> array: """ - Returns the discrete Fourier transform sample frequencies. + Computes the discrete Fourier transform sample frequencies. - For a Fourier transform of length ``n`` and length unit of ``d`` the frequencies are described as: + For a Fourier transform of length ``n`` and length unit of ``d``, the frequencies are described as: .. code-block:: @@ -556,7 +545,7 @@ def fftfreq(n: int, /, *, d: float = 1.0, device: Optional[device] = None) -> ar Returns ------- out: array - an array of length ``n`` containing the sample frequencies. The returned array must have a real-valued floating-point data type determined by :ref:`type-promotion`. + an array of shape ``(n,)`` containing the sample frequencies. The returned array must have the default real-valued floating-point data type. Notes ----- @@ -567,9 +556,9 @@ def fftfreq(n: int, /, *, d: float = 1.0, device: Optional[device] = None) -> ar def rfftfreq(n: int, /, *, d: float = 1.0, device: Optional[device] = None) -> array: """ - Returns the discrete Fourier transform sample frequencies (for ``rfft`` and ``irfft``). + Computes the discrete Fourier transform sample frequencies (for ``rfft`` and ``irfft``). - For a Fourier transform of length ``n`` and length unit of ``d`` the frequencies are described as: + For a Fourier transform of length ``n`` and length unit of ``d``, the frequencies are described as: .. code-block:: @@ -590,7 +579,7 @@ def rfftfreq(n: int, /, *, d: float = 1.0, device: Optional[device] = None) -> a Returns ------- out: array - an array of length ``n//2+1`` containing the sample frequencies. The returned array must have a real-valued floating-point data type determined by :ref:`type-promotion`. + an array of shape ``(n//2+1,)`` containing the sample frequencies. The returned array must have the default real-valued floating-point data type. Notes ----- @@ -599,9 +588,9 @@ def rfftfreq(n: int, /, *, d: float = 1.0, device: Optional[device] = None) -> a """ -def fftshift(x: array, /, *, axes: Union[int, Sequence[int]] = None) -> array: +def fftshift(x: array, /, *, axes: Optional[Union[int, Sequence[int]]] = None) -> array: """ - Shift the zero-frequency component to the center of the spectrum. + Shifts the zero-frequency component to the center of the spectrum. This function swaps half-spaces for all axes (dimensions) specified by ``axes``. @@ -612,13 +601,13 @@ def fftshift(x: array, /, *, axes: Union[int, Sequence[int]] = None) -> array: ---------- x: array input array. Should have a floating-point data type. - axes: Union[int, Sequence[int]] + axes: Optional[Union[int, Sequence[int]]] axes over which to shift. If ``None``, the function must shift all axes. Default: ``None``. Returns ------- out: array - the shifted array. The returned array must have the same data type as ``x``. + the shifted array. The returned array must have the same data type and shape as ``x``. Notes ----- @@ -627,7 +616,9 @@ def fftshift(x: array, /, *, axes: Union[int, Sequence[int]] = None) -> array: """ -def ifftshift(x: array, /, *, axes: Union[int, Sequence[int]] = None) -> array: +def ifftshift( + x: array, /, *, axes: Optional[Union[int, Sequence[int]]] = None +) -> array: """ Inverse of ``fftshift``. @@ -638,34 +629,16 @@ def ifftshift(x: array, /, *, axes: Union[int, Sequence[int]] = None) -> array: ---------- x: array input array. Should have a floating-point data type. - axes: Union[int, Sequence[int]] + axes: Optional[Union[int, Sequence[int]]] axes over which to perform the inverse shift. If ``None``, the function must shift all axes. Default: ``None``. Returns ------- out: array - the shifted array. The returned array must have the same data type as ``x``. + the shifted array. The returned array must have the same data type and shape as ``x``. Notes ----- .. versionadded:: 2022.12 """ - - -__all__ = [ - "fft", - "ifft", - "fftn", - "ifftn", - "rfft", - "irfft", - "rfftn", - "irfftn", - "hfft", - "ihfft", - "fftfreq", - "rfftfreq", - "fftshift", - "ifftshift", -] diff --git a/src/array_api_stubs/_draft/fft.py b/src/array_api_stubs/_draft/fft.py index 4a59392d5..f79aa155e 100644 --- a/src/array_api_stubs/_draft/fft.py +++ b/src/array_api_stubs/_draft/fft.py @@ -30,24 +30,22 @@ def fft( Computes the one-dimensional discrete Fourier transform. .. note:: - Applying the one-dimensional inverse discrete Fourier transform to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``ifft(fft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (length, axis, and normalization mode). + Applying the one-dimensional inverse discrete Fourier transform to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``ifft(fft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (number of elements, axis, and normalization mode). Parameters ---------- x: array - input array. Should have a floating-point data type. - n: int - length of the transformed axis of the output. If + input array. Should have a complex-valued floating-point data type. + n: Optional[int] + number of elements over which to compute the transform along the axis (dimension) specified by ``axis``. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``M``. - - ``n`` is greater than the length of the input array, the input array is zero-padded to length ``n``. - - ``n`` is less than the length of the input array, the input array is trimmed to length ``n``. - - ``n`` is not provided, the length of the transformed axis of the output must equal the length of the input along the axis specified by ``axis``. + - If ``n`` is greater than ``M``, the axis specified by ``axis`` must be zero-padded to size ``n``. + - If ``n`` is less than ``M``, the axis specified by ``axis`` must be trimmed to size ``n``. + - If ``n`` equals ``M``, all elements along the axis specified by ``axis`` must be used when computing the transform. Default: ``None``. axis: int - axis (dimension) over which to compute the Fourier transform. If not set, the last axis (dimension) is used. - - Default: ``-1``. + axis (dimension) of the input array over which to compute the transform. A valid ``axis`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an ``axis`` is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). Default: ``-1``. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -60,7 +58,7 @@ def fft( Returns ------- out: array - an array transformed along the axis (dimension) indicated by ``axis``. The returned array must have a complex floating-point data type determined by :ref:`type-promotion`. + an array transformed along the axis (dimension) specified by ``axis``. The returned array must have the same data type as ``x`` and must have the same shape as ``x``, except for the axis specified by ``axis`` which must have size ``n``. Notes ----- @@ -81,24 +79,22 @@ def ifft( Computes the one-dimensional inverse discrete Fourier transform. .. note:: - Applying the one-dimensional inverse discrete Fourier transform to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``ifft(fft(x)) == x``), provided that the transform and inverse transform are performed with the same (length, axis, and normalization mode). + Applying the one-dimensional inverse discrete Fourier transform to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``ifft(fft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (number of elements, axis, and normalization mode). Parameters ---------- x: array - input array. Should have a floating-point data type. - n: int - length of the transformed axis of the output. If + input array. Should have a complex-valued floating-point data type. + n: Optional[int] + number of elements over which to compute the transform along the axis (dimension) specified by ``axis``. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``M``. - - ``n`` is greater than the length of the input array, the input array is zero-padded to length ``n``. - - ``n`` is less than the length of the input array, the input array is trimmed to length ``n``. - - ``n`` is not provided, the length of the transformed axis of the output must equal the length of the input along the axis specified by ``axis``. + - If ``n`` is greater than ``M``, the axis specified by ``axis`` must be zero-padded to size ``n``. + - If ``n`` is less than ``M``, the axis specified by ``axis`` must be trimmed to size ``n``. + - If ``n`` equals ``M``, all elements along the axis specified by ``axis`` must be used when computing the transform. Default: ``None``. axis: int - axis (dimension) over which to compute the inverse Fourier transform. If not set, the last axis (dimension) is used. - - Default: ``-1``. + axis (dimension) of the input array over which to compute the transform. A valid ``axis`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an ``axis`` is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). Default: ``-1``. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -111,7 +107,7 @@ def ifft( Returns ------- out: array - an array transformed along the axis (dimension) indicated by ``axis``. The returned array must have a complex floating-point data type determined by :ref:`type-promotion`. + an array transformed along the axis (dimension) specified by ``axis``. The returned array must have the same data type as ``x`` and must have the same shape as ``x``, except for the axis specified by ``axis`` which must have size ``n``. Notes ----- @@ -124,8 +120,8 @@ def fftn( x: array, /, *, - s: Sequence[int] = None, - axes: Sequence[int] = None, + s: Optional[Sequence[int]] = None, + axes: Optional[Sequence[int]] = None, norm: Literal["backward", "ortho", "forward"] = "backward", ) -> array: """ @@ -137,24 +133,19 @@ def fftn( Parameters ---------- x: array - input array. Should have a floating-point data type. - s: Sequence[int] - size of each transformed axis of the output. If - - - ``s[i]`` is greater than the size of the input array along the corresponding axis (dimension) ``i``, the input array along the axis ``i`` is zero-padded to size ``s[i]``. - - ``s[i]`` is less than the size of the input array along a corresponding axis (dimension) ``i``, the input array along the axis ``i`` is trimmed to size ``s[i]``. - - ``s[i]`` is ``-1``, the whole input array along the axis ``i`` is used (no padding/trimming). - - ``s`` is not provided, the size of each transformed axis (dimension) in the output array must equal the size of the corresponding axis in the input array. - - If ``s`` is not ``None``, ``axes`` must not be ``None`` either, and ``s[i]`` corresponds to the size along the transformed axis specified by ``axes[i]``. + input array. Should have a complex-valued floating-point data type. + s: Optional[Sequence[int]] + number of elements over which to compute the transform along the axes (dimensions) specified by ``axes``. Let ``i`` be the index of the nth axis specified by ``axes`` and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers, such that, for all ``i``, ``s[i]`` equals ``M[i]``. - Default: ``None``. - axes: Sequence[int] - axes (dimensions) over which to compute the Fourier transform. If ``None``, all axes must be transformed. + - If ``s[i]`` is greater than ``M[i]``, axis ``i`` must be zero-padded to size ``s[i]``. + - If ``s[i]`` is less than ``M[i]``, axis ``i`` must be trimmed to size ``s[i]``. + - If ``s[i]`` equals ``M[i]`` or ``-1``, all elements along axis ``i`` must be used when computing the transform. - If ``s`` is specified, the corresponding ``axes`` to be transformed must be explicitly specified too. + If ``s`` is not ``None``, ``axes`` must not be ``None``. Default: ``None``. + axes: Optional[Sequence[int]] + axes (dimensions) over which to compute the transform. A valid axis must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). - Default: ``None``. + If ``s`` is provided, the corresponding ``axes`` to be transformed must also be provided. If ``axes`` is ``None``, the function must compute the transform over all axes. Default: ``None``. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -169,7 +160,7 @@ def fftn( Returns ------- out: array - an array transformed along the axes (dimension) indicated by ``axes``. The returned array must have a complex floating-point data type determined by :ref:`type-promotion`. + an array transformed along the axes (dimensions) specified by ``axes``. The returned array must have the same data type as ``x`` and must have the same shape as ``x``, except for the axes specified by ``axes`` which must have size ``s[i]``. Notes ----- @@ -182,8 +173,8 @@ def ifftn( x: array, /, *, - s: Sequence[int] = None, - axes: Sequence[int] = None, + s: Optional[Sequence[int]] = None, + axes: Optional[Sequence[int]] = None, norm: Literal["backward", "ortho", "forward"] = "backward", ) -> array: """ @@ -195,24 +186,19 @@ def ifftn( Parameters ---------- x: array - input array. Should have a floating-point data type. - s: Sequence[int] - size of each transformed axis of the output. If - - - ``s[i]`` is greater than the size of the input array along the corresponding axis (dimension) ``i``, the input array along the axis ``i`` is zero-padded to size ``s[i]``. - - ``s[i]`` is less than the size of the input array along a corresponding axis (dimension) ``i``, the input array along the axis ``i`` is trimmed to size ``s[i]``. - - ``s[i]`` is ``-1``, the whole input array along the axis ``i`` is used (no padding/trimming). - - ``s`` is not provided, the size of each transformed axis (dimension) in the output array must equal the size of the corresponding axis in the input array. - - If ``s`` is not ``None``, ``axes`` must not be ``None`` either, and ``s[i]`` corresponds to the size along the transformed axis specified by ``axes[i]``. + input array. Should have a complex-valued floating-point data type. + s: Optional[Sequence[int]] + number of elements over which to compute the transform along the axes (dimensions) specified by ``axes``. Let ``i`` be the index of the nth axis specified by ``axes`` and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers, such that, for all ``i``, ``s[i]`` equals ``M[i]``. - Default: ``None``. - axes: Sequence[int] - axes (dimensions) over which to compute the Fourier transform. If ``None``, all axes must be transformed. + - If ``s[i]`` is greater than ``M[i]``, axis ``i`` must be zero-padded to size ``s[i]``. + - If ``s[i]`` is less than ``M[i]``, axis ``i`` must be trimmed to size ``s[i]``. + - If ``s[i]`` equals ``M[i]`` or ``-1``, all elements along axis ``i`` must be used when computing the transform. - If ``s`` is specified, the corresponding ``axes`` to be transformed must be explicitly specified too. + If ``s`` is not ``None``, ``axes`` must not be ``None``. Default: ``None``. + axes: Optional[Sequence[int]] + axes (dimensions) over which to compute the transform. A valid axis must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). - Default: ``None``. + If ``s`` is provided, the corresponding ``axes`` to be transformed must also be provided. If ``axes`` is ``None``, the function must compute the transform over all axes. Default: ``None``. norm: Literal['backward', 'ortho', 'forward'] specify the normalization mode. Should be one of the following modes: @@ -227,7 +213,7 @@ def ifftn( Returns ------- out: array - an array transformed along the axes (dimension) indicated by ``axes``. The returned array must have a complex floating-point data type determined by :ref:`type-promotion`. + an array transformed along the axes (dimensions) specified by ``axes``. The returned array must have the same data type as ``x`` and must have the same shape as ``x``, except for the axes specified by ``axes`` which must have size ``s[i]``. Notes ----- @@ -248,24 +234,22 @@ def rfft( Computes the one-dimensional discrete Fourier transform for real-valued input. .. note:: - Applying the one-dimensional inverse discrete Fourier transform for real-valued input to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``irfft(rfft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (axis and normalization mode) and consistent length. + Applying the one-dimensional inverse discrete Fourier transform for real-valued input to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``irfft(rfft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (axis and and normalization mode) and consistent values for the number of elements over which to compute the transforms. Parameters ---------- x: array input array. Must have a real-valued floating-point data type. - n: int - length of the transformed axis of the **input**. If + n: Optional[int] + number of elements over which to compute the transform along the axis (dimension) specified by ``axis``. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``M``. - - ``n`` is greater than the length of the input array, the input array is zero-padded to length ``n``. - - ``n`` is less than the length of the input array, the input array is trimmed to length ``n``. - - ``n`` is not provided, the length of the transformed axis of the output must equal the length of the input along the axis specified by ``axis``. + - If ``n`` is greater than ``M``, the axis specified by ``axis`` must be zero-padded to size ``n``. + - If ``n`` is less than ``M``, the axis specified by ``axis`` must be trimmed to size ``n``. + - If ``n`` equals ``M``, all elements along the axis specified by ``axis`` must be used when computing the transform. Default: ``None``. axis: int - axis (dimension) over which to compute the Fourier transform. If not set, the last axis (dimension) is used. - - Default: ``-1``. + axis (dimension) of the input array over which to compute the transform. A valid ``axis`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an ``axis`` is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). Default: ``-1``. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -278,7 +262,7 @@ def rfft( Returns ------- out: array - an array transformed along the axis (dimension) indicated by ``axis``. The returned array must have a complex-valued floating-point data type determined by :ref:`type-promotion`. + an array transformed along the axis (dimension) specified by ``axis``. The returned array must have a complex floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``float64``, then the returned array must have a ``complex128`` data type). The returned array must have the same shape as ``x``, except for the axis specified by ``axis`` which must have size ``n//2 + 1``. Notes ----- @@ -299,24 +283,22 @@ def irfft( Computes the one-dimensional inverse of ``rfft`` for complex-valued input. .. note:: - Applying the one-dimensional inverse discrete Fourier transform for real-valued input to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``irfft(rfft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (axis and normalization mode) and consistent length. + Applying the one-dimensional inverse discrete Fourier transform for real-valued input to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``irfft(rfft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (axis and and normalization mode) and consistent values for the number of elements over which to compute the transforms. Parameters ---------- x: array input array. Should have a complex-valued floating-point data type. - n: int - length of the transformed axis of the **output**. If + n: Optional[int] + number of elements along the transformed axis (dimension) specified by ``axis`` in the **output array**. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``2*(M-1)``. - - ``n//2+1`` is greater than the length of the input array, the input array is zero-padded to length ``n//2+1``. - - ``n//2+1`` is less than the length of the input array, the input array is trimmed to length ``n//2+1``. - - ``n`` is not provided, the length of the transformed axis of the output must equal the length ``2*(m-1)``, where ``m`` is the length of the input along the axis specified by ``axis``. + - If ``n//2+1`` is greater than ``M``, the axis of the input array specified by ``axis`` must be zero-padded to size ``n//2+1``. + - If ``n//2+1`` is less than ``M``, the axis of the input array specified by ``axis`` must be trimmed to size ``n//2+1``. + - If ``n//2+1`` equals ``M``, all elements along the axis of the input array specified by ``axis`` must be used when computing the transform. Default: ``None``. axis: int - axis (dimension) over which to compute the inverse Fourier transform. If not set, the last axis (dimension) is used. - - Default: ``-1``. + axis (dimension) of the input array over which to compute the transform. A valid ``axis`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an ``axis`` is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). Default: ``-1``. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -329,11 +311,13 @@ def irfft( Returns ------- out: array - an array transformed along the axis (dimension) indicated by ``axis``. The returned array must have a real-valued floating-point data type determined by :ref:`type-promotion`. The length along the transformed axis is ``n`` (if given) or ``2*(m-1)`` (otherwise). + an array transformed along the axis (dimension) specified by ``axis``. The returned array must have a real-valued floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``complex128``, then the returned array must have a ``float64`` data type). The returned array must have the same shape as ``x``, except for the axis specified by ``axis`` which must have size ``n``. Notes ----- + - In order to return an array having an odd number of elements along the transformed axis, the function must be provided an odd integer for ``n``. + .. versionadded:: 2022.12 """ @@ -342,8 +326,8 @@ def rfftn( x: array, /, *, - s: Sequence[int] = None, - axes: Sequence[int] = None, + s: Optional[Sequence[int]] = None, + axes: Optional[Sequence[int]] = None, norm: Literal["backward", "ortho", "forward"] = "backward", ) -> array: """ @@ -356,23 +340,18 @@ def rfftn( ---------- x: array input array. Must have a real-valued floating-point data type. - s: Sequence[int] - size of each transformed axis of the **input**. If - - - ``s[i]`` is greater than the size of the input array along the corresponding axis (dimension) ``i``, the input array along the axis ``i`` is zero-padded to size ``s[i]``. - - ``s[i]`` is less than the size of the input array along a corresponding axis (dimension) ``i``, the input array along the axis ``i`` is trimmed to size ``s[i]``. - - ``s[i]`` is ``-1``, the whole input array along the axis ``i`` is used (no padding/trimming). - - ``s`` is not provided, the size of each transformed axis (dimension) in the output array must equal the size of the corresponding axis in the input array. + s: Optional[Sequence[int]] + number of elements over which to compute the transform along axes (dimensions) specified by ``axes``. Let ``i`` be the index of the nth axis specified by ``axes`` and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers, such that, for all ``i``, ``s[i]`` equals ``M[i]``. - If ``s`` is not ``None``, ``axes`` must not be ``None`` either, and ``s[i]`` corresponds to the size along the transformed axis specified by ``axes[i]``. + - If ``s[i]`` is greater than ``M[i]``, axis ``i`` must be zero-padded to size ``s[i]``. + - If ``s[i]`` is less than ``M[i]``, axis ``i`` must be trimmed to size ``s[i]``. + - If ``s[i]`` equals ``M[i]`` or ``-1``, all elements along axis ``i`` must be used when computing the transform. - Default: ``None``. - axes: Sequence[int] - axes (dimensions) over which to compute the Fourier transform. If ``None``, all axes must be transformed. - - If ``s`` is specified, the corresponding ``axes`` to be transformed must be explicitly specified too. + If ``s`` is not ``None``, ``axes`` must not be ``None``. Default: ``None``. + axes: Optional[Sequence[int]] + axes (dimensions) over which to compute the transform. A valid axis must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). - Default: ``None``. + If ``s`` is provided, the corresponding ``axes`` to be transformed must also be provided. If ``axes`` is ``None``, the function must compute the transform over all axes. Default: ``None``. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -387,7 +366,7 @@ def rfftn( Returns ------- out: array - an array transformed along the axes (dimension) indicated by ``axes``. The returned array must have a complex-valued floating-point data type determined by :ref:`type-promotion`. + an array transformed along the axes (dimension) specified by ``axes``. The returned array must have a complex floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``float64``, then the returned array must have a ``complex128`` data type). The returned array must have the same shape as ``x``, except for the last transformed axis which must have size ``s[-1]//2 + 1`` and the remaining transformed axes which must have size ``s[i]``. Notes ----- @@ -400,8 +379,8 @@ def irfftn( x: array, /, *, - s: Sequence[int] = None, - axes: Sequence[int] = None, + s: Optional[Sequence[int]] = None, + axes: Optional[Sequence[int]] = None, norm: Literal["backward", "ortho", "forward"] = "backward", ) -> array: """ @@ -414,23 +393,18 @@ def irfftn( ---------- x: array input array. Should have a complex-valued floating-point data type. - s: Sequence[int] - size of each transformed axis of the **output**. ``n=s[i]`` is also the number of input points used along the axis (dimension) ``i``, except for the last axis, where ``n=s[-1]//2+1`` points of the input are used. If - - - ``n`` is greater than the size of the input array along the corresponding axis (dimension) ``i``, the input array along the axis ``i`` is zero-padded to size ``n``. - - ``n`` is less than the size of the input array along the corresponding axis (dimension) ``i``, the input array along the axis ``i`` is trimmed to size ``n``. - - ``s[i]`` is ``-1``, the whole input array along the axis ``i`` is used (no padding/trimming). - - ``s`` is not provided, the size of each transformed axis (dimension) in the output array must equal the size of the corresponding axis in the input array, except for the last axis which is trimmed to ``2*(m-1)``, where ``m`` is the length of the input along the axis. - - If ``s`` is not ``None``, ``axes`` must not be ``None`` either, and ``s[i]`` corresponds to the size along the transformed axis specified by ``axes[i]``. + s: Optional[Sequence[int]] + number of elements along the transformed axes (dimensions) specified by ``axes`` in the **output array**. Let ``i`` be the index of the nth axis specified by ``axes`` and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers, such that, for all ``i``, ``s[i]`` equals ``M[i]``, except for the last transformed axis in which ``s[i]`` equals ``2*(M[i]-1)``. For each ``i``, let ``n`` equal ``s[i]``, except for the last transformed axis in which ``n`` equals ``s[i]//2+1``. - Default: ``None``. - axes: Sequence[int] - axes (dimensions) over which to compute the Fourier transform. If ``None``, all axes must be transformed. + - If ``n`` is greater than ``M[i]``, axis ``i`` of the input array must be zero-padded to size ``n``. + - If ``n`` is less than ``M[i]``, axis ``i`` of the input array must be trimmed to size ``n``. + - If ``n`` equals ``M[i]`` or ``-1``, all elements along axis ``i`` of the input array must be used when computing the transform. - If ``s`` is specified, the corresponding ``axes`` to be transformed must be explicitly specified too. + If ``s`` is not ``None``, ``axes`` must not be ``None``. Default: ``None``. + axes: Optional[Sequence[int]] + axes (dimensions) over which to compute the transform. A valid axis must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). - Default: ``None``. + If ``s`` is provided, the corresponding ``axes`` to be transformed must also be provided. If ``axes`` is ``None``, the function must compute the transform over all axes. Default: ``None``. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -445,11 +419,13 @@ def irfftn( Returns ------- out: array - an array transformed along the axes (dimension) indicated by ``axes``. The returned array must have a real-valued floating-point data type determined by :ref:`type-promotion`. The length along the last transformed axis is ``s[-1]`` (if given) or ``2*(m - 1)`` (otherwise), and all other axes ``s[i]``. + an array transformed along the axes (dimension) specified by ``axes``. The returned array must have a real-valued floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``complex128``, then the returned array must have a ``float64`` data type). The returned array must have the same shape as ``x``, except for the transformed axes which must have size ``s[i]``. Notes ----- + - In order to return an array having an odd number of elements along the last transformed axis, the function must be provided an odd integer for ``s[-1]``. + .. versionadded:: 2022.12 """ @@ -468,19 +444,17 @@ def hfft( Parameters ---------- x: array - input array. Should have a floating-point data type. - n: int - length of the transformed axis of the **output**. If + input array. Should have a complex-valued floating-point data type. + n: Optional[int] + number of elements along the transformed axis (dimension) specified by ``axis`` in the **output array**. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``2*(M-1)``. - - ``n//2+1`` is greater than the length of the input array, the input array is zero-padded to length ``n//2+1``. - - ``n//2+1`` is less than the length of the input array, the input array is trimmed to length ``n//2+1``. - - ``n`` is not provided, the length of the transformed axis of the output must equal the length ``2*(m-1)``, where ``m`` is the length of the input along the axis specified by ``axis``. + - If ``n//2+1`` is greater than ``M``, the axis of the input array specified by ``axis`` must be zero-padded to length ``n//2+1``. + - If ``n//2+1`` is less than ``M``, the axis of the input array specified by ``axis`` must be trimmed to size ``n//2+1``. + - If ``n//2+1`` equals ``M``, all elements along the axis of the input array specified by ``axis`` must be used when computing the transform. Default: ``None``. axis: int - axis (dimension) over which to compute the Fourier transform. If not set, the last axis (dimension) is used. - - Default: ``-1``. + axis (dimension) of the input array over which to compute the transform. A valid ``axis`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an ``axis`` is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). Default: ``-1``. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -493,7 +467,7 @@ def hfft( Returns ------- out: array - an array transformed along the axis (dimension) indicated by ``axis``. The returned array must have a real-valued floating-point data type determined by :ref:`type-promotion`. + an array transformed along the axis (dimension) specified by ``axis``. The returned array must have a real-valued floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``complex128``, then the returned array must have a ``float64`` data type). The returned array must have the same shape as ``x``, except for the axis specified by ``axis`` which must have size ``n``. Notes ----- @@ -517,18 +491,16 @@ def ihfft( ---------- x: array input array. Must have a real-valued floating-point data type. - n: int - length of the transformed axis of the **input**. If + n: Optional[int] + number of elements over which to compute the transform along the axis (dimension) specified by ``axis``. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``M``. - - ``n`` is greater than the length of the input array, the input array is zero-padded to length ``n``. - - ``n`` is less than the length of the input array, the input array is trimmed to length ``n``. - - ``n`` is not provided, the length of the transformed axis of the output must equal the length of the input along the axis specified by ``axis``. + - If ``n`` is greater than ``M``, the axis specified by ``axis`` must be zero-padded to size ``n``. + - If ``n`` is less than ``M``, the axis specified by ``axis`` must be trimmed to size ``n``. + - If ``n`` equals ``M``, all elements along the axis specified by ``axis`` must be used when computing the transform. Default: ``None``. axis: int - axis (dimension) over which to compute the Fourier transform. If not set, the last axis (dimension) is used. - - Default: ``-1``. + axis (dimension) of the input array over which to compute the transform. A valid ``axis`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an ``axis`` is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). Default: ``-1``. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -541,7 +513,7 @@ def ihfft( Returns ------- out: array - an array transformed along the axis (dimension) indicated by ``axis``. The returned array must have a complex-valued floating-point data type determined by :ref:`type-promotion`. + an array transformed along the axis (dimension) specified by ``axis``. The returned array must have a complex floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``float64``, then the returned array must have a ``complex128`` data type). The returned array must have the same shape as ``x``, except for the axis specified by ``axis`` which must have size ``n//2 + 1``. Notes ----- @@ -552,9 +524,9 @@ def ihfft( def fftfreq(n: int, /, *, d: float = 1.0, device: Optional[device] = None) -> array: """ - Returns the discrete Fourier transform sample frequencies. + Computes the discrete Fourier transform sample frequencies. - For a Fourier transform of length ``n`` and length unit of ``d`` the frequencies are described as: + For a Fourier transform of length ``n`` and length unit of ``d``, the frequencies are described as: .. code-block:: @@ -573,7 +545,7 @@ def fftfreq(n: int, /, *, d: float = 1.0, device: Optional[device] = None) -> ar Returns ------- out: array - an array of length ``n`` containing the sample frequencies. The returned array must have a real-valued floating-point data type determined by :ref:`type-promotion`. + an array of shape ``(n,)`` containing the sample frequencies. The returned array must have the default real-valued floating-point data type. Notes ----- @@ -584,9 +556,9 @@ def fftfreq(n: int, /, *, d: float = 1.0, device: Optional[device] = None) -> ar def rfftfreq(n: int, /, *, d: float = 1.0, device: Optional[device] = None) -> array: """ - Returns the discrete Fourier transform sample frequencies (for ``rfft`` and ``irfft``). + Computes the discrete Fourier transform sample frequencies (for ``rfft`` and ``irfft``). - For a Fourier transform of length ``n`` and length unit of ``d`` the frequencies are described as: + For a Fourier transform of length ``n`` and length unit of ``d``, the frequencies are described as: .. code-block:: @@ -607,7 +579,7 @@ def rfftfreq(n: int, /, *, d: float = 1.0, device: Optional[device] = None) -> a Returns ------- out: array - an array of length ``n//2+1`` containing the sample frequencies. The returned array must have a real-valued floating-point data type determined by :ref:`type-promotion`. + an array of shape ``(n//2+1,)`` containing the sample frequencies. The returned array must have the default real-valued floating-point data type. Notes ----- @@ -616,9 +588,9 @@ def rfftfreq(n: int, /, *, d: float = 1.0, device: Optional[device] = None) -> a """ -def fftshift(x: array, /, *, axes: Union[int, Sequence[int]] = None) -> array: +def fftshift(x: array, /, *, axes: Optional[Union[int, Sequence[int]]] = None) -> array: """ - Shift the zero-frequency component to the center of the spectrum. + Shifts the zero-frequency component to the center of the spectrum. This function swaps half-spaces for all axes (dimensions) specified by ``axes``. @@ -629,13 +601,13 @@ def fftshift(x: array, /, *, axes: Union[int, Sequence[int]] = None) -> array: ---------- x: array input array. Should have a floating-point data type. - axes: Union[int, Sequence[int]] + axes: Optional[Union[int, Sequence[int]]] axes over which to shift. If ``None``, the function must shift all axes. Default: ``None``. Returns ------- out: array - the shifted array. The returned array must have the same data type as ``x``. + the shifted array. The returned array must have the same data type and shape as ``x``. Notes ----- @@ -644,7 +616,9 @@ def fftshift(x: array, /, *, axes: Union[int, Sequence[int]] = None) -> array: """ -def ifftshift(x: array, /, *, axes: Union[int, Sequence[int]] = None) -> array: +def ifftshift( + x: array, /, *, axes: Optional[Union[int, Sequence[int]]] = None +) -> array: """ Inverse of ``fftshift``. @@ -655,13 +629,13 @@ def ifftshift(x: array, /, *, axes: Union[int, Sequence[int]] = None) -> array: ---------- x: array input array. Should have a floating-point data type. - axes: Union[int, Sequence[int]] + axes: Optional[Union[int, Sequence[int]]] axes over which to perform the inverse shift. If ``None``, the function must shift all axes. Default: ``None``. Returns ------- out: array - the shifted array. The returned array must have the same data type as ``x``. + the shifted array. The returned array must have the same data type and shape as ``x``. Notes ----- From 425e9eb4eeeaf9057b5f2e93168ff377ddb9775f Mon Sep 17 00:00:00 2001 From: Leo Fang Date: Thu, 8 Feb 2024 16:17:52 -0500 Subject: [PATCH 11/57] Fix typo in FFT guidance PR-URL: https://github.com/data-apis/array-api/pull/743 Reviewed-by: Athan Reines Ref: https://github.com/data-apis/array-api/pull/720 --- src/array_api_stubs/_2022_12/fft.py | 4 ++-- src/array_api_stubs/_draft/fft.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/array_api_stubs/_2022_12/fft.py b/src/array_api_stubs/_2022_12/fft.py index f79aa155e..901b8407b 100644 --- a/src/array_api_stubs/_2022_12/fft.py +++ b/src/array_api_stubs/_2022_12/fft.py @@ -234,7 +234,7 @@ def rfft( Computes the one-dimensional discrete Fourier transform for real-valued input. .. note:: - Applying the one-dimensional inverse discrete Fourier transform for real-valued input to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``irfft(rfft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (axis and and normalization mode) and consistent values for the number of elements over which to compute the transforms. + Applying the one-dimensional inverse discrete Fourier transform for real-valued input to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``irfft(rfft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (axis and normalization mode) and consistent values for the number of elements over which to compute the transforms. Parameters ---------- @@ -283,7 +283,7 @@ def irfft( Computes the one-dimensional inverse of ``rfft`` for complex-valued input. .. note:: - Applying the one-dimensional inverse discrete Fourier transform for real-valued input to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``irfft(rfft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (axis and and normalization mode) and consistent values for the number of elements over which to compute the transforms. + Applying the one-dimensional inverse discrete Fourier transform for real-valued input to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``irfft(rfft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (axis and normalization mode) and consistent values for the number of elements over which to compute the transforms. Parameters ---------- diff --git a/src/array_api_stubs/_draft/fft.py b/src/array_api_stubs/_draft/fft.py index f79aa155e..901b8407b 100644 --- a/src/array_api_stubs/_draft/fft.py +++ b/src/array_api_stubs/_draft/fft.py @@ -234,7 +234,7 @@ def rfft( Computes the one-dimensional discrete Fourier transform for real-valued input. .. note:: - Applying the one-dimensional inverse discrete Fourier transform for real-valued input to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``irfft(rfft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (axis and and normalization mode) and consistent values for the number of elements over which to compute the transforms. + Applying the one-dimensional inverse discrete Fourier transform for real-valued input to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``irfft(rfft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (axis and normalization mode) and consistent values for the number of elements over which to compute the transforms. Parameters ---------- @@ -283,7 +283,7 @@ def irfft( Computes the one-dimensional inverse of ``rfft`` for complex-valued input. .. note:: - Applying the one-dimensional inverse discrete Fourier transform for real-valued input to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``irfft(rfft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (axis and and normalization mode) and consistent values for the number of elements over which to compute the transforms. + Applying the one-dimensional inverse discrete Fourier transform for real-valued input to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``irfft(rfft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (axis and normalization mode) and consistent values for the number of elements over which to compute the transforms. Parameters ---------- From 83420d21394e92b2fc92313f789f7b3d8c0c8c93 Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Fri, 9 Feb 2024 01:23:41 +0100 Subject: [PATCH 12/57] Add versioning support to DLPack APIs (#602) * Add versioning support to DLPack APIs xref https://github.com/dmlc/dlpack/issues/116 * Address review comment, replace ">=2 years" by "from March 2025" * nit: re-order * improvements & fixes * Satisfy linter * Satisfy linter * Update src/array_api_stubs/_draft/array_object.py Co-authored-by: Sebastian Berg --------- Co-authored-by: Leo Fang Co-authored-by: Athan Co-authored-by: Sebastian Berg --- src/array_api_stubs/_draft/array_object.py | 102 +++++++++++++----- .../_draft/creation_functions.py | 5 + 2 files changed, 83 insertions(+), 24 deletions(-) diff --git a/src/array_api_stubs/_draft/array_object.py b/src/array_api_stubs/_draft/array_object.py index 600e947e0..9f8be2479 100644 --- a/src/array_api_stubs/_draft/array_object.py +++ b/src/array_api_stubs/_draft/array_object.py @@ -288,7 +288,11 @@ def __complex__(self: array, /) -> complex: """ def __dlpack__( - self: array, /, *, stream: Optional[Union[int, Any]] = None + self: array, + /, + *, + stream: Optional[Union[int, Any]] = None, + max_version: Optional[tuple[int, int]] = None, ) -> PyCapsule: """ Exports the array for consumption by :func:`~array_api.from_dlpack` as a DLPack capsule. @@ -298,20 +302,14 @@ def __dlpack__( self: array array instance. stream: Optional[Union[int, Any]] - for CUDA and ROCm, a Python integer representing a pointer to a stream, on devices that support streams. ``stream`` is provided by the consumer to the producer to instruct the producer to ensure that operations can safely be performed on the array (e.g., by inserting a dependency between streams via "wait for event"). The pointer must be a positive integer or ``-1``. If ``stream`` is ``-1``, the value may be used by the consumer to signal "producer must not perform any synchronization". The ownership of the stream stays with the consumer. On CPU and other device types without streams, only ``None`` is accepted. - - For other device types which do have a stream, queue or similar synchronization mechanism, the most appropriate type to use for ``stream`` is not yet determined. E.g., for SYCL one may want to use an object containing an in-order ``cl::sycl::queue``. This is allowed when libraries agree on such a convention, and may be standardized in a future version of this API standard. - - - .. note:: - Support for a ``stream`` value other than ``None`` is optional and implementation-dependent. + for CUDA and ROCm, a Python integer representing a pointer to a stream, on devices that support streams. ``stream`` is provided by the consumer to the producer to instruct the producer to ensure that operations can safely be performed on the array (e.g., by inserting a dependency between streams via "wait for event"). The pointer must be an integer larger than or equal to ``-1`` (see below for allowed values on each platform). If ``stream`` is ``-1``, the value may be used by the consumer to signal "producer must not perform any synchronization". The ownership of the stream stays with the consumer. On CPU and other device types without streams, only ``None`` is accepted. + For other device types which do have a stream, queue, or similar synchronization/ordering mechanism, the most appropriate type to use for ``stream`` is not yet determined. E.g., for SYCL one may want to use an object containing an in-order ``cl::sycl::queue``. This is allowed when libraries agree on such a convention, and may be standardized in a future version of this API standard. - Device-specific notes: + .. note:: + Support for a ``stream`` value other than ``None`` is optional and implementation-dependent. - - .. admonition:: CUDA - :class: note + Device-specific values of ``stream`` for CUDA: - ``None``: producer must assume the legacy default stream (default). - ``1``: the legacy default stream. @@ -319,24 +317,28 @@ def __dlpack__( - ``> 2``: stream number represented as a Python integer. - ``0`` is disallowed due to its ambiguity: ``0`` could mean either ``None``, ``1``, or ``2``. - - .. admonition:: ROCm - :class: note + Device-specific values of ``stream`` for ROCm: - ``None``: producer must assume the legacy default stream (default). - ``0``: the default stream. - ``> 2``: stream number represented as a Python integer. - Using ``1`` and ``2`` is not supported. - - .. admonition:: Tip - :class: important - - It is recommended that implementers explicitly handle streams. If - they use the legacy default stream, specifying ``1`` (CUDA) or ``0`` - (ROCm) is preferred. ``None`` is a safe default for developers who do - not want to think about stream handling at all, potentially at the - cost of more synchronization than necessary. + .. admonition:: Tip + :class: important + + It is recommended that implementers explicitly handle streams. If + they use the legacy default stream, specifying ``1`` (CUDA) or ``0`` + (ROCm) is preferred. ``None`` is a safe default for developers who do + not want to think about stream handling at all, potentially at the + cost of more synchronizations than necessary. + max_version: Optional[tuple[int, int]] + The maximum DLPack version that the *consumer* (i.e., the caller of + ``__dlpack__``) supports, in the form of a 2-tuple ``(major, minor)``. + This method may return a capsule of version ``max_version`` (recommended + if it does support that), or of a different version. + This means the consumer must verify the version even when + `max_version` is passed. Returns ------- @@ -353,9 +355,61 @@ def __dlpack__( Notes ----- + The DLPack version scheme is SemVer, where the major DLPack versions + represent ABI breaks, and minor versions represent ABI-compatible additions + (e.g., new enum values for new data types or device types). + + The ``max_version`` keyword was introduced in v2023.12, and goes + together with the ``DLManagedTensorVersioned`` struct added in DLPack + 1.0. This keyword may not be used by consumers until a later time after + introduction, because producers may implement the support at a different + point in time. + + It is recommended for the producer to use this logic in the implementation + of ``__dlpack__``: + + .. code:: python + + if max_version is None: + # Keep and use the DLPack 0.X implementation + # Note: from March 2025 onwards (but ideally as late as + # possible), it's okay to raise BufferError here + else: + # We get to produce `DLManagedTensorVersioned` now. Note that + # our_own_dlpack_version is the max version that the *producer* + # supports and fills in the `DLManagedTensorVersioned::version` + # field + if max_version >= our_own_dlpack_version: + # Consumer understands us, just return a Capsule with our max version + elif max_version[0] == our_own_dlpack_version[0]: + # major versions match, we should still be fine here - + # return our own max version + else: + # if we're at a higher major version internally, did we + # keep an implementation of the older major version around? + # For example, if the producer is on DLPack 1.x and the consumer + # is 0.y, can the producer still export a capsule containing + # DLManagedTensor and not DLManagedTensorVersioned? + # If so, use that. Else, the producer should raise a BufferError + # here to tell users that the consumer's max_version is too + # old to allow the data exchange to happen. + + And this logic for the consumer in ``from_dlpack``: + + .. code:: python + + try: + x.__dlpack__(max_version=(1, 0)) + # if it succeeds, store info from the capsule named "dltensor_versioned", + # and need to set the name to "used_dltensor_versioned" when we're done + except TypeError: + x.__dlpack__() .. versionchanged:: 2022.12 Added BufferError. + + .. versionchanged:: 2023.12 + Added the ``max_version`` keyword. """ def __dlpack_device__(self: array, /) -> Tuple[Enum, int]: diff --git a/src/array_api_stubs/_draft/creation_functions.py b/src/array_api_stubs/_draft/creation_functions.py index 69733a646..d0f967717 100644 --- a/src/array_api_stubs/_draft/creation_functions.py +++ b/src/array_api_stubs/_draft/creation_functions.py @@ -246,6 +246,11 @@ def from_dlpack(x: object, /) -> array: If the ``__dlpack__`` and ``__dlpack_device__`` methods are not present on the input array. This may happen for libraries that are never able to export their data with DLPack. + + Notes + ----- + See :meth:`array.__dlpack__` for implementation suggestions for `from_dlpack` in + order to handle DLPack versioning correctly. """ From a902944642a553b287a226711d380676c070940e Mon Sep 17 00:00:00 2001 From: Athan Date: Fri, 9 Feb 2024 00:17:51 -0800 Subject: [PATCH 13/57] Add note regarding copy behavior when moving to same device (#742) Closes: https://github.com/data-apis/array-api/issues/645 --- src/array_api_stubs/_draft/array_object.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/array_api_stubs/_draft/array_object.py b/src/array_api_stubs/_draft/array_object.py index 9f8be2479..5b412685e 100644 --- a/src/array_api_stubs/_draft/array_object.py +++ b/src/array_api_stubs/_draft/array_object.py @@ -1140,8 +1140,11 @@ def to_device( an array with the same data and data type as ``self`` and located on the specified ``device``. - .. 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. + Notes + ----- + + - When a provided ``device`` object corresponds to the same device on which an array instance resides, implementations may choose to perform an explicit copy or return ``self``. + - If ``stream`` is provided, 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 array library's documentation. """ From 2404c9968726f11be30047e903b613fa2031b9e9 Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Tue, 13 Feb 2024 07:41:16 +0100 Subject: [PATCH 14/57] docs: reorganize data type specification guidance PR-URL: https://github.com/data-apis/array-api/pull/745 Co-authored-by: Athan Reines Reviewed-by: Athan Reines --- spec/draft/API_specification/data_types.rst | 198 ++++++++------------ 1 file changed, 78 insertions(+), 120 deletions(-) diff --git a/spec/draft/API_specification/data_types.rst b/spec/draft/API_specification/data_types.rst index a9be88181..5987dd322 100644 --- a/spec/draft/API_specification/data_types.rst +++ b/spec/draft/API_specification/data_types.rst @@ -5,109 +5,83 @@ Data Types Array API specification for supported data types. -A conforming implementation of the array API standard must provide and support the following data types. +A conforming implementation of the array API standard must provide and support +the following data types ("dtypes") in its array object, and as data type +objects in its main namespace under the specified names: + ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| dtype object | description | ++==============+============================================================================================================================================================================================+ +| bool | Boolean (``True`` or ``False``). | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| int8 | An 8-bit signed integer whose values exist on the interval ``[-128, +127]``. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| int16 | A 16-bit signed integer whose values exist on the interval ``[−32,767, +32,767]``. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| int32 | A 32-bit signed integer whose values exist on the interval ``[−2,147,483,647, +2,147,483,647]``. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| int64 | A 64-bit signed integer whose values exist on the interval ``[−9,223,372,036,854,775,807, +9,223,372,036,854,775,807]``. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| uint8 | An 8-bit unsigned integer whose values exist on the interval ``[0, +255]``. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| uint16 | A 16-bit unsigned integer whose values exist on the interval ``[0, +65,535]``. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| uint32 | A 32-bit unsigned integer whose values exist on the interval ``[0, +4,294,967,295]``. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| uint64 | A 64-bit unsigned integer whose values exist on the interval ``[0, +18,446,744,073,709,551,615]``. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| float32 | IEEE 754 single-precision (32-bit) binary floating-point number (see IEEE 754-2019). | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| float64 | IEEE 754 double-precision (64-bit) binary floating-point number (see IEEE 754-2019). | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| complex64 | Single-precision (64-bit) complex floating-point number whose real and imaginary components must be IEEE 754 single-precision (32-bit) binary floating-point numbers (see IEEE 754-2019). | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| complex128 | Double-precision (128-bit) complex floating-point number whose real and imaginary components must be IEEE 754 double-precision (64-bit) binary floating-point numbers (see IEEE 754-2019). | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +Data type objects must have the following methods (no attributes are required): -bool ----- - -Boolean (``True`` or ``False``). - -int8 ----- - -An 8-bit signed integer whose values exist on the interval ``[-128, +127]``. - -int16 ------ - -A 16-bit signed integer whose values exist on the interval ``[−32,767, +32,767]``. - -int32 ------ - -A 32-bit signed integer whose values exist on the interval ``[−2,147,483,647, +2,147,483,647]``. - -int64 ------ - -A 64-bit signed integer whose values exist on the interval ``[−9,223,372,036,854,775,807, +9,223,372,036,854,775,807]``. - -uint8 ------ - -An 8-bit unsigned integer whose values exist on the interval ``[0, +255]``. - -uint16 ------- - -A 16-bit unsigned integer whose values exist on the interval ``[0, +65,535]``. - -uint32 ------- - -A 32-bit unsigned integer whose values exist on the interval ``[0, +4,294,967,295]``. - -uint64 ------- - -A 64-bit unsigned integer whose values exist on the interval ``[0, +18,446,744,073,709,551,615]``. - -float32 -------- - -IEEE 754 single-precision (32-bit) binary floating-point number (see IEEE 754-2019). - -float64 -------- +.. + NOTE: please keep the functions in alphabetical order -IEEE 754 double-precision (64-bit) binary floating-point number (see IEEE 754-2019). +.. currentmodule:: array_api.data_types -complex64 ---------- +.. autosummary:: + :toctree: generated + :template: method.rst -Single-precision (64-bit) complex floating-point number whose real and imaginary components must be IEEE 754 single-precision (32-bit) binary floating-point numbers (see IEEE 754-2019). + __eq__ -complex128 ----------- -Double-precision (128-bit) complex floating-point number whose real and imaginary components must be IEEE 754 double-precision (64-bit) binary floating-point numbers (see IEEE 754-2019). +.. note:: + A conforming implementation of the array API standard may provide and + support additional data types beyond those described in this specification. + It may also support additional methods and attributes on dtype objects. .. note:: IEEE 754-2019 requires support for subnormal (a.k.a., denormal) numbers, which are useful for supporting gradual underflow. However, hardware support for subnormal numbers is not universal, and many platforms (e.g., accelerators) and compilers support toggling denormals-are-zero (DAZ) and/or flush-to-zero (FTZ) behavior to increase performance and to guard against timing attacks. Accordingly, subnormal behavior is left unspecified and, thus, implementation-defined. Conforming implementations may vary in their support for subnormal numbers. -.. note:: - A conforming implementation of the array API standard may provide and support additional data types beyond those described in this specification. - -.. _data-type-objects: -Data Type Objects ------------------ +Use of data type objects +------------------------ -Data types ("dtypes") are objects which are used as ``dtype`` specifiers in functions and methods (e.g., ``zeros((2, 3), dtype=float32)``). +Data type objects are used as ``dtype`` specifiers in functions and methods +(e.g., ``zeros((2, 3), dtype=float32)``), accessible as ``.dtype`` attribute on +arrays, and used in various casting and introspection functions (e.g., +``isdtype(x.dtype, 'integral')``). -.. note:: - A conforming implementation may add additional methods or attributes to data type objects beyond those described in this specification. +``dtype`` keywords in functions specify the data type of arrays returned from +functions or methods. ``dtype`` keywords are not required to affect the data +type used for intermediate calculations or results (e.g., implementors are free +to use a higher-precision data type when accumulating values for reductions, as +long as the returned array has the specified data type). .. note:: Implementations may provide other ways to specify data types (e.g., ``zeros((2, 3), dtype='f4')``) which are not described in this specification; however, in order to ensure portability, array library consumers are recommended to use data type objects as provided by specification conforming array libraries. -A conforming implementation of the array API standard must provide and support data type objects having the following attributes and methods. - -Methods -~~~~~~~ - -.. - NOTE: please keep the functions in alphabetical order - -.. currentmodule:: array_api.data_types - -.. autosummary:: - :toctree: generated - :template: method.rst - - __eq__ +See :ref:`type-promotion` for specification guidance describing the rules governing the interaction of two or more data types or data type objects. .. _data-type-defaults: @@ -138,6 +112,7 @@ the library should clearly warn about this in its documentation. .. note:: The default data types should be clearly defined in a conforming library's documentation. + .. _data-type-categories: Data Type Categories @@ -145,41 +120,24 @@ Data Type Categories For the purpose of organizing functions within this specification, the following data type categories are defined. -.. note:: - Conforming libraries are not required to organize data types according to these categories. These categories are only intended for use within this specification. - - -Numeric Data Types -~~~~~~~~~~~~~~~~~~ - -``int8``, ``int16``, ``int32``, ``int64``, ``uint8``, ``uint16``, ``uint32``, ``uint64``, ``float32``, ``float64``, ``complex64``, and ``complex128``. - -Real-valued Data Types -~~~~~~~~~~~~~~~~~~~~~~ - -``int8``, ``int16``, ``int32``, ``int64``, ``uint8``, ``uint16``, ``uint32``, ``uint64``, ``float32``, and ``float64``. - -Integer Data Types -~~~~~~~~~~~~~~~~~~ - -``int8``, ``int16``, ``int32``, ``int64``, ``uint8``, ``uint16``, ``uint32``, and ``uint64``. - -Floating-point Data Types -~~~~~~~~~~~~~~~~~~~~~~~~~ ++----------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ +| data type category | dtypes | ++============================+========================================================================================================================================================+ +| Numeric | ``int8``, ``int16``, ``int32``, ``int64``, ``uint8``, ``uint16``, ``uint32``, ``uint64``, ``float32``, ``float64``, ``complex64``, and ``complex128``. | ++----------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Real-valued | ``int8``, ``int16``, ``int32``, ``int64``, ``uint8``, ``uint16``, ``uint32``, ``uint64``, ``float32``, and ``float64``. | ++----------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Integer | ``int8``, ``int16``, ``int32``, ``int64``, ``uint8``, ``uint16``, ``uint32``, and ``uint64``. | ++----------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Floating-point | ``float32``, ``float64``, ``complex64``, and ``complex128``. | ++----------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Real-valued floating-point | ``float32`` and ``float64``. | ++----------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Complex floating-point | ``complex64`` and ``complex128``. | ++----------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Boolean | ``bool``. | ++----------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ -``float32``, ``float64``, ``complex64``, and ``complex128``. -Real-valued Floating-point Data Types -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``float32`` and ``float64``. - -Complex Floating-point Data Types -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``complex64`` and ``complex128``. - -Boolean Data Types -~~~~~~~~~~~~~~~~~~ - -``bool``. +.. note:: + Conforming libraries are not required to organize data types according to these categories. These categories are only intended for use within this specification. From 72740507b478f014fb65156198534f8046fbe37f Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Tue, 13 Feb 2024 09:22:53 +0100 Subject: [PATCH 15/57] feat!: change default promotion behavior in summation APIs This commit modifies type promotion behavior in `sum`, `prod`, `cumulative_sum`, and `linalg.trace` when the input array has a floating-point data type. Previously, the specification required that conforming implementations upcast to the default floating-point data type when the input array data type was of a lower precision. This commit revises that guidance to require conforming libraries return an array having the same data type as the input array. This revision stems from feedback from implementing libraries, where the current status quo matches the changes in this commit, with little desire to change. As such, the specification is amended to match this reality. Closes: https://github.com/data-apis/array-api/issues/731 PR-URL: https://github.com/data-apis/array-api/pull/744 Co-authored-by: Athan Reines Reviewed-by: Athan Reines --- src/array_api_stubs/_draft/linalg.py | 15 ++---- .../_draft/statistical_functions.py | 50 ++++++------------- 2 files changed, 19 insertions(+), 46 deletions(-) diff --git a/src/array_api_stubs/_draft/linalg.py b/src/array_api_stubs/_draft/linalg.py index d05b53a9f..c66258c7b 100644 --- a/src/array_api_stubs/_draft/linalg.py +++ b/src/array_api_stubs/_draft/linalg.py @@ -742,19 +742,12 @@ def trace(x: array, /, *, offset: int = 0, dtype: Optional[dtype] = None) -> arr Default: ``0``. dtype: Optional[dtype] - data type of the returned array. If ``None``, + data type of the returned array. If ``None``, the returned array must have the same data type as ``x``, unless ``x`` has an integer data type supporting a smaller range of values than the default integer data type (e.g., ``x`` has an ``int16`` or ``uint32`` data type and the default integer data type is ``int64``). In those latter cases: - - if the default data type corresponding to the data type "kind" (integer, real-valued floating-point, or complex floating-point) of ``x`` has a smaller range of values than the data type of ``x`` (e.g., ``x`` has data type ``int64`` and the default data type is ``int32``, or ``x`` has data type ``uint64`` and the default data type is ``int64``), the returned array must have the same data type as ``x``. - - if the default data type corresponding to the data type "kind" of ``x`` has the same or a larger range of values than the data type of ``x``, - - if ``x`` has a real-valued floating-point data type, the returned array must have the default real-valued floating-point data type. - - if ``x`` has a complex floating-point data type, the returned array must have the default complex floating-point data type. - - if ``x`` has a signed integer data type (e.g., ``int16``), the returned array must have the default integer data type. - - if ``x`` has an unsigned integer data type (e.g., ``uint16``), the returned array must have an unsigned integer data type having the same number of bits as the default integer data type (e.g., if the default integer data type is ``int32``, the returned array must have a ``uint32`` data type). + - if ``x`` has a signed integer data type (e.g., ``int16``), the returned array must have the default integer data type. + - if ``x`` has an unsigned integer data type (e.g., ``uint16``), the returned array must have an unsigned integer data type having the same number of bits as the default integer data type (e.g., if the default integer data type is ``int32``, the returned array must have a ``uint32`` data type). - If the data type (either specified or resolved) differs from the data type of ``x``, the input array should be cast to the specified data type before computing the sum. Default: ``None``. - - .. note:: - keyword argument is intended to help prevent data type overflows. + If the data type (either specified or resolved) differs from the data type of ``x``, the input array should be cast to the specified data type before computing the sum (rationale: the ``dtype`` keyword argument is intended to help prevent overflows). Default: ``None``. Returns ------- diff --git a/src/array_api_stubs/_draft/statistical_functions.py b/src/array_api_stubs/_draft/statistical_functions.py index 14952402c..496440535 100644 --- a/src/array_api_stubs/_draft/statistical_functions.py +++ b/src/array_api_stubs/_draft/statistical_functions.py @@ -23,22 +23,14 @@ def cumulative_sum( axis along which a cumulative sum must be computed. If ``axis`` is negative, the function must determine the axis along which to compute a cumulative sum by counting from the last dimension. If ``x`` is a one-dimensional array, providing an ``axis`` is optional; however, if ``x`` has more than one dimension, providing an ``axis`` is required. - dtype: Optional[dtype] - data type of the returned array. If ``None``, - - - if the default data type corresponding to the data type "kind" (integer, real-valued floating-point, or complex floating-point) of ``x`` has a smaller range of values than the data type of ``x`` (e.g., ``x`` has data type ``int64`` and the default data type is ``int32``, or ``x`` has data type ``uint64`` and the default data type is ``int64``), the returned array must have the same data type as ``x``. - - - if the default data type corresponding to the data type "kind" of ``x`` has the same or a larger range of values than the data type of ``x``, - - if ``x`` has a real-valued floating-point data type, the returned array must have the default real-valued floating-point data type. - - if ``x`` has a complex floating-point data type, the returned array must have the default complex floating-point data type. - - if ``x`` has a signed integer data type (e.g., ``int16``), the returned array must have the default integer data type. - - if ``x`` has an unsigned integer data type (e.g., ``uint16``), the returned array must have an unsigned integer data type having the same number of bits as the default integer data type (e.g., if the default integer data type is ``int32``, the returned array must have a ``uint32`` data type). + dtype: Optional[dtype] + data type of the returned array. If ``None``, the returned array must have the same data type as ``x``, unless ``x`` has an integer data type supporting a smaller range of values than the default integer data type (e.g., ``x`` has an ``int16`` or ``uint32`` data type and the default integer data type is ``int64``). In those latter cases: - If the data type (either specified or resolved) differs from the data type of ``x``, the input array should be cast to the specified data type before computing the sum. Default: ``None``. + - if ``x`` has a signed integer data type (e.g., ``int16``), the returned array must have the default integer data type. + - if ``x`` has an unsigned integer data type (e.g., ``uint16``), the returned array must have an unsigned integer data type having the same number of bits as the default integer data type (e.g., if the default integer data type is ``int32``, the returned array must have a ``uint32`` data type). - .. note:: - keyword argument is intended to help prevent data type overflows. + If the data type (either specified or resolved) differs from the data type of ``x``, the input array should be cast to the specified data type before computing the sum (rationale: the ``dtype`` keyword argument is intended to help prevent overflows). Default: ``None``. include_initial: bool boolean indicating whether to include the initial value as the first value in the output. By convention, the initial value must be the additive identity (i.e., zero). Default: ``False``. @@ -200,20 +192,14 @@ def prod( input array. Should have a numeric data type. axis: Optional[Union[int, Tuple[int, ...]]] axis or axes along which products must be computed. By default, the product must be computed over the entire array. If a tuple of integers, products must be computed over multiple axes. Default: ``None``. - dtype: Optional[dtype] - data type of the returned array. If ``None``, - - if the default data type corresponding to the data type "kind" (integer, real-valued floating-point, or complex floating-point) of ``x`` has a smaller range of values than the data type of ``x`` (e.g., ``x`` has data type ``int64`` and the default data type is ``int32``, or ``x`` has data type ``uint64`` and the default data type is ``int64``), the returned array must have the same data type as ``x``. - - if the default data type corresponding to the data type "kind" of ``x`` has the same or a larger range of values than the data type of ``x``, - - if ``x`` has a real-valued floating-point data type, the returned array must have the default real-valued floating-point data type. - - if ``x`` has a complex floating-point data type, the returned array must have the default complex floating-point data type. - - if ``x`` has a signed integer data type (e.g., ``int16``), the returned array must have the default integer data type. - - if ``x`` has an unsigned integer data type (e.g., ``uint16``), the returned array must have an unsigned integer data type having the same number of bits as the default integer data type (e.g., if the default integer data type is ``int32``, the returned array must have a ``uint32`` data type). + dtype: Optional[dtype] + data type of the returned array. If ``None``, the returned array must have the same data type as ``x``, unless ``x`` has an integer data type supporting a smaller range of values than the default integer data type (e.g., ``x`` has an ``int16`` or ``uint32`` data type and the default integer data type is ``int64``). In those latter cases: - If the data type (either specified or resolved) differs from the data type of ``x``, the input array should be cast to the specified data type before computing the product. Default: ``None``. + - if ``x`` has a signed integer data type (e.g., ``int16``), the returned array must have the default integer data type. + - if ``x`` has an unsigned integer data type (e.g., ``uint16``), the returned array must have an unsigned integer data type having the same number of bits as the default integer data type (e.g., if the default integer data type is ``int32``, the returned array must have a ``uint32`` data type). - .. note:: - This keyword argument is intended to help prevent data type overflows. + If the data type (either specified or resolved) differs from the data type of ``x``, the input array should be cast to the specified data type before computing the sum (rationale: the ``dtype`` keyword argument is intended to help prevent overflows). Default: ``None``. keepdims: bool if ``True``, the reduced axes (dimensions) must be included in the result as singleton dimensions, and, accordingly, the result must be compatible with the input array (see :ref:`broadcasting`). Otherwise, if ``False``, the reduced axes (dimensions) must not be included in the result. Default: ``False``. @@ -298,20 +284,14 @@ def sum( input array. Should have a numeric data type. axis: Optional[Union[int, Tuple[int, ...]]] axis or axes along which sums must be computed. By default, the sum must be computed over the entire array. If a tuple of integers, sums must be computed over multiple axes. Default: ``None``. - dtype: Optional[dtype] - data type of the returned array. If ``None``, - - if the default data type corresponding to the data type "kind" (integer, real-valued floating-point, or complex floating-point) of ``x`` has a smaller range of values than the data type of ``x`` (e.g., ``x`` has data type ``int64`` and the default data type is ``int32``, or ``x`` has data type ``uint64`` and the default data type is ``int64``), the returned array must have the same data type as ``x``. - - if the default data type corresponding to the data type "kind" of ``x`` has the same or a larger range of values than the data type of ``x``, - - if ``x`` has a real-valued floating-point data type, the returned array must have the default real-valued floating-point data type. - - if ``x`` has a complex floating-point data type, the returned array must have the default complex floating-point data type. - - if ``x`` has a signed integer data type (e.g., ``int16``), the returned array must have the default integer data type. - - if ``x`` has an unsigned integer data type (e.g., ``uint16``), the returned array must have an unsigned integer data type having the same number of bits as the default integer data type (e.g., if the default integer data type is ``int32``, the returned array must have a ``uint32`` data type). + dtype: Optional[dtype] + data type of the returned array. If ``None``, the returned array must have the same data type as ``x``, unless ``x`` has an integer data type supporting a smaller range of values than the default integer data type (e.g., ``x`` has an ``int16`` or ``uint32`` data type and the default integer data type is ``int64``). In those latter cases: - If the data type (either specified or resolved) differs from the data type of ``x``, the input array should be cast to the specified data type before computing the sum. Default: ``None``. + - if ``x`` has a signed integer data type (e.g., ``int16``), the returned array must have the default integer data type. + - if ``x`` has an unsigned integer data type (e.g., ``uint16``), the returned array must have an unsigned integer data type having the same number of bits as the default integer data type (e.g., if the default integer data type is ``int32``, the returned array must have a ``uint32`` data type). - .. note:: - keyword argument is intended to help prevent data type overflows. + If the data type (either specified or resolved) differs from the data type of ``x``, the input array should be cast to the specified data type before computing the sum (rationale: the ``dtype`` keyword argument is intended to help prevent overflows). Default: ``None``. keepdims: bool if ``True``, the reduced axes (dimensions) must be included in the result as singleton dimensions, and, accordingly, the result must be compatible with the input array (see :ref:`broadcasting`). Otherwise, if ``False``, the reduced axes (dimensions) must not be included in the result. Default: ``False``. From 1745c88764934021a8cb0ac8cd2de43a3cec4a67 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 13 Feb 2024 01:46:11 -0700 Subject: [PATCH 16/57] feat!: restrict `axis` kwarg to negative integers in vecdot and cross This commit updates specification guidance in `vecdot` and `cross` to no longer explicitly support positive `axis` kwarg values. Previous specification guidance conflicts with NumPy gufuncs and restricting to negative integers removes ambiguity in determining over which axis to perform computation. This commit uses `should`, not `must`, to allow conforming libraries to support nonnegative `axis` values for backward compatibility. Closes: #724 Closes: #617 PR-URL: https://github.com/data-apis/array-api/pull/740 Reviewed-by: Athan Reines --- src/array_api_stubs/_draft/linalg.py | 9 ++++----- src/array_api_stubs/_draft/linear_algebra_functions.py | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/array_api_stubs/_draft/linalg.py b/src/array_api_stubs/_draft/linalg.py index c66258c7b..903f928d8 100644 --- a/src/array_api_stubs/_draft/linalg.py +++ b/src/array_api_stubs/_draft/linalg.py @@ -83,15 +83,15 @@ def cross(x1: array, x2: array, /, *, axis: int = -1) -> array: Parameters ---------- x1: array - first input array. Must have a numeric data type. + first input array. Must have a numeric data type. The size of the axis over which the cross product is to be computed must be equal to 3. x2: array - second input array. Must be compatible with ``x1`` for all non-compute axes (see :ref:`broadcasting`). The size of the axis over which to compute the cross product must be the same size as the respective axis in ``x1``. Must have a numeric data type. + second input array. Must be broadcast compatible with ``x1`` along all axes other than the axis along which the cross-product is computed (see :ref:`broadcasting`). The size of the axis over which the cross product is to be computed must be equal to 3. Must have a numeric data type. .. note:: The compute axis (dimension) must not be broadcasted. axis: int - the axis (dimension) of ``x1`` and ``x2`` containing the vectors for which to compute the cross product. Must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of the shape determined according to :ref:`broadcasting`. If specified as a negative integer, the function must determine the axis along which to compute the cross product by counting backward from the last dimension (where ``-1`` refers to the last dimension). By default, the function must compute the cross product over the last axis. Default: ``-1``. + the axis (dimension) of ``x1`` and ``x2`` containing the vectors for which to compute the cross product. Should be an integer on the interval ``[-N, -1]``, where ``N`` is ``min(x1.ndim, x2.ndim)``. The function must determine the axis along which to compute the cross product by counting backward from the last dimension (where ``-1`` refers to the last dimension). By default, the function must compute the cross product over the last axis. Default: ``-1``. Returns ------- @@ -110,8 +110,7 @@ def cross(x1: array, x2: array, /, *, axis: int = -1) -> array: **Raises** - - if the size of the axis over which to compute the cross product is not equal to ``3``. - - if the size of the axis over which to compute the cross product is not the same (before broadcasting) for both ``x1`` and ``x2``. + - if the size of the axis over which to compute the cross product is not equal to ``3`` (before broadcasting) for both ``x1`` and ``x2``. """ diff --git a/src/array_api_stubs/_draft/linear_algebra_functions.py b/src/array_api_stubs/_draft/linear_algebra_functions.py index 96f082bd5..eea898a6b 100644 --- a/src/array_api_stubs/_draft/linear_algebra_functions.py +++ b/src/array_api_stubs/_draft/linear_algebra_functions.py @@ -141,7 +141,7 @@ def vecdot(x1: array, x2: array, /, *, axis: int = -1) -> array: The contracted axis (dimension) must not be broadcasted. axis: int - axis over which to compute the dot product. Must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of the shape determined according to :ref:`broadcasting`. If specified as a negative integer, the function must determine the axis along which to compute the dot product by counting backward from the last dimension (where ``-1`` refers to the last dimension). By default, the function must compute the dot product over the last axis. Default: ``-1``. + the axis (dimension) of ``x1`` and ``x2`` containing the vectors for which to compute the dot product. Should be an integer on the interval ``[-N, -1]``, where ``N`` is ``min(x1.ndim, x2.ndim)``. The function must determine the axis along which to compute the dot product by counting backward from the last dimension (where ``-1`` refers to the last dimension). By default, the function must compute the dot product over the last axis. Default: ``-1``. Returns ------- From 474ec2b3eeedc4a6520e63df9601a2e4c6529a5a Mon Sep 17 00:00:00 2001 From: Leo Fang Date: Wed, 14 Feb 2024 04:44:52 -0500 Subject: [PATCH 17/57] docs: clarify `s` and `axes` in FFT guidance Closes: https://github.com/data-apis/array-api/issues/747 PR-URL: https://github.com/data-apis/array-api/pull/746 Co-authored-by: Athan Reines Reviewed-by: Athan Reines --- src/array_api_stubs/_2022_12/fft.py | 30 ++++++++++++++++++++--------- src/array_api_stubs/_draft/fft.py | 30 ++++++++++++++++++++--------- 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/src/array_api_stubs/_2022_12/fft.py b/src/array_api_stubs/_2022_12/fft.py index 901b8407b..bdd7a9c83 100644 --- a/src/array_api_stubs/_2022_12/fft.py +++ b/src/array_api_stubs/_2022_12/fft.py @@ -135,7 +135,7 @@ def fftn( x: array input array. Should have a complex-valued floating-point data type. s: Optional[Sequence[int]] - number of elements over which to compute the transform along the axes (dimensions) specified by ``axes``. Let ``i`` be the index of the nth axis specified by ``axes`` and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers, such that, for all ``i``, ``s[i]`` equals ``M[i]``. + number of elements over which to compute the transform along the axes (dimensions) specified by ``axes``. Let ``i`` be the index of the ``n``-th axis specified by ``axes`` (i.e., ``i = axes[n]``) and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers such that ``s[i]`` equals ``M[i]`` for all ``i``. - If ``s[i]`` is greater than ``M[i]``, axis ``i`` must be zero-padded to size ``s[i]``. - If ``s[i]`` is less than ``M[i]``, axis ``i`` must be trimmed to size ``s[i]``. @@ -143,9 +143,11 @@ def fftn( If ``s`` is not ``None``, ``axes`` must not be ``None``. Default: ``None``. axes: Optional[Sequence[int]] - axes (dimensions) over which to compute the transform. A valid axis must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). + axes (dimensions) over which to compute the transform. A valid axis in ``axes`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). If ``s`` is provided, the corresponding ``axes`` to be transformed must also be provided. If ``axes`` is ``None``, the function must compute the transform over all axes. Default: ``None``. + + If ``axes`` contains two or more entries which resolve to the same axis (i.e., resolved axes are not unique), the behavior is unspecified and thus implementation-defined. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -188,7 +190,7 @@ def ifftn( x: array input array. Should have a complex-valued floating-point data type. s: Optional[Sequence[int]] - number of elements over which to compute the transform along the axes (dimensions) specified by ``axes``. Let ``i`` be the index of the nth axis specified by ``axes`` and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers, such that, for all ``i``, ``s[i]`` equals ``M[i]``. + number of elements over which to compute the transform along the axes (dimensions) specified by ``axes``. Let ``i`` be the index of the ``n``-th axis specified by ``axes`` (i.e., ``i = axes[n]``) and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers such that ``s[i]`` equals ``M[i]`` for all ``i``. - If ``s[i]`` is greater than ``M[i]``, axis ``i`` must be zero-padded to size ``s[i]``. - If ``s[i]`` is less than ``M[i]``, axis ``i`` must be trimmed to size ``s[i]``. @@ -196,9 +198,11 @@ def ifftn( If ``s`` is not ``None``, ``axes`` must not be ``None``. Default: ``None``. axes: Optional[Sequence[int]] - axes (dimensions) over which to compute the transform. A valid axis must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). + axes (dimensions) over which to compute the transform. A valid axis in ``axes`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). If ``s`` is provided, the corresponding ``axes`` to be transformed must also be provided. If ``axes`` is ``None``, the function must compute the transform over all axes. Default: ``None``. + + If ``axes`` contains two or more entries which resolve to the same axis (i.e., resolved axes are not unique), the behavior is unspecified and thus implementation-defined. norm: Literal['backward', 'ortho', 'forward'] specify the normalization mode. Should be one of the following modes: @@ -341,7 +345,7 @@ def rfftn( x: array input array. Must have a real-valued floating-point data type. s: Optional[Sequence[int]] - number of elements over which to compute the transform along axes (dimensions) specified by ``axes``. Let ``i`` be the index of the nth axis specified by ``axes`` and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers, such that, for all ``i``, ``s[i]`` equals ``M[i]``. + number of elements over which to compute the transform along axes (dimensions) specified by ``axes``. Let ``i`` be the index of the ``n``-th axis specified by ``axes`` (i.e., ``i = axes[n]``) and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers such that ``s[i]`` equals ``M[i]`` for all ``i``. - If ``s[i]`` is greater than ``M[i]``, axis ``i`` must be zero-padded to size ``s[i]``. - If ``s[i]`` is less than ``M[i]``, axis ``i`` must be trimmed to size ``s[i]``. @@ -349,9 +353,11 @@ def rfftn( If ``s`` is not ``None``, ``axes`` must not be ``None``. Default: ``None``. axes: Optional[Sequence[int]] - axes (dimensions) over which to compute the transform. A valid axis must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). + axes (dimensions) over which to compute the transform. A valid axis in ``axes`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). If ``s`` is provided, the corresponding ``axes`` to be transformed must also be provided. If ``axes`` is ``None``, the function must compute the transform over all axes. Default: ``None``. + + If ``axes`` contains two or more entries which resolve to the same axis (i.e., resolved axes are not unique), the behavior is unspecified and thus implementation-defined. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -394,7 +400,7 @@ def irfftn( x: array input array. Should have a complex-valued floating-point data type. s: Optional[Sequence[int]] - number of elements along the transformed axes (dimensions) specified by ``axes`` in the **output array**. Let ``i`` be the index of the nth axis specified by ``axes`` and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers, such that, for all ``i``, ``s[i]`` equals ``M[i]``, except for the last transformed axis in which ``s[i]`` equals ``2*(M[i]-1)``. For each ``i``, let ``n`` equal ``s[i]``, except for the last transformed axis in which ``n`` equals ``s[i]//2+1``. + number of elements along the transformed axes (dimensions) specified by ``axes`` in the **output array**. Let ``i`` be the index of the ``n``-th axis specified by ``axes`` (i.e., ``i = axes[n]``) and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers such that ``s[i]`` equals ``M[i]`` for all ``i``, except for the last transformed axis in which ``s[i]`` equals ``2*(M[i]-1)``. For each ``i``, let ``n`` equal ``s[i]``, except for the last transformed axis in which ``n`` equals ``s[i]//2+1``. - If ``n`` is greater than ``M[i]``, axis ``i`` of the input array must be zero-padded to size ``n``. - If ``n`` is less than ``M[i]``, axis ``i`` of the input array must be trimmed to size ``n``. @@ -402,9 +408,11 @@ def irfftn( If ``s`` is not ``None``, ``axes`` must not be ``None``. Default: ``None``. axes: Optional[Sequence[int]] - axes (dimensions) over which to compute the transform. A valid axis must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). + axes (dimensions) over which to compute the transform. A valid axis in ``axes`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). - If ``s`` is provided, the corresponding ``axes`` to be transformed must also be provided. If ``axes`` is ``None``, the function must compute the transform over all axes. Default: ``None``. + If ``s`` is provided, the corresponding ``axes`` to be transformed must also be provided. If ``axes`` is ``None``, the function must compute the transform over all axes. Default: ``None``. + + If ``axes`` contains two or more entries which resolve to the same axis (i.e., resolved axes are not unique), the behavior is unspecified and thus implementation-defined. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -604,6 +612,8 @@ def fftshift(x: array, /, *, axes: Optional[Union[int, Sequence[int]]] = None) - axes: Optional[Union[int, Sequence[int]]] axes over which to shift. If ``None``, the function must shift all axes. Default: ``None``. + If ``axes`` contains two or more entries which resolve to the same axis (i.e., resolved axes are not unique), the behavior is unspecified and thus implementation-defined. + Returns ------- out: array @@ -632,6 +642,8 @@ def ifftshift( axes: Optional[Union[int, Sequence[int]]] axes over which to perform the inverse shift. If ``None``, the function must shift all axes. Default: ``None``. + If ``axes`` contains two or more entries which resolve to the same axis (i.e., resolved axes are not unique), the behavior is unspecified and thus implementation-defined. + Returns ------- out: array diff --git a/src/array_api_stubs/_draft/fft.py b/src/array_api_stubs/_draft/fft.py index 901b8407b..bdd7a9c83 100644 --- a/src/array_api_stubs/_draft/fft.py +++ b/src/array_api_stubs/_draft/fft.py @@ -135,7 +135,7 @@ def fftn( x: array input array. Should have a complex-valued floating-point data type. s: Optional[Sequence[int]] - number of elements over which to compute the transform along the axes (dimensions) specified by ``axes``. Let ``i`` be the index of the nth axis specified by ``axes`` and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers, such that, for all ``i``, ``s[i]`` equals ``M[i]``. + number of elements over which to compute the transform along the axes (dimensions) specified by ``axes``. Let ``i`` be the index of the ``n``-th axis specified by ``axes`` (i.e., ``i = axes[n]``) and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers such that ``s[i]`` equals ``M[i]`` for all ``i``. - If ``s[i]`` is greater than ``M[i]``, axis ``i`` must be zero-padded to size ``s[i]``. - If ``s[i]`` is less than ``M[i]``, axis ``i`` must be trimmed to size ``s[i]``. @@ -143,9 +143,11 @@ def fftn( If ``s`` is not ``None``, ``axes`` must not be ``None``. Default: ``None``. axes: Optional[Sequence[int]] - axes (dimensions) over which to compute the transform. A valid axis must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). + axes (dimensions) over which to compute the transform. A valid axis in ``axes`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). If ``s`` is provided, the corresponding ``axes`` to be transformed must also be provided. If ``axes`` is ``None``, the function must compute the transform over all axes. Default: ``None``. + + If ``axes`` contains two or more entries which resolve to the same axis (i.e., resolved axes are not unique), the behavior is unspecified and thus implementation-defined. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -188,7 +190,7 @@ def ifftn( x: array input array. Should have a complex-valued floating-point data type. s: Optional[Sequence[int]] - number of elements over which to compute the transform along the axes (dimensions) specified by ``axes``. Let ``i`` be the index of the nth axis specified by ``axes`` and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers, such that, for all ``i``, ``s[i]`` equals ``M[i]``. + number of elements over which to compute the transform along the axes (dimensions) specified by ``axes``. Let ``i`` be the index of the ``n``-th axis specified by ``axes`` (i.e., ``i = axes[n]``) and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers such that ``s[i]`` equals ``M[i]`` for all ``i``. - If ``s[i]`` is greater than ``M[i]``, axis ``i`` must be zero-padded to size ``s[i]``. - If ``s[i]`` is less than ``M[i]``, axis ``i`` must be trimmed to size ``s[i]``. @@ -196,9 +198,11 @@ def ifftn( If ``s`` is not ``None``, ``axes`` must not be ``None``. Default: ``None``. axes: Optional[Sequence[int]] - axes (dimensions) over which to compute the transform. A valid axis must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). + axes (dimensions) over which to compute the transform. A valid axis in ``axes`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). If ``s`` is provided, the corresponding ``axes`` to be transformed must also be provided. If ``axes`` is ``None``, the function must compute the transform over all axes. Default: ``None``. + + If ``axes`` contains two or more entries which resolve to the same axis (i.e., resolved axes are not unique), the behavior is unspecified and thus implementation-defined. norm: Literal['backward', 'ortho', 'forward'] specify the normalization mode. Should be one of the following modes: @@ -341,7 +345,7 @@ def rfftn( x: array input array. Must have a real-valued floating-point data type. s: Optional[Sequence[int]] - number of elements over which to compute the transform along axes (dimensions) specified by ``axes``. Let ``i`` be the index of the nth axis specified by ``axes`` and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers, such that, for all ``i``, ``s[i]`` equals ``M[i]``. + number of elements over which to compute the transform along axes (dimensions) specified by ``axes``. Let ``i`` be the index of the ``n``-th axis specified by ``axes`` (i.e., ``i = axes[n]``) and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers such that ``s[i]`` equals ``M[i]`` for all ``i``. - If ``s[i]`` is greater than ``M[i]``, axis ``i`` must be zero-padded to size ``s[i]``. - If ``s[i]`` is less than ``M[i]``, axis ``i`` must be trimmed to size ``s[i]``. @@ -349,9 +353,11 @@ def rfftn( If ``s`` is not ``None``, ``axes`` must not be ``None``. Default: ``None``. axes: Optional[Sequence[int]] - axes (dimensions) over which to compute the transform. A valid axis must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). + axes (dimensions) over which to compute the transform. A valid axis in ``axes`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). If ``s`` is provided, the corresponding ``axes`` to be transformed must also be provided. If ``axes`` is ``None``, the function must compute the transform over all axes. Default: ``None``. + + If ``axes`` contains two or more entries which resolve to the same axis (i.e., resolved axes are not unique), the behavior is unspecified and thus implementation-defined. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -394,7 +400,7 @@ def irfftn( x: array input array. Should have a complex-valued floating-point data type. s: Optional[Sequence[int]] - number of elements along the transformed axes (dimensions) specified by ``axes`` in the **output array**. Let ``i`` be the index of the nth axis specified by ``axes`` and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers, such that, for all ``i``, ``s[i]`` equals ``M[i]``, except for the last transformed axis in which ``s[i]`` equals ``2*(M[i]-1)``. For each ``i``, let ``n`` equal ``s[i]``, except for the last transformed axis in which ``n`` equals ``s[i]//2+1``. + number of elements along the transformed axes (dimensions) specified by ``axes`` in the **output array**. Let ``i`` be the index of the ``n``-th axis specified by ``axes`` (i.e., ``i = axes[n]``) and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers such that ``s[i]`` equals ``M[i]`` for all ``i``, except for the last transformed axis in which ``s[i]`` equals ``2*(M[i]-1)``. For each ``i``, let ``n`` equal ``s[i]``, except for the last transformed axis in which ``n`` equals ``s[i]//2+1``. - If ``n`` is greater than ``M[i]``, axis ``i`` of the input array must be zero-padded to size ``n``. - If ``n`` is less than ``M[i]``, axis ``i`` of the input array must be trimmed to size ``n``. @@ -402,9 +408,11 @@ def irfftn( If ``s`` is not ``None``, ``axes`` must not be ``None``. Default: ``None``. axes: Optional[Sequence[int]] - axes (dimensions) over which to compute the transform. A valid axis must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). + axes (dimensions) over which to compute the transform. A valid axis in ``axes`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). - If ``s`` is provided, the corresponding ``axes`` to be transformed must also be provided. If ``axes`` is ``None``, the function must compute the transform over all axes. Default: ``None``. + If ``s`` is provided, the corresponding ``axes`` to be transformed must also be provided. If ``axes`` is ``None``, the function must compute the transform over all axes. Default: ``None``. + + If ``axes`` contains two or more entries which resolve to the same axis (i.e., resolved axes are not unique), the behavior is unspecified and thus implementation-defined. norm: Literal['backward', 'ortho', 'forward'] normalization mode. Should be one of the following modes: @@ -604,6 +612,8 @@ def fftshift(x: array, /, *, axes: Optional[Union[int, Sequence[int]]] = None) - axes: Optional[Union[int, Sequence[int]]] axes over which to shift. If ``None``, the function must shift all axes. Default: ``None``. + If ``axes`` contains two or more entries which resolve to the same axis (i.e., resolved axes are not unique), the behavior is unspecified and thus implementation-defined. + Returns ------- out: array @@ -632,6 +642,8 @@ def ifftshift( axes: Optional[Union[int, Sequence[int]]] axes over which to perform the inverse shift. If ``None``, the function must shift all axes. Default: ``None``. + If ``axes`` contains two or more entries which resolve to the same axis (i.e., resolved axes are not unique), the behavior is unspecified and thus implementation-defined. + Returns ------- out: array From 71b01c1fbecf7e53fb9f7849b60ba2c2c44a6b73 Mon Sep 17 00:00:00 2001 From: Leo Fang Date: Wed, 14 Feb 2024 15:44:32 -0500 Subject: [PATCH 18/57] Support `copy` and `device` keywords in `from_dlpack` (#741) * support copy in from_dlpack * specify copy stream * allow 3-way copy arg to align all constructors * update to reflect the discussions * clairfy a bit and fix typos * sync the copy docs * clarify what's 'on CPU' * try to make linter happy * remove namespace leak clause, clean up, and add an example * make linter happy * fix Sphinx complaint about Enum * add/update v2023-specific notes on device * remove a note on kDLCPU --------- Co-authored-by: Ralf Gommers --- src/array_api_stubs/_draft/array_object.py | 51 +++++++++++++++++-- .../_draft/creation_functions.py | 46 +++++++++++++++-- 2 files changed, 89 insertions(+), 8 deletions(-) diff --git a/src/array_api_stubs/_draft/array_object.py b/src/array_api_stubs/_draft/array_object.py index 5b412685e..5017bf87c 100644 --- a/src/array_api_stubs/_draft/array_object.py +++ b/src/array_api_stubs/_draft/array_object.py @@ -293,6 +293,8 @@ def __dlpack__( *, stream: Optional[Union[int, Any]] = None, max_version: Optional[tuple[int, int]] = None, + dl_device: Optional[tuple[Enum, int]] = None, + copy: Optional[bool] = None, ) -> PyCapsule: """ Exports the array for consumption by :func:`~array_api.from_dlpack` as a DLPack capsule. @@ -324,6 +326,12 @@ def __dlpack__( - ``> 2``: stream number represented as a Python integer. - Using ``1`` and ``2`` is not supported. + .. note:: + When ``dl_device`` is provided explicitly, ``stream`` must be a valid + construct for the specified device type. In particular, when ``kDLCPU`` + is in use, ``stream`` must be ``None`` and a synchronization must be + performed to ensure data safety. + .. admonition:: Tip :class: important @@ -333,12 +341,40 @@ def __dlpack__( not want to think about stream handling at all, potentially at the cost of more synchronizations than necessary. max_version: Optional[tuple[int, int]] - The maximum DLPack version that the *consumer* (i.e., the caller of + the maximum DLPack version that the *consumer* (i.e., the caller of ``__dlpack__``) supports, in the form of a 2-tuple ``(major, minor)``. This method may return a capsule of version ``max_version`` (recommended if it does support that), or of a different version. This means the consumer must verify the version even when `max_version` is passed. + dl_device: Optional[tuple[enum.Enum, int]] + the DLPack device type. Default is ``None``, meaning the exported capsule + should be on the same device as ``self`` is. When specified, the format + must be a 2-tuple, following that of the return value of :meth:`array.__dlpack_device__`. + If the device type cannot be handled by the producer, this function must + raise ``BufferError``. + + The v2023.12 standard only mandates that a compliant library should offer a way for + ``__dlpack__`` to return a capsule referencing an array whose underlying memory is + accessible to the Python interpreter (represented by the ``kDLCPU`` enumerator in DLPack). + If a copy must be made to enable this support but ``copy`` is set to ``False``, the + function must raise ``ValueError``. + + Other device kinds will be considered for standardization in a future version of this + API standard. + copy: Optional[bool] + boolean indicating whether or not to copy the input. If ``True``, the + function must always copy (performed by the producer). If ``False``, the + function must never copy, and raise a ``BufferError`` in case a copy is + deemed necessary (e.g. if a cross-device data movement is requested, and + it is not possible without a copy). If ``None``, the function must reuse + the existing memory buffer if possible and copy otherwise. Default: ``None``. + + When a copy happens, the ``DLPACK_FLAG_BITMASK_IS_COPIED`` flag must be set. + + .. note:: + If a copy happens, and if the consumer-provided ``stream`` and ``dl_device`` + can be understood by the producer, the copy must be performed over ``stream``. Returns ------- @@ -394,22 +430,25 @@ def __dlpack__( # here to tell users that the consumer's max_version is too # old to allow the data exchange to happen. - And this logic for the consumer in ``from_dlpack``: + And this logic for the consumer in :func:`~array_api.from_dlpack`: .. code:: python try: - x.__dlpack__(max_version=(1, 0)) + x.__dlpack__(max_version=(1, 0), ...) # if it succeeds, store info from the capsule named "dltensor_versioned", # and need to set the name to "used_dltensor_versioned" when we're done except TypeError: - x.__dlpack__() + x.__dlpack__(...) + + This logic is also applicable to handling of the new ``dl_device`` and ``copy`` + keywords. .. versionchanged:: 2022.12 Added BufferError. .. versionchanged:: 2023.12 - Added the ``max_version`` keyword. + Added the ``max_version``, ``dl_device``, and ``copy`` keywords. """ def __dlpack_device__(self: array, /) -> Tuple[Enum, int]: @@ -436,6 +475,8 @@ def __dlpack_device__(self: array, /) -> Tuple[Enum, int]: METAL = 8 VPI = 9 ROCM = 10 + CUDA_MANAGED = 13 + ONE_API = 14 """ def __eq__(self: array, other: Union[int, float, bool, array], /) -> array: diff --git a/src/array_api_stubs/_draft/creation_functions.py b/src/array_api_stubs/_draft/creation_functions.py index d0f967717..dec09db0c 100644 --- a/src/array_api_stubs/_draft/creation_functions.py +++ b/src/array_api_stubs/_draft/creation_functions.py @@ -214,7 +214,13 @@ def eye( """ -def from_dlpack(x: object, /) -> array: +def from_dlpack( + x: object, + /, + *, + device: Optional[device] = None, + copy: Optional[bool] = None, +) -> array: """ Returns a new array containing the data from another (array) object with a ``__dlpack__`` method. @@ -222,11 +228,22 @@ def from_dlpack(x: object, /) -> array: ---------- x: object input (array) object. + device: Optional[device] + device on which to place the created array. If ``device`` is ``None`` and ``x`` supports DLPack, the output array must be on the same device as ``x``. Default: ``None``. + + The v2023.12 standard only mandates that a compliant library should offer a way for ``from_dlpack`` to return an array + whose underlying memory is accessible to the Python interpreter, when the corresponding ``device`` is provided. If the + array library does not support such cases at all, the function must raise ``BufferError``. If a copy must be made to + enable this support but ``copy`` is set to ``False``, the function must raise ``ValueError``. + + Other device kinds will be considered for standardization in a future version of this API standard. + copy: Optional[bool] + boolean indicating whether or not to copy the input. If ``True``, the function must always copy. If ``False``, the function must never copy, and raise ``BufferError`` in case a copy is deemed necessary (e.g. if a cross-device data movement is requested, and it is not possible without a copy). If ``None``, the function must reuse the existing memory buffer if possible and copy otherwise. Default: ``None``. Returns ------- out: array - an array containing the data in `x`. + an array containing the data in ``x``. .. admonition:: Note :class: note @@ -238,7 +255,7 @@ def from_dlpack(x: object, /) -> array: BufferError The ``__dlpack__`` and ``__dlpack_device__`` methods on the input array may raise ``BufferError`` when the data cannot be exported as DLPack - (e.g., incompatible dtype or strides). It may also raise other errors + (e.g., incompatible dtype, strides, or device). It may also raise other errors when export fails for other reasons (e.g., not enough memory available to materialize the data). ``from_dlpack`` must propagate such exceptions. @@ -246,11 +263,34 @@ def from_dlpack(x: object, /) -> array: If the ``__dlpack__`` and ``__dlpack_device__`` methods are not present on the input array. This may happen for libraries that are never able to export their data with DLPack. + ValueError + If data exchange is possible via an explicit copy but ``copy`` is set to ``False``. Notes ----- See :meth:`array.__dlpack__` for implementation suggestions for `from_dlpack` in order to handle DLPack versioning correctly. + + A way to move data from two array libraries to the same device (assumed supported by both libraries) in + a library-agnostic fashion is illustrated below: + + .. code:: python + + def func(x, y): + xp_x = x.__array_namespace__() + xp_y = y.__array_namespace__() + + # Other functions than `from_dlpack` only work if both arrays are from the same library. So if + # `y` is from a different one than `x`, let's convert `y` into an array of the same type as `x`: + if not xp_x == xp_y: + y = xp_x.from_dlpack(y, copy=True, device=x.device) + + # From now on use `xp_x.xxxxx` functions, as both arrays are from the library `xp_x` + ... + + + .. versionchanged:: 2023.12 + Added device and copy support. """ From cc11aa344d8dab03345acb563f37c25583af151a Mon Sep 17 00:00:00 2001 From: Athan Date: Wed, 14 Feb 2024 15:37:07 -0800 Subject: [PATCH 19/57] Add Array API inspection utilities (#689) --- spec/draft/API_specification/index.rst | 1 + spec/draft/API_specification/inspection.rst | 40 +++++ spec/draft/design_topics/device_support.rst | 29 ++-- src/_array_api_conf.py | 4 + src/array_api_stubs/_draft/__init__.py | 1 + src/array_api_stubs/_draft/_types.py | 59 +++++++ src/array_api_stubs/_draft/info.py | 170 ++++++++++++++++++++ 7 files changed, 290 insertions(+), 14 deletions(-) create mode 100644 spec/draft/API_specification/inspection.rst create mode 100644 src/array_api_stubs/_draft/info.py diff --git a/spec/draft/API_specification/index.rst b/spec/draft/API_specification/index.rst index 603131ab9..ffc3d3775 100644 --- a/spec/draft/API_specification/index.rst +++ b/spec/draft/API_specification/index.rst @@ -29,6 +29,7 @@ A conforming implementation of the array API standard must provide and support t function_and_method_signatures indexing indexing_functions + inspection linear_algebra_functions manipulation_functions searching_functions diff --git a/spec/draft/API_specification/inspection.rst b/spec/draft/API_specification/inspection.rst new file mode 100644 index 000000000..04691e712 --- /dev/null +++ b/spec/draft/API_specification/inspection.rst @@ -0,0 +1,40 @@ +Inspection +========== + + Array API specification for namespace inspection utilities. + +A conforming implementation of the array API standard must provide and support the following functions and associated inspection APIs. + + +Objects in API +-------------- + +.. currentmodule:: array_api.info + +.. + NOTE: please keep the functions in alphabetical order + +.. autosummary:: + :toctree: generated + :template: method.rst + + __array_namespace_info__ + + +Inspection APIs +--------------- + +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. + +.. + NOTE: please keep the functions in alphabetical order + +.. autosummary:: + :toctree: generated + :template: method.rst + + capabilities + default_device + default_dtypes + devices + dtypes diff --git a/spec/draft/design_topics/device_support.rst b/spec/draft/design_topics/device_support.rst index 29f0789bb..593b0b9fa 100644 --- a/spec/draft/design_topics/device_support.rst +++ b/spec/draft/design_topics/device_support.rst @@ -39,7 +39,7 @@ into library code which may have to do the following: Syntax for device assignment ---------------------------- -The array API will offer the following syntax for device assignment and +The array API provides the following syntax for device assignment and cross-device data transfer: 1. A ``.device`` property on the array object, which returns a ``Device`` object @@ -52,19 +52,20 @@ cross-device data transfer: 3. A ``.to_device`` method on the array object to copy an array to a different device. .. note:: - In the current API standard, the only way to obtain a ``Device`` object is from the - ``.device`` property on the array object. The standard does **not** include a universal - ``Device`` object recognized by all compliant libraries. Accordingly, the standard does - not provide a means of instantiating a ``Device`` object to point to a specific physical or - logical device. - - The choice to not include a standardized ``Device`` object may be revisited in a future revision of this standard. - - For array libraries which concern themselves with multi-device support, including CPU and GPU, - they are free to expose a library-specific device object (e.g., for creating an - array on a particular device). While a library-specific device object can be used as input to - ``to_device``, beware that this will mean non-portability as code will be specific to - that library. + The current API standard does **not** include a universal ``Device`` object + recognized by all compliant libraries. Accordingly, the standard does not + provide a means of instantiating a ``Device`` object to point to a specific + physical or logical device. + + The choice to not include a standardized ``Device`` object may be revisited + in a future revision of this standard. + + For array libraries which concern themselves with multi-device support, + including CPU and GPU, they are free to expose a library-specific device + object (e.g., for creating an array on a particular device). While a + library-specific device object can be used as input to ``to_device``, beware + that this will mean non-portability as code will be specific to that + library. Semantics --------- diff --git a/src/_array_api_conf.py b/src/_array_api_conf.py index 9c5722f94..9233df3c4 100644 --- a/src/_array_api_conf.py +++ b/src/_array_api_conf.py @@ -72,6 +72,10 @@ ("py:class", ".*PyCapsule"), ("py:class", ".*finfo_object"), ("py:class", ".*iinfo_object"), + ("py:class", ".*Info"), + ("py:class", ".*Capabilities"), + ("py:class", ".*DefaultDataTypes"), + ("py:class", ".*DataTypes"), ] # In array_object.py we have to use aliased names for some types because they # would otherwise refer back to method objects of array diff --git a/src/array_api_stubs/_draft/__init__.py b/src/array_api_stubs/_draft/__init__.py index 12b6d9fbb..8415f2765 100644 --- a/src/array_api_stubs/_draft/__init__.py +++ b/src/array_api_stubs/_draft/__init__.py @@ -16,6 +16,7 @@ from .utility_functions import * from . import linalg from . import fft +from . import info __array_api_version__: str = "YYYY.MM" diff --git a/src/array_api_stubs/_draft/_types.py b/src/array_api_stubs/_draft/_types.py index 2a73dda24..7c3d903d7 100644 --- a/src/array_api_stubs/_draft/_types.py +++ b/src/array_api_stubs/_draft/_types.py @@ -25,6 +25,10 @@ "finfo_object", "iinfo_object", "Enum", + "DefaultDataTypes", + "DataTypes", + "Capabilities", + "Info", ] from dataclasses import dataclass @@ -35,6 +39,7 @@ Optional, Sequence, Tuple, + TypedDict, TypeVar, Union, Protocol, @@ -83,3 +88,57 @@ def __getitem__(self, key: int, /) -> Union[_T_co, NestedSequence[_T_co]]: def __len__(self, /) -> int: ... + + +class Info(Protocol): + """Namespace returned by `__array_namespace_info__`.""" + + def capabilities(self) -> Capabilities: + ... + + def default_device(self) -> device: + ... + + def default_dtypes(self, *, device: Optional[device]) -> DefaultDataTypes: + ... + + def devices(self) -> List[device]: + ... + + def dtypes( + self, *, device: Optional[device], kind: Optional[Union[str, Tuple[str, ...]]] + ) -> DataTypes: + ... + + +DefaultDataTypes = TypedDict( + "DefaultDataTypes", + { + "real floating": dtype, + "complex floating": dtype, + "integral": dtype, + "indexing": dtype, + }, +) +DataTypes = TypedDict( + "DataTypes", + { + "bool": dtype, + "float32": dtype, + "float64": dtype, + "complex64": dtype, + "complex128": dtype, + "int8": dtype, + "int16": dtype, + "int32": dtype, + "int64": dtype, + "uint8": dtype, + "uint16": dtype, + "uint32": dtype, + "uint64": dtype, + }, + total=False, +) +Capabilities = TypedDict( + "Capabilities", {"boolean indexing": bool, "data-dependent shapes": bool} +) diff --git a/src/array_api_stubs/_draft/info.py b/src/array_api_stubs/_draft/info.py new file mode 100644 index 000000000..f70418405 --- /dev/null +++ b/src/array_api_stubs/_draft/info.py @@ -0,0 +1,170 @@ +__all__ = [ + "__array_namespace_info__", + "capabilities", + "default_device", + "default_dtypes", + "devices", + "dtypes", +] + +from ._types import ( + Optional, + Union, + Tuple, + List, + device, + dtype, + DefaultDataTypes, + DataTypes, + Capabilities, + Info, +) + + +def __array_namespace_info__() -> Info: + """ + Returns a namespace with Array API namespace inspection utilities. + + Returns + ------- + out: Info + An object containing Array API namespace inspection utilities. + + Notes + ----- + + The returned object may be either a namespace or a class, so long as an Array API user can access inspection utilities as follows: + + :: + + info = xp.__array_namespace_info__() + info.capabilities() + info.devices() + info.dtypes() + info.default_dtypes() + # ... + """ + + +def capabilities() -> Capabilities: + """ + Returns a dictionary of array library capabilities. + + The dictionary must contain the following keys: + + - `"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``. + - `"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``. + + Returns + ------- + out: Capabilities + a dictionary of array library capabilities. + """ + + +def default_device() -> device: + """ + Returns the default device. + + Returns + ------- + out: device + an object corresponding to the default device. + """ + + +def default_dtypes( + *, + device: Optional[device] = None, +) -> DefaultDataTypes: + """ + Returns a dictionary containing default data types. + + The dictionary must have the following keys: + + - `"real floating"`: default real floating-point data type. + - `"complex floating"`: default complex floating-point data type. + - `"integral"`: default integral data type. + - `"indexing"`: default array index data type. + + Dictionary values must be the corresponding data type object. + + Parameters + ---------- + device: Optional[device] + 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``. + + .. note:: + 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. + + Returns + ------- + out: DefaultDataTypes + a dictionary containing the default data type for respective data type kinds. + """ + + +def dtypes( + *, + device: Optional[device] = None, + kind: Optional[Union[str, Tuple[str, ...]]] = None, +) -> DataTypes: + """ + Returns a dictionary of supported *Array API* data types. + + .. note:: + 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. + + .. note:: + 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. + + Parameters + ---------- + kind: Optional[Union[str, Tuple[str, ...]]] + data type kind. + + - If ``kind`` is ``None``, the function must return a dictionary containing all supported Array API data types. + + - 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: + + - ``'bool'``: boolean data types (e.g., ``bool``). + - ``'signed integer'``: signed integer data types (e.g., ``int8``, ``int16``, ``int32``, ``int64``). + - ``'unsigned integer'``: unsigned integer data types (e.g., ``uint8``, ``uint16``, ``uint32``, ``uint64``). + - ``'integral'``: integer data types. Shorthand for ``('signed integer', 'unsigned integer')``. + - ``'real floating'``: real-valued floating-point data types (e.g., ``float32``, ``float64``). + - ``'complex floating'``: complex floating-point data types (e.g., ``complex64``, ``complex128``). + - ``'numeric'``: numeric data types. Shorthand for ``('integral', 'real floating', 'complex floating')``. + + - 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. + + Default: ``None``. + device: Optional[device] + 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``. + + .. note:: + 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. + + Returns + ------- + out: DataTypes + a dictionary containing supported data types. + + .. note:: + Dictionary keys must only consist of canonical names as defined in :ref:`data-types`. + """ + + +def devices() -> List[device]: + """ + Returns a list of supported devices which are available at runtime. + + Returns + ------- + out: List[device] + a list of supported devices. + + Notes + ----- + + 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. + """ From ba787e3aaa75d41bcafa59ddc0915f61b483a403 Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Fri, 16 Feb 2024 17:32:18 +0100 Subject: [PATCH 20/57] Add recommendation on what to do with DLPack read-only flag (#749) This is a follow-up to the discussion in gh-191. It's a recommendation rather than a hard requirement to allow implementers some choice. That said, this is how things worked in practice before DLPack 1.0 as well, since there was no flag to represent read-only. Experience with JAX showed that exposing shared memory was preferred by users over raising or always making a copy of the data on the producer side. --- src/array_api_stubs/_draft/array_object.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/array_api_stubs/_draft/array_object.py b/src/array_api_stubs/_draft/array_object.py index 5017bf87c..2a33a60ae 100644 --- a/src/array_api_stubs/_draft/array_object.py +++ b/src/array_api_stubs/_draft/array_object.py @@ -444,6 +444,12 @@ def __dlpack__( This logic is also applicable to handling of the new ``dl_device`` and ``copy`` keywords. + DLPack 1.0 added a flag to indicate that the array is read-only + (``DLPACK_FLAG_BITMASK_READ_ONLY``). A consumer that does not support + read-only arrays should ignore this flag (this is preferred over + raising an exception; the user is then responsible for ensuring the + memory isn't modified). + .. versionchanged:: 2022.12 Added BufferError. From bc48a1a3f73a64fdc6677f361bff11ee2baf7f97 Mon Sep 17 00:00:00 2001 From: Athan Date: Wed, 21 Feb 2024 12:18:13 -0800 Subject: [PATCH 21/57] Add `hypot` specification for computing the square root of the sum of squares (#703) --- .../elementwise_functions.rst | 1 + .../_draft/elementwise_functions.py | 46 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/spec/draft/API_specification/elementwise_functions.rst b/spec/draft/API_specification/elementwise_functions.rst index f0b69cfcb..4919cff98 100644 --- a/spec/draft/API_specification/elementwise_functions.rst +++ b/spec/draft/API_specification/elementwise_functions.rst @@ -46,6 +46,7 @@ Objects in API floor_divide greater greater_equal + hypot imag isfinite isinf diff --git a/src/array_api_stubs/_draft/elementwise_functions.py b/src/array_api_stubs/_draft/elementwise_functions.py index c71b6674b..ee6ea27f3 100644 --- a/src/array_api_stubs/_draft/elementwise_functions.py +++ b/src/array_api_stubs/_draft/elementwise_functions.py @@ -28,6 +28,7 @@ "floor_divide", "greater", "greater_equal", + "hypot", "imag", "isfinite", "isinf", @@ -1367,6 +1368,51 @@ def greater_equal(x1: array, x2: array, /) -> array: """ +def hypot(x1: array, x2: array, /) -> array: + r""" + Computes the square root of the sum of squares for each element ``x1_i`` of the input array ``x1`` with the respective element ``x2_i`` of the input array ``x2``. + + .. note:: + The value computed by this function may be interpreted as the length of the hypotenuse of a right-angled triangle with sides of length ``x1_i`` and ``x2_i``, the distance of a point ``(x1_i, x2_i)`` from the origin ``(0, 0)``, or the magnitude of a complex number ``x1_i + x2_i * 1j``. + + Parameters + ---------- + x1: array + first input array. Should have a real-valued floating-point data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a real-valued floating-point data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a real-valued floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + The purpose of this function is to avoid underflow and overflow during intermediate stages of computation. Accordingly, conforming implementations should not use naive implementations. + + **Special Cases** + + For real-valued floating-point operands, + + - If ``x1_i`` is ``+infinity`` or ``-infinity`` and ``x2_i`` is any value, including ``NaN``, the result is ``+infinity``. + - If ``x2_i`` is ``+infinity`` or ``-infinity`` and ``x1_i`` is any value, including ``NaN``, the result is ``+infinity``. + - If ``x1_i`` is either ``+0`` or ``-0``, the result is equivalent to ``abs(x2_i)``. + - If ``x2_i`` is either ``+0`` or ``-0``, the result is equivalent to ``abs(x1_i)``. + - If ``x1_i`` is a finite number or ``NaN`` and ``x2_i`` is ``NaN``, the result is ``NaN``. + - If ``x2_i`` is a finite number or ``NaN`` and ``x1_i`` is ``NaN``, the result is ``NaN``. + - Underflow may only occur when both arguments are subnormal and the correct result is also subnormal. + + For real-valued floating-point operands, ``hypot(x1, x2)`` must equal ``hypot(x2, x1)``, ``hypot(x1, -x2)``, ``hypot(-x1, x2)``, and ``hypot(-x1, -x2)``. + + .. note:: + IEEE 754-2019 requires support for subnormal (a.k.a., denormal) numbers, which are useful for supporting gradual underflow. However, hardware support for subnormal numbers is not universal, and many platforms (e.g., accelerators) and compilers support toggling denormals-are-zero (DAZ) and/or flush-to-zero (FTZ) behavior to increase performance and to guard against timing attacks. + + Accordingly, conforming implementations may vary in their support for subnormal numbers. + """ + + def imag(x: array, /) -> array: """ Returns the imaginary component of a complex number for each element ``x_i`` of the input array ``x``. From 6e9c48720fcde6941d48596e134c869c011a6a65 Mon Sep 17 00:00:00 2001 From: Athan Date: Wed, 21 Feb 2024 23:18:27 -0800 Subject: [PATCH 22/57] docs: add note concerning signed zeros (#751) --- .../_draft/elementwise_functions.py | 14 ++++++----- .../_draft/statistical_functions.py | 24 +++++++++---------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/array_api_stubs/_draft/elementwise_functions.py b/src/array_api_stubs/_draft/elementwise_functions.py index ee6ea27f3..4dc0c949f 100644 --- a/src/array_api_stubs/_draft/elementwise_functions.py +++ b/src/array_api_stubs/_draft/elementwise_functions.py @@ -1905,9 +1905,6 @@ def maximum(x1: array, x2: array, /) -> array: r""" Computes the maximum value for each element ``x1_i`` of the input array ``x1`` relative to the respective element ``x2_i`` of the input array ``x2``. - .. note:: - For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-dependent (see :ref:`complex-number-ordering`). - Parameters ---------- x1: array @@ -1923,6 +1920,10 @@ def maximum(x1: array, x2: array, /) -> array: Notes ----- + The order of signed zeros is unspecified and thus implementation-defined. When choosing between ``-0`` or ``+0`` as a maximum value, specification-compliant libraries may choose to return either value. + + For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-defined (see :ref:`complex-number-ordering`). + **Special Cases** For floating-point operands, @@ -1935,9 +1936,6 @@ def minimum(x1: array, x2: array, /) -> array: r""" Computes the minimum value for each element ``x1_i`` of the input array ``x1`` relative to the respective element ``x2_i`` of the input array ``x2``. - .. note:: - For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-dependent (see :ref:`complex-number-ordering`). - Parameters ---------- x1: array @@ -1953,6 +1951,10 @@ def minimum(x1: array, x2: array, /) -> array: Notes ----- + The order of signed zeros is unspecified and thus implementation-defined. When choosing between ``-0`` or ``+0`` as a minimum value, specification-compliant libraries may choose to return either value. + + For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-defined (see :ref:`complex-number-ordering`). + **Special Cases** For floating-point operands, diff --git a/src/array_api_stubs/_draft/statistical_functions.py b/src/array_api_stubs/_draft/statistical_functions.py index 496440535..2304ece80 100644 --- a/src/array_api_stubs/_draft/statistical_functions.py +++ b/src/array_api_stubs/_draft/statistical_functions.py @@ -64,12 +64,6 @@ def max( """ Calculates the maximum value of the input array ``x``. - .. note:: - When the number of elements over which to compute the maximum value is zero, the maximum value is implementation-defined. Specification-compliant libraries may choose to raise an error, return a sentinel value (e.g., if ``x`` is a floating-point input array, return ``NaN``), or return the minimum possible value for the input array ``x`` data type (e.g., if ``x`` is a floating-point array, return ``-infinity``). - - .. note:: - For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-dependent (see :ref:`complex-number-ordering`). - Parameters ---------- x: array @@ -87,6 +81,12 @@ def max( Notes ----- + When the number of elements over which to compute the maximum value is zero, the maximum value is implementation-defined. Specification-compliant libraries may choose to raise an error, return a sentinel value (e.g., if ``x`` is a floating-point input array, return ``NaN``), or return the minimum possible value for the input array ``x`` data type (e.g., if ``x`` is a floating-point array, return ``-infinity``). + + The order of signed zeros is unspecified and thus implementation-defined. When choosing between ``-0`` or ``+0`` as a maximum value, specification-compliant libraries may choose to return either value. + + For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-defined (see :ref:`complex-number-ordering`). + **Special Cases** For floating-point operands, @@ -144,12 +144,6 @@ def min( """ Calculates the minimum value of the input array ``x``. - .. note:: - When the number of elements over which to compute the minimum value is zero, the minimum value is implementation-defined. Specification-compliant libraries may choose to raise an error, return a sentinel value (e.g., if ``x`` is a floating-point input array, return ``NaN``), or return the maximum possible value for the input array ``x`` data type (e.g., if ``x`` is a floating-point array, return ``+infinity``). - - .. note:: - For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-dependent (see :ref:`complex-number-ordering`). - Parameters ---------- x: array @@ -167,6 +161,12 @@ def min( Notes ----- + When the number of elements over which to compute the minimum value is zero, the minimum value is implementation-defined. Specification-compliant libraries may choose to raise an error, return a sentinel value (e.g., if ``x`` is a floating-point input array, return ``NaN``), or return the maximum possible value for the input array ``x`` data type (e.g., if ``x`` is a floating-point array, return ``+infinity``). + + The order of signed zeros is unspecified and thus implementation-defined. When choosing between ``-0`` or ``+0`` as a minimum value, specification-compliant libraries may choose to return either value. + + For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-defined (see :ref:`complex-number-ordering`). + **Special Cases** For floating-point operands, From 11273e62fbfbc17b2a2f61535520ea3e34d71d52 Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 22 Feb 2024 12:40:28 -0800 Subject: [PATCH 23/57] docs: clarify shape of `values` and order of `counts` (#752) --- src/array_api_stubs/_draft/set_functions.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/array_api_stubs/_draft/set_functions.py b/src/array_api_stubs/_draft/set_functions.py index 394fa2b17..25a897355 100644 --- a/src/array_api_stubs/_draft/set_functions.py +++ b/src/array_api_stubs/_draft/set_functions.py @@ -34,10 +34,10 @@ def unique_all(x: array, /) -> Tuple[array, array, array, array]: out: Tuple[array, array, array, array] a namedtuple ``(values, indices, inverse_indices, counts)`` whose - - first element must have the field name ``values`` and must be an array containing the unique elements of ``x``. The array must have the same data type as ``x``. - - second element must have the field name ``indices`` and must be an array containing the indices (first occurrences) of ``x`` that result in ``values``. The array must have the same shape as ``values`` and must have the default array index data type. + - first element must have the field name ``values`` and must be a one-dimensional array containing the unique elements of ``x``. The array must have the same data type as ``x``. + - second element must have the field name ``indices`` and must be an array containing the indices (first occurrences) of a flattened ``x`` that result in ``values``. The array must have the same shape as ``values`` and must have the default array index data type. - third element must have the field name ``inverse_indices`` and must be an array containing the indices of ``values`` that reconstruct ``x``. The array must have the same shape as ``x`` and must have the default array index data type. - - fourth element must have the field name ``counts`` and must be an array containing the number of times each unique element occurs in ``x``. The returned array must have same shape as ``values`` and must have the default array index data type. + - fourth element must have the field name ``counts`` and must be an array containing the number of times each unique element occurs in ``x``. The order of the returned counts must match the order of ``values``, such that a specific element in ``counts`` corresponds to the respective unique element in ``values``. The returned array must have same shape as ``values`` and must have the default array index data type. .. note:: The order of unique elements is not specified and may vary between implementations. @@ -78,8 +78,8 @@ def unique_counts(x: array, /) -> Tuple[array, array]: out: Tuple[array, array] a namedtuple `(values, counts)` whose - - first element must have the field name ``values`` and must be an array containing the unique elements of ``x``. The array must have the same data type as ``x``. - - second element must have the field name `counts` and must be an array containing the number of times each unique element occurs in ``x``. The returned array must have same shape as ``values`` and must have the default array index data type. + - first element must have the field name ``values`` and must be a one-dimensional array containing the unique elements of ``x``. The array must have the same data type as ``x``. + - second element must have the field name `counts` and must be an array containing the number of times each unique element occurs in ``x``. The order of the returned counts must match the order of ``values``, such that a specific element in ``counts`` corresponds to the respective unique element in ``values``. The returned array must have same shape as ``values`` and must have the default array index data type. .. note:: The order of unique elements is not specified and may vary between implementations. @@ -120,7 +120,7 @@ def unique_inverse(x: array, /) -> Tuple[array, array]: out: Tuple[array, array] a namedtuple ``(values, inverse_indices)`` whose - - first element must have the field name ``values`` and must be an array containing the unique elements of ``x``. The array must have the same data type as ``x``. + - first element must have the field name ``values`` and must be a one-dimensional array containing the unique elements of ``x``. The array must have the same data type as ``x``. - second element must have the field name ``inverse_indices`` and must be an array containing the indices of ``values`` that reconstruct ``x``. The array must have the same shape as ``x`` and have the default array index data type. .. note:: @@ -158,7 +158,7 @@ def unique_values(x: array, /) -> array: Returns ------- out: array - an array containing the set of unique elements in ``x``. The returned array must have the same data type as ``x``. + a one-dimensional array containing the set of unique elements in ``x``. The returned array must have the same data type as ``x``. .. note:: The order of unique elements is not specified and may vary between implementations. From 9d200ea3478a27bc3e93e3543a73369cfdc60ec0 Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 22 Feb 2024 12:40:58 -0800 Subject: [PATCH 24/57] Add `repeat` to the specification (#690) --- .../manipulation_functions.rst | 1 + .../_draft/manipulation_functions.py | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/spec/draft/API_specification/manipulation_functions.rst b/spec/draft/API_specification/manipulation_functions.rst index 680efb55f..395c1c3e2 100644 --- a/spec/draft/API_specification/manipulation_functions.rst +++ b/spec/draft/API_specification/manipulation_functions.rst @@ -25,6 +25,7 @@ Objects in API flip moveaxis permute_dims + repeat reshape roll squeeze diff --git a/src/array_api_stubs/_draft/manipulation_functions.py b/src/array_api_stubs/_draft/manipulation_functions.py index 74538a8a3..4d7a17dda 100644 --- a/src/array_api_stubs/_draft/manipulation_functions.py +++ b/src/array_api_stubs/_draft/manipulation_functions.py @@ -6,6 +6,7 @@ "flip", "moveaxis", "permute_dims", + "repeat", "reshape", "roll", "squeeze", @@ -159,6 +160,53 @@ def permute_dims(x: array, /, axes: Tuple[int, ...]) -> array: """ +def repeat( + x: array, + repeats: Union[int, array], + /, + *, + axis: Optional[int] = None, +) -> array: + """ + Repeats each element of an array a specified number of times on a per-element basis. + + .. admonition:: Data-dependent output shape + :class: important + + When ``repeats`` is an array, the shape of the output array for this function depends on the data values in the ``repeats`` array; hence, array libraries which build computation graphs (e.g., JAX, Dask, etc.) may find this function difficult to implement without knowing the values in ``repeats``. Accordingly, such libraries may choose to omit support for ``repeats`` arrays; however, conforming implementations must support providing a literal ``int``. See :ref:`data-dependent-output-shapes` section for more details. + + Parameters + ---------- + x: array + input array containing elements to repeat. + repeats: Union[int, array] + the number of repetitions for each element. + + If ``axis`` is ``None``, let ``N = prod(x.shape)`` and + + - if ``repeats`` is an array, ``repeats`` must be broadcast compatible with the shape ``(N,)`` (i.e., be a one-dimensional array having shape ``(1,)`` or ``(N,)``). + - if ``repeats`` is an integer, ``repeats`` must be broadcasted to the shape `(N,)`. + + If ``axis`` is not ``None``, let ``M = x.shape[axis]`` and + + - if ``repeats`` is an array, ``repeats`` must be broadcast compatible with the shape ``(M,)`` (i.e., be a one-dimensional array having shape ``(1,)`` or ``(M,)``). + - if ``repeats`` is an integer, ``repeats`` must be broadcasted to the shape ``(M,)``. + + If ``repeats`` is an array, the array must have an integer data type. + + .. note:: + For specification-conforming array libraries supporting hardware acceleration, providing an array for ``repeats`` may cause device synchronization due to an unknown output shape. For those array libraries where synchronization concerns are applicable, conforming array libraries are advised to include a warning in their documentation regarding potential performance degradation when ``repeats`` is an array. + + axis: Optional[int] + the axis (dimension) along which to repeat elements. If ``axis`` is `None`, the function must flatten the input array ``x`` and then repeat elements of the flattened input array and return the result as a one-dimensional output array. A flattened input array must be flattened in row-major, C-style order. Default: ``None``. + + Returns + ------- + out: array + an output array containing repeated elements. The returned array must have the same data type as ``x``. If ``axis`` is ``None``, the returned array must be a one-dimensional array; otherwise, the returned array must have the same shape as ``x``, except for the axis (dimension) along which elements were repeated. + """ + + def reshape( x: array, /, shape: Tuple[int, ...], *, copy: Optional[bool] = None ) -> array: From cb44271dda95468e596fe7dcd0c08462bc7e0d60 Mon Sep 17 00:00:00 2001 From: Athan Date: Sun, 25 Feb 2024 23:54:19 -0800 Subject: [PATCH 25/57] docs: add changelog for v2023.12 specification revision PR-URL: https://github.com/data-apis/array-api/pull/754 Reviewed-by: Athan Reines --- CHANGELOG.md | 132 ++++++++++++++++++ .../_2022_12/indexing_functions.py | 2 +- src/array_api_stubs/_draft/array_object.py | 25 +++- .../_draft/creation_functions.py | 3 + .../_draft/data_type_functions.py | 3 + .../_draft/elementwise_functions.py | 12 ++ src/array_api_stubs/_draft/fft.py | 27 ++++ .../_draft/indexing_functions.py | 5 +- src/array_api_stubs/_draft/info.py | 27 ++++ src/array_api_stubs/_draft/linalg.py | 12 +- .../_draft/linear_algebra_functions.py | 12 +- .../_draft/manipulation_functions.py | 20 +++ .../_draft/searching_functions.py | 2 + src/array_api_stubs/_draft/set_functions.py | 12 ++ .../_draft/statistical_functions.py | 14 ++ 15 files changed, 298 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0897e4ea5..eace3f0a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,138 @@ This changelog is organized by specification version and notes all changes with respect to the previous version. Within the section for a specific version (e.g., v2022.12), separate sections are used for (a) changes to existing APIs and requirements, (b) new APIs and new requirements, and (c) errata. +## v2023.12 + +### Updates + +> Updates to existing APIs and requirements. + +#### Normative + +- Clarify expectations concerning exception handling ([gh-613](https://github.com/data-apis/array-api/pull/613)) +- Clarify that the constant `newaxis` is an alias of `None` ([gh-687](https://github.com/data-apis/array-api/pull/687)) +- Add design discussion on lazy versus eager implementations ([gh-708](https://github.com/data-apis/array-api/pull/708)) +- Revise guidance to require a minimum upper bound for supported ranks ([gh-702](https://github.com/data-apis/array-api/pull/702)) +- Relax design requirements for positional and keyword-only arguments ([gh-730](https://github.com/data-apis/array-api/pull/730)) +- Add recommendation to `__dlpack__` for handling read-only arrays ([gh-749](https://github.com/data-apis/array-api/pull/749)) + +#### APIs + +- `__bool__`: allow lazy implementations to raise from intrinsically eager functions ([gh-652](https://github.com/data-apis/array-api/pull/652)) +- `__complex__`: allow lazy implementations to raise from intrinsically eager functions ([gh-652](https://github.com/data-apis/array-api/pull/652)) +- `__dlpack__`: add `max_version` keyword argument to support versioning ([gh-602](https://github.com/data-apis/array-api/pull/602)) +- `__dlpack__`: add `dl_device` and `copy` keyword arguments ([gh-741](https://github.com/data-apis/array-api/pull/741)) +- `__float__`: allow lazy implementations to raise from intrinsically eager functions ([gh-652](https://github.com/data-apis/array-api/pull/652)) +- `__index__`: allow lazy implementations to raise from intrinsically eager functions ([gh-652](https://github.com/data-apis/array-api/pull/652)) +- `__int__`: allow lazy implementations to raise from intrinsically eager functions ([gh-652](https://github.com/data-apis/array-api/pull/652)) +- `astype`: add support for an optional `device` keyword argument ([gh-665](https://github.com/data-apis/array-api/pull/665)) +- `from_dlpack`: require exceptions to address unsupported use cases ([gh-709](https://github.com/data-apis/array-api/pull/709)) +- `from_dlpack`: add support for `copy` and `device` keywords ([gh-741](https://github.com/data-apis/array-api/pull/741)) +- `max`: clarify that the order of signed zeros is unspecified ([gh-751](https://github.com/data-apis/array-api/pull/751)) +- `min`: clarify that the order of signed zeros is unspecified ([gh-751](https://github.com/data-apis/array-api/pull/751)) +- `take`: explicitly leave out-of-bounds behavior unspecified ([gh-701](https://github.com/data-apis/array-api/pull/701)) +- `tensordot`: allow negative axes ([gh-625](https://github.com/data-apis/array-api/pull/625)) +- `to_device`: clarify behavior when a provided `device` object corresponds to the same device on which an array instance resides ([gh-742](https://github.com/data-apis/array-api/pull/742)) +- `unique_all`: clarify the shape of the array containing unique values and the order of returned counts ([gh-752](https://github.com/data-apis/array-api/pull/752)) +- `unique_counts`: clarify the shape of the array containing unique values and the order of returned counts ([gh-752](https://github.com/data-apis/array-api/pull/752)) +- `unique_inverse`: clarify the shape of the array containing unique values ([gh-752](https://github.com/data-apis/array-api/pull/752)) +- `unique_values`: clarify the shape of the returned array ([gh-752](https://github.com/data-apis/array-api/pull/752)) + +#### Extensions + +> Updates to APIs and requirements included as part of specification extensions. + +- `fft.*`: clarify behavior of the `n` and `s` keyword arguments and the expected output array shape ([gh-720](https://github.com/data-apis/array-api/pull/720) and [gh-746](https://github.com/data-apis/array-api/pull/746); backported to v2022.12 revision of Array API specification) + +* * * + +### Additions + +> New APIs and requirements added to the specification. + +#### APIs + +The following APIs were added to the specification: + +- `__array_namespace_info__`: namespace with Array API namespace inspection utilities ([gh-689](https://github.com/data-apis/array-api/pull/689)) +- `clip`: clamp each element of an input array to a specified range ([gh-715](https://github.com/data-apis/array-api/pull/715)) +- `copysign`: compose a floating-point value with the magnitude of a `x1_i` and the sign of `x2_i` ([gh-693](https://github.com/data-apis/array-api/pull/693)) +- `cumulative_sum`: calculate the cumulative sum ([gh-653](https://github.com/data-apis/array-api/pull/653)) +- `hypot`: compute the square root of the sum of squares for each element in an array ([gh-703](https://github.com/data-apis/array-api/pull/703)) +- `maximum`: compute the maximum value for each element of an array relative to the respective element in another array ([gh-713](https://github.com/data-apis/array-api/pull/713)) +- `minimum`: compute the minimum value for each element of an array relative to the respective element in another array ([gh-713](https://github.com/data-apis/array-api/pull/713)) +- `moveaxis`: move array axes to new positions, while leaving other axes in their original positions ([gh-656](https://github.com/data-apis/array-api/pull/656)) +- `repeat`: repeat each element of an array a specified number of times ([gh-690](https://github.com/data-apis/array-api/pull/690)) +- `searchsorted`: find the indices into `x1` such that, if the corresponding elements in `x2` were inserted before the indices, the order of `x1`, when sorted in ascending order, would be preserved ([gh-699](https://github.com/data-apis/array-api/pull/699)) +- `signbit`: determine whether the sign bit is set for each element of an array ([gh-705](https://github.com/data-apis/array-api/pull/705)) +- `tile`: construct an array by tiling an input array ([gh-692](https://github.com/data-apis/array-api/pull/692)) +- `unstack`: split an array into a sequence of arrays along a given axis ([gh-604](https://github.com/data-apis/array-api/pull/604)) + +#### Inspection APIs + +The following inspection APIs were added to the specification: + +- `capabilities`: return a dictionary of array library capabilities ([gh-689](https://github.com/data-apis/array-api/pull/689)) +- `default_device`: return the default device ([gh-689](https://github.com/data-apis/array-api/pull/689)) +- `default_dtypes`: return a dictionary containing default data types ([gh-689](https://github.com/data-apis/array-api/pull/689)) +- `dtypes`: return a dictionary support Array API data types ([gh-689](https://github.com/data-apis/array-api/pull/689)) +- `devices`: return a list of supported devices ([gh-689](https://github.com/data-apis/array-api/pull/689)) + +* * * + +### Breaking Changes + +The following is a list of breaking changes relative to the previous version of the specification: + +- `prod`: when provided a floating-point array, the function must return a floating-point array having the same data type ([gh-744](https://github.com/data-apis/array-api/pull/744)) +- `sum`: when provided a floating-point array, the function must return a floating-point array having the same data type ([gh-744](https://github.com/data-apis/array-api/pull/744)) +- `vecdot`: only allow a negative integer for the `axis` keyword argument ([gh-740](https://github.com/data-apis/array-api/pull/740)) + +#### Extensions + +The following is a list of breaking changes in specification extensions relative to the previous version of the specification: + +- `fft.fft`: require the input array to have a complex-valued floating-point data type and require that the output array have the same data type as the input array ([gh-720](https://github.com/data-apis/array-api/pull/720); backported to v2022.12 revision of Array API specification) +- `fft.fftn`: require the input array to have a complex-valued floating-point data type and require that the output array have the same data type as the input array ([gh-720](https://github.com/data-apis/array-api/pull/720); backported to v2022.12 revision of Array API specification) +- `fft.hfft`: require the input array to have a complex-valued floating-point data type and require that the output array have a real-valued data type having the same precision as the input array ([gh-720](https://github.com/data-apis/array-api/pull/720); backported to v2022.12 revision of Array API specification) +- `fft.ifft`: require the input array to have a complex-valued floating-point data type and require that the output array have the same data type as the input array ([gh-720](https://github.com/data-apis/array-api/pull/720); backported to v2022.12 revision of Array API specification) +- `fft.ifftn`: require the input array to have a complex-valued floating-point data type and require that the output array have the same data type as the input array ([gh-720](https://github.com/data-apis/array-api/pull/720); backported to v2022.12 revision of Array API specification) +- `fft.irfft`: require the output array have a real-valued floating-point data type having the same precision as the input array ([gh-720](https://github.com/data-apis/array-api/pull/720); backported to v2022.12 revision of Array API specification) +- `fft.irfftn`: require the output array have a real-valued floating-point data type having the same precision as the input array ([gh-720](https://github.com/data-apis/array-api/pull/720); backported to v2022.12 revision of Array API specification) +- `fft.fftfreq`: require the output array have the default real-valued floating-point data type ([gh-720](https://github.com/data-apis/array-api/pull/720); backported to v2022.12 revision of Array API specification) +- `fft.rfftfreq`: require the output array have the default real-valued floating-point data type ([gh-720](https://github.com/data-apis/array-api/pull/720); backported to v2022.12 revision of Array API specification) +- `linalg.cross`: broadcast only along non-compute axes and only allow a negative integer for the `axis` keyword argument ([gh-740](https://github.com/data-apis/array-api/pull/740)) +- `linalg.trace`: when provided a floating-point array, the function must return a floating-point array having the same data type ([gh-744](https://github.com/data-apis/array-api/pull/744)) + +* * * + +### Errata + +The following is a list of fixes and points of clarification with regard to the previous version of the specification: + +- `__getitem__`: clarify typing to allow `None` in indexing ([gh-674](https://github.com/data-apis/array-api/pull/674) and [gh-687](https://github.com/data-apis/array-api/pull/687)) +- `__ge__`: clarify that the operation is only defined for arrays having real-valued data types ([gh-736](https://github.com/data-apis/array-api/pull/736)) +- `__gt__`: clarify that the operation is only defined for arrays having real-valued data types ([gh-736](https://github.com/data-apis/array-api/pull/736)) +- `__le__`: clarify that the operation is only defined for arrays having real-valued data types ([gh-736](https://github.com/data-apis/array-api/pull/736)) +- `__lt__`: clarify that the operation is only defined for arrays having real-valued data types ([gh-736](https://github.com/data-apis/array-api/pull/736)) +- `abs`: fix typo in return value description ([gh-633](https://github.com/data-apis/array-api/pull/633)) +- `asarray`: fix typo in `device` keyword argument description ([gh-681](https://github.com/data-apis/array-api/pull/681)) +- `conj`: fix typo in parameter description ([gh-706](https://github.com/data-apis/array-api/pull/706)) +- `finfo_object`: fix missing `dtype` attribute ([gh-639](https://github.com/data-apis/array-api/pull/639)) +- `fft.*`: fix various typing issues ([gh-720](https://github.com/data-apis/array-api/pull/720)) +- `iinfo_object`: fix missing `dtype` attribute ([gh-639](https://github.com/data-apis/array-api/pull/639)) +- `linalg.qr`: fix typo in function description ([gh-661](https://github.com/data-apis/array-api/pull/661)) +- `linalg.cholesky`: fix typo in function description ([gh-677](https://github.com/data-apis/array-api/pull/677)) +- `linalg.svd`: fix return type ([gh-619](https://github.com/data-apis/array-api/pull/619)) +- `prod`: clarify type promotion behavior when `dtype=None` ([gh-666](https://github.com/data-apis/array-api/pull/666)) +- `sum`: clarify type promotion behavior when `dtype=None` ([gh-666](https://github.com/data-apis/array-api/pull/666)) +- `take`: fix typing for optional `axis` keyword argument ([gh-644](https://github.com/data-apis/array-api/pull/644)) +- `tensordot`: fix typo in parameter description ([gh-622](https://github.com/data-apis/array-api/pull/622)) +- `trace`: clarify type promotion behavior when `dtype=None` ([gh-666](https://github.com/data-apis/array-api/pull/666)) +- `vecdot`: fix definition of complex inner product ([gh-723](https://github.com/data-apis/array-api/pull/723)) + +* * * + ## v2022.12 ### Updates diff --git a/src/array_api_stubs/_2022_12/indexing_functions.py b/src/array_api_stubs/_2022_12/indexing_functions.py index 0cbad55ab..5dd74461d 100644 --- a/src/array_api_stubs/_2022_12/indexing_functions.py +++ b/src/array_api_stubs/_2022_12/indexing_functions.py @@ -14,7 +14,7 @@ def take(x: array, indices: array, /, *, axis: Optional[int] = None) -> array: input array. indices: array array indices. The array must be one-dimensional and have an integer data type. - axis: int + axis: Optional[int] axis over which to select values. If ``axis`` is negative, the function must determine the axis along which to select values by counting from the last dimension. If ``x`` is a one-dimensional array, providing an ``axis`` is optional; however, if ``x`` has more than one dimension, providing an ``axis`` is required. diff --git a/src/array_api_stubs/_draft/array_object.py b/src/array_api_stubs/_draft/array_object.py index 2a33a60ae..6dd70c278 100644 --- a/src/array_api_stubs/_draft/array_object.py +++ b/src/array_api_stubs/_draft/array_object.py @@ -247,6 +247,9 @@ def __bool__(self: array, /) -> bool: .. versionchanged:: 2022.12 Added boolean and complex data type support. + + .. versionchanged:: 2023.12 + Allowed lazy implementations to error. """ def __complex__(self: array, /) -> complex: @@ -285,6 +288,9 @@ def __complex__(self: array, /) -> complex: The Python language requires the return value to be of type ``complex``. Lazy implementations are therefore not able to return any kind of lazy/delayed object here and should raise a ``ValueError`` instead. .. versionadded:: 2022.12 + + .. versionchanged:: 2023.12 + Allowed lazy implementations to error. """ def __dlpack__( @@ -451,10 +457,13 @@ def __dlpack__( memory isn't modified). .. versionchanged:: 2022.12 - Added BufferError. + Added BufferError. .. versionchanged:: 2023.12 - Added the ``max_version``, ``dl_device``, and ``copy`` keywords. + Added the ``max_version``, ``dl_device``, and ``copy`` keywords. + + .. versionchanged:: 2023.12 + Added recommendation for handling read-only arrays. """ def __dlpack_device__(self: array, /) -> Tuple[Enum, int]: @@ -539,6 +548,9 @@ def __float__(self: array, /) -> float: .. versionchanged:: 2022.12 Added boolean and complex data type support. + + .. versionchanged:: 2023.12 + Allowed lazy implementations to error. """ def __floordiv__(self: array, other: Union[int, float, array], /) -> array: @@ -664,6 +676,9 @@ def __index__(self: array, /) -> int: **Lazy implementations** The Python language requires the return value to be of type ``int``. Lazy implementations are therefore not able to return any kind of lazy/delayed object here and should raise a ``ValueError`` instead. + + .. versionchanged:: 2023.12 + Allowed lazy implementations to error. """ def __int__(self: array, /) -> int: @@ -711,6 +726,9 @@ def __int__(self: array, /) -> int: .. versionchanged:: 2022.12 Added boolean and complex data type support. + + .. versionchanged:: 2023.12 + Allowed lazy implementations to error. """ def __invert__(self: array, /) -> array: @@ -1192,6 +1210,9 @@ def to_device( - When a provided ``device`` object corresponds to the same device on which an array instance resides, implementations may choose to perform an explicit copy or return ``self``. - If ``stream`` is provided, 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 array library's documentation. + + .. versionchanged:: 2023.12 + Clarified behavior when a provided ``device`` object corresponds to the device on which an array instance resides. """ diff --git a/src/array_api_stubs/_draft/creation_functions.py b/src/array_api_stubs/_draft/creation_functions.py index dec09db0c..6de79268e 100644 --- a/src/array_api_stubs/_draft/creation_functions.py +++ b/src/array_api_stubs/_draft/creation_functions.py @@ -289,6 +289,9 @@ def func(x, y): ... + .. versionchanged:: 2023.12 + Required exceptions to address unsupported use cases. + .. versionchanged:: 2023.12 Added device and copy support. """ diff --git a/src/array_api_stubs/_draft/data_type_functions.py b/src/array_api_stubs/_draft/data_type_functions.py index 81d518807..e12d349c6 100644 --- a/src/array_api_stubs/_draft/data_type_functions.py +++ b/src/array_api_stubs/_draft/data_type_functions.py @@ -57,6 +57,9 @@ def astype( .. versionchanged:: 2022.12 Added complex data type support. + + .. versionchanged:: 2023.12 + Added device keyword argument support. """ diff --git a/src/array_api_stubs/_draft/elementwise_functions.py b/src/array_api_stubs/_draft/elementwise_functions.py index 4dc0c949f..4462329d6 100644 --- a/src/array_api_stubs/_draft/elementwise_functions.py +++ b/src/array_api_stubs/_draft/elementwise_functions.py @@ -806,6 +806,8 @@ def clip( - If both ``min`` and ``max`` are ``None``, the elements of the returned array must equal the respective elements in ``x``. - If a broadcasted element in ``min`` is greater than a corresponding broadcasted element in ``max``, behavior is unspecified and thus implementation-dependent. - If ``x`` and either ``min`` or ``max`` have different data type kinds (e.g., integer versus floating-point), behavior is unspecified and thus implementation-dependent. + + .. versionadded:: 2023.12 """ @@ -880,6 +882,8 @@ def copysign(x1: array, x2: array, /) -> array: - If ``x2_i`` is greater than ``0``, the result is ``NaN`` with a sign bit of ``0``. - If ``x2_i`` is ``NaN`` and the sign bit of ``x2_i`` is ``1``, the result is ``NaN`` with a sign bit of ``1``. - If ``x2_i`` is ``NaN`` and the sign bit of ``x2_i`` is ``0``, the result is ``NaN`` with a sign bit of ``0``. + + .. versionadded:: 2023.12 """ @@ -1410,6 +1414,8 @@ def hypot(x1: array, x2: array, /) -> array: IEEE 754-2019 requires support for subnormal (a.k.a., denormal) numbers, which are useful for supporting gradual underflow. However, hardware support for subnormal numbers is not universal, and many platforms (e.g., accelerators) and compilers support toggling denormals-are-zero (DAZ) and/or flush-to-zero (FTZ) behavior to increase performance and to guard against timing attacks. Accordingly, conforming implementations may vary in their support for subnormal numbers. + + .. versionadded:: 2023.12 """ @@ -1929,6 +1935,8 @@ def maximum(x1: array, x2: array, /) -> array: For floating-point operands, - If either ``x1_i`` or ``x2_i`` is ``NaN``, the result is ``NaN``. + + .. versionadded:: 2023.12 """ @@ -1960,6 +1968,8 @@ def minimum(x1: array, x2: array, /) -> array: For floating-point operands, - If either ``x1_i`` or ``x2_i`` is ``NaN``, the result is ``NaN``. + + .. versionadded:: 2023.12 """ @@ -2390,6 +2400,8 @@ def signbit(x: array, /) -> array: - If ``x_i`` is a negative (i.e., less than ``0``) finite number, the result is ``True``. - If ``x_i`` is ``NaN`` and the sign bit of ``x_i`` is ``0``, the result is ``False``. - If ``x_i`` is ``NaN`` and the sign bit of ``x_i`` is ``1``, the result is ``True``. + + .. versionadded:: 2023.12 """ diff --git a/src/array_api_stubs/_draft/fft.py b/src/array_api_stubs/_draft/fft.py index bdd7a9c83..4e8131c8b 100644 --- a/src/array_api_stubs/_draft/fft.py +++ b/src/array_api_stubs/_draft/fft.py @@ -64,6 +64,9 @@ def fft( ----- .. versionadded:: 2022.12 + + .. versionchanged:: 2023.12 + Required the input array have a complex-valued floating-point data type and required that the output array have the same data type as the input array. """ @@ -113,6 +116,9 @@ def ifft( ----- .. versionadded:: 2022.12 + + .. versionchanged:: 2023.12 + Required the input array have a complex-valued floating-point data type and required that the output array have the same data type as the input array. """ @@ -168,6 +174,9 @@ def fftn( ----- .. versionadded:: 2022.12 + + .. versionchanged:: 2023.12 + Required the input array have a complex-valued floating-point data type and required that the output array have the same data type as the input array. """ @@ -223,6 +232,9 @@ def ifftn( ----- .. versionadded:: 2022.12 + + .. versionchanged:: 2023.12 + Required the input array have a complex-valued floating-point data type and required that the output array have the same data type as the input array. """ @@ -323,6 +335,9 @@ def irfft( - In order to return an array having an odd number of elements along the transformed axis, the function must be provided an odd integer for ``n``. .. versionadded:: 2022.12 + + .. versionchanged:: 2023.12 + Required the output array have a real-valued floating-point data type having the same precision as the input array. """ @@ -435,6 +450,9 @@ def irfftn( - In order to return an array having an odd number of elements along the last transformed axis, the function must be provided an odd integer for ``s[-1]``. .. versionadded:: 2022.12 + + .. versionchanged:: 2023.12 + Required the output array have a real-valued floating-point data type having the same precision as the input array. """ @@ -481,6 +499,9 @@ def hfft( ----- .. versionadded:: 2022.12 + + .. versionchanged:: 2023.12 + Required the input array to have a complex-valued floating-point data type and required that the output array have a real-valued data type having the same precision as the input array. """ @@ -559,6 +580,9 @@ def fftfreq(n: int, /, *, d: float = 1.0, device: Optional[device] = None) -> ar ----- .. versionadded:: 2022.12 + + .. versionchanged:: 2023.12 + Required the output array have the default real-valued floating-point data type. """ @@ -593,6 +617,9 @@ def rfftfreq(n: int, /, *, d: float = 1.0, device: Optional[device] = None) -> a ----- .. versionadded:: 2022.12 + + .. versionchanged:: 2023.12 + Required the output array have the default real-valued floating-point data type. """ diff --git a/src/array_api_stubs/_draft/indexing_functions.py b/src/array_api_stubs/_draft/indexing_functions.py index f3bf0fc14..35066a4a2 100644 --- a/src/array_api_stubs/_draft/indexing_functions.py +++ b/src/array_api_stubs/_draft/indexing_functions.py @@ -20,7 +20,7 @@ def take(x: array, indices: array, /, *, axis: Optional[int] = None) -> array: .. note:: This specification does not require bounds checking. The behavior for out-of-bounds indices is left unspecified. - axis: int + axis: Optional[int] axis over which to select values. If ``axis`` is negative, the function must determine the axis along which to select values by counting from the last dimension. If ``x`` is a one-dimensional array, providing an ``axis`` is optional; however, if ``x`` has more than one dimension, providing an ``axis`` is required. @@ -34,4 +34,7 @@ def take(x: array, indices: array, /, *, axis: Optional[int] = None) -> array: ----- .. versionadded:: 2022.12 + + .. versionchanged:: 2023.12 + Out-of-bounds behavior is explicitly left unspecified. """ diff --git a/src/array_api_stubs/_draft/info.py b/src/array_api_stubs/_draft/info.py index f70418405..b755ca2c0 100644 --- a/src/array_api_stubs/_draft/info.py +++ b/src/array_api_stubs/_draft/info.py @@ -43,6 +43,8 @@ def __array_namespace_info__() -> Info: info.dtypes() info.default_dtypes() # ... + + .. versionadded: 2023.12 """ @@ -59,6 +61,11 @@ def capabilities() -> Capabilities: ------- out: Capabilities a dictionary of array library capabilities. + + Notes + ----- + + .. versionadded: 2023.12 """ @@ -70,6 +77,11 @@ def default_device() -> device: ------- out: device an object corresponding to the default device. + + Notes + ----- + + .. versionadded: 2023.12 """ @@ -101,6 +113,11 @@ def default_dtypes( ------- out: DefaultDataTypes a dictionary containing the default data type for respective data type kinds. + + Notes + ----- + + .. versionadded: 2023.12 """ @@ -151,6 +168,11 @@ def dtypes( .. note:: Dictionary keys must only consist of canonical names as defined in :ref:`data-types`. + + Notes + ----- + + .. versionadded: 2023.12 """ @@ -167,4 +189,9 @@ def devices() -> List[device]: ----- 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. + + Notes + ----- + + .. versionadded: 2023.12 """ diff --git a/src/array_api_stubs/_draft/linalg.py b/src/array_api_stubs/_draft/linalg.py index 903f928d8..0950e6937 100644 --- a/src/array_api_stubs/_draft/linalg.py +++ b/src/array_api_stubs/_draft/linalg.py @@ -102,15 +102,18 @@ def cross(x1: array, x2: array, /, *, axis: int = -1) -> array: Notes ----- + **Raises** + + - if the size of the axis over which to compute the cross product is not equal to ``3`` (before broadcasting) for both ``x1`` and ``x2``. + .. versionchanged:: 2022.12 Added support for broadcasting. .. versionchanged:: 2022.12 Added complex data type support. - **Raises** - - - if the size of the axis over which to compute the cross product is not equal to ``3`` (before broadcasting) for both ``x1`` and ``x2``. + .. versionchanged:: 2023.12 + Restricted broadcasting to only non-compute axes and required that ``axis`` be a negative integer. """ @@ -772,6 +775,9 @@ def trace(x: array, /, *, offset: int = 0, dtype: Optional[dtype] = None) -> arr .. versionchanged:: 2022.12 Added complex data type support. + + .. versionchanged:: 2023.12 + Required the function to return a floating-point array having the same data type as the input array when provided a floating-point array. """ diff --git a/src/array_api_stubs/_draft/linear_algebra_functions.py b/src/array_api_stubs/_draft/linear_algebra_functions.py index eea898a6b..da4c97743 100644 --- a/src/array_api_stubs/_draft/linear_algebra_functions.py +++ b/src/array_api_stubs/_draft/linear_algebra_functions.py @@ -116,6 +116,9 @@ def tensordot( .. versionchanged:: 2022.12 Added complex data type support. + + .. versionchanged:: 2023.12 + Allow negative axes. """ @@ -151,10 +154,13 @@ def vecdot(x1: array, x2: array, /, *, axis: int = -1) -> array: Notes ----- - .. versionchanged:: 2022.12 - Added complex data type support. - **Raises** - if the size of the axis over which to compute the dot product is not the same (before broadcasting) for both ``x1`` and ``x2``. + + .. versionchanged:: 2022.12 + Added complex data type support. + + .. versionchanged:: 2023.12 + Restricted ``axis`` to only negative integers. """ diff --git a/src/array_api_stubs/_draft/manipulation_functions.py b/src/array_api_stubs/_draft/manipulation_functions.py index 4d7a17dda..87f9511b0 100644 --- a/src/array_api_stubs/_draft/manipulation_functions.py +++ b/src/array_api_stubs/_draft/manipulation_functions.py @@ -139,6 +139,11 @@ def moveaxis( ------- out: array an array containing reordered axes. The returned array must have the same data type as ``x``. + + Notes + ----- + + .. versionadded:: 2023.12 """ @@ -204,6 +209,11 @@ def repeat( ------- out: array an output array containing repeated elements. The returned array must have the same data type as ``x``. If ``axis`` is ``None``, the returned array must be a one-dimensional array; otherwise, the returned array must have the same shape as ``x``, except for the axis (dimension) along which elements were repeated. + + Notes + ----- + + .. versionadded:: 2023.12 """ @@ -327,6 +337,11 @@ def tile(x: array, repetitions: Tuple[int, ...], /): ------- out: array a tiled output array. The returned array must have the same data type as ``x`` and must have a rank (i.e., number of dimensions) equal to ``max(N, M)``. If ``S`` is the shape of the tiled array after prepending singleton dimensions (if necessary) and ``r`` is the tuple of repetitions after prepending ones (if necessary), then the number of elements along each axis (dimension) must satisfy ``S[i]*r[i]``, where ``i`` refers to the ``i`` th axis (dimension). + + Notes + ----- + + .. versionadded:: 2023.12 """ @@ -345,4 +360,9 @@ def unstack(x: array, /, *, axis: int = 0) -> Tuple[array, ...]: ------- out: Tuple[array, ...] tuple of slices along the given dimension. All the arrays have the same shape. + + Notes + ----- + + .. versionadded:: 2023.12 """ diff --git a/src/array_api_stubs/_draft/searching_functions.py b/src/array_api_stubs/_draft/searching_functions.py index dda000e74..029459b9a 100644 --- a/src/array_api_stubs/_draft/searching_functions.py +++ b/src/array_api_stubs/_draft/searching_functions.py @@ -134,6 +134,8 @@ def searchsorted( For real-valued floating-point arrays, the sort order of NaNs and signed zeros is unspecified and thus implementation-dependent. Accordingly, when a real-valued floating-point array contains NaNs and signed zeros, what constitutes ascending order may vary among specification-conforming array libraries. While behavior for arrays containing NaNs and signed zeros is implementation-dependent, specification-conforming libraries should, however, ensure consistency with ``sort`` and ``argsort`` (i.e., if a value in ``x2`` is inserted into ``x1`` according to the corresponding index in the output array and ``sort`` is invoked on the resultant array, the sorted result should be an array in the same order). + + .. versionadded:: 2023.12 """ diff --git a/src/array_api_stubs/_draft/set_functions.py b/src/array_api_stubs/_draft/set_functions.py index 25a897355..5b7e9a56c 100644 --- a/src/array_api_stubs/_draft/set_functions.py +++ b/src/array_api_stubs/_draft/set_functions.py @@ -47,6 +47,9 @@ def unique_all(x: array, /) -> Tuple[array, array, array, array]: .. versionchanged:: 2022.12 Added complex data type support. + + .. versionchanged:: 2023.12 + Clarified flattening behavior and required the order of ``counts`` match the order of ``values``. """ @@ -89,6 +92,9 @@ def unique_counts(x: array, /) -> Tuple[array, array]: .. versionchanged:: 2022.12 Added complex data type support. + + .. versionchanged:: 2023.12 + Clarified flattening behavior and required the order of ``counts`` match the order of ``values``. """ @@ -131,6 +137,9 @@ def unique_inverse(x: array, /) -> Tuple[array, array]: .. versionchanged:: 2022.12 Added complex data type support. + + .. versionchanged:: 2023.12 + Clarified flattening behavior. """ @@ -168,4 +177,7 @@ def unique_values(x: array, /) -> array: .. versionchanged:: 2022.12 Added complex data type support. + + .. versionchanged:: 2023.12 + Required that the output array must be one-dimensional. """ diff --git a/src/array_api_stubs/_draft/statistical_functions.py b/src/array_api_stubs/_draft/statistical_functions.py index 2304ece80..9d3563e26 100644 --- a/src/array_api_stubs/_draft/statistical_functions.py +++ b/src/array_api_stubs/_draft/statistical_functions.py @@ -51,6 +51,8 @@ def cumulative_sum( **Special Cases** For both real-valued and complex floating-point operands, special cases must be handled as if the operation is implemented by successive application of :func:`~array_api.add`. + + .. versionadded:: 2023.12 """ @@ -92,6 +94,9 @@ def max( For floating-point operands, - If ``x_i`` is ``NaN``, the maximum value is ``NaN`` (i.e., ``NaN`` values propagate). + + .. versionchanged:: 2023.12 + Clarified that the order of signed zeros is implementation-defined. """ @@ -172,6 +177,9 @@ def min( For floating-point operands, - If ``x_i`` is ``NaN``, the minimum value is ``NaN`` (i.e., ``NaN`` values propagate). + + .. versionchanged:: 2023.12 + Clarified that the order of signed zeros is implementation-defined. """ @@ -222,6 +230,9 @@ def prod( .. versionchanged:: 2022.12 Added complex data type support. + + .. versionchanged:: 2023.12 + Required the function to return a floating-point array having the same data type as the input array when provided a floating-point array. """ @@ -314,6 +325,9 @@ def sum( .. versionchanged:: 2022.12 Added complex data type support. + + .. versionchanged:: 2023.12 + Required the function to return a floating-point array having the same data type as the input array when provided a floating-point array. """ From fbc9ddec2f0366df069901a1d99580b5791f8772 Mon Sep 17 00:00:00 2001 From: Athan Reines Date: Mon, 26 Feb 2024 00:04:18 -0800 Subject: [PATCH 26/57] build: create new release --- .DS_Store | Bin 0 -> 6148 bytes LICENSE | 2 +- Makefile | 3 +- .../API_specification/array_object.rst | 308 ++ .../API_specification/broadcasting.rst | 128 + spec/2023.12/API_specification/constants.rst | 26 + .../API_specification/creation_functions.rst | 36 + .../API_specification/data_type_functions.rst | 26 + spec/2023.12/API_specification/data_types.rst | 143 + .../elementwise_functions.rst | 84 + .../function_and_method_signatures.rst | 63 + spec/2023.12/API_specification/index.rst | 41 + spec/2023.12/API_specification/indexing.rst | 208 ++ .../API_specification/indexing_functions.rst | 23 + spec/2023.12/API_specification/inspection.rst | 40 + .../linear_algebra_functions.rst | 23 + .../manipulation_functions.rst | 34 + .../API_specification/searching_functions.rst | 27 + .../API_specification/set_functions.rst | 24 + .../API_specification/sorting_functions.rst | 31 + .../statistical_functions.rst | 28 + .../API_specification/type_promotion.rst | 148 + .../API_specification/utility_functions.rst | 22 + spec/2023.12/API_specification/version.rst | 22 + spec/2023.12/assumptions.md | 77 + spec/2023.12/benchmark_suite.md | 3 + spec/2023.12/changelog.rst | 5 + spec/2023.12/conf.py | 13 + spec/2023.12/design_topics/C_API.rst | 94 + spec/2023.12/design_topics/accuracy.rst | 77 + .../2023.12/design_topics/complex_numbers.rst | 61 + .../copies_views_and_mutation.rst | 77 + .../data_dependent_output_shapes.rst | 15 + .../design_topics/data_interchange.rst | 105 + spec/2023.12/design_topics/device_support.rst | 112 + spec/2023.12/design_topics/exceptions.rst | 28 + spec/2023.12/design_topics/index.rst | 18 + spec/2023.12/design_topics/lazy_eager.rst | 43 + spec/2023.12/design_topics/parallelism.rst | 24 + spec/2023.12/design_topics/static_typing.rst | 50 + .../fourier_transform_functions.rst | 45 + spec/2023.12/extensions/index.rst | 34 + .../extensions/linear_algebra_functions.rst | 116 + spec/2023.12/future_API_evolution.md | 60 + spec/2023.12/index.rst | 37 + spec/2023.12/license.rst | 9 + spec/2023.12/purpose_and_scope.md | 470 +++ spec/2023.12/usage_data.md | 86 + spec/2023.12/use_cases.md | 235 ++ spec/2023.12/verification_test_suite.md | 62 + spec/_ghpages/versions.json | 1 + src/.DS_Store | Bin 0 -> 6148 bytes src/_array_api_conf.py | 2 +- src/array_api_stubs/_2023_12/__init__.py | 25 + src/array_api_stubs/_2023_12/_types.py | 144 + src/array_api_stubs/_2023_12/array_object.py | 1219 ++++++++ src/array_api_stubs/_2023_12/constants.py | 30 + .../_2023_12/creation_functions.py | 647 ++++ .../_2023_12/data_type_functions.py | 228 ++ src/array_api_stubs/_2023_12/data_types.py | 22 + .../_2023_12/elementwise_functions.py | 2774 +++++++++++++++++ src/array_api_stubs/_2023_12/fft.py | 683 ++++ .../_2023_12/indexing_functions.py | 40 + src/array_api_stubs/_2023_12/info.py | 197 ++ src/array_api_stubs/_2023_12/linalg.py | 850 +++++ .../_2023_12/linear_algebra_functions.py | 166 + .../_2023_12/manipulation_functions.py | 368 +++ .../_2023_12/searching_functions.py | 159 + src/array_api_stubs/_2023_12/set_functions.py | 183 ++ .../_2023_12/sorting_functions.py | 58 + .../_2023_12/statistical_functions.py | 374 +++ .../_2023_12/utility_functions.py | 86 + src/array_api_stubs/__init__.py | 2 +- 73 files changed, 11700 insertions(+), 4 deletions(-) create mode 100644 .DS_Store create mode 100644 spec/2023.12/API_specification/array_object.rst create mode 100644 spec/2023.12/API_specification/broadcasting.rst create mode 100644 spec/2023.12/API_specification/constants.rst create mode 100644 spec/2023.12/API_specification/creation_functions.rst create mode 100644 spec/2023.12/API_specification/data_type_functions.rst create mode 100644 spec/2023.12/API_specification/data_types.rst create mode 100644 spec/2023.12/API_specification/elementwise_functions.rst create mode 100644 spec/2023.12/API_specification/function_and_method_signatures.rst create mode 100644 spec/2023.12/API_specification/index.rst create mode 100644 spec/2023.12/API_specification/indexing.rst create mode 100644 spec/2023.12/API_specification/indexing_functions.rst create mode 100644 spec/2023.12/API_specification/inspection.rst create mode 100644 spec/2023.12/API_specification/linear_algebra_functions.rst create mode 100644 spec/2023.12/API_specification/manipulation_functions.rst create mode 100644 spec/2023.12/API_specification/searching_functions.rst create mode 100644 spec/2023.12/API_specification/set_functions.rst create mode 100644 spec/2023.12/API_specification/sorting_functions.rst create mode 100644 spec/2023.12/API_specification/statistical_functions.rst create mode 100644 spec/2023.12/API_specification/type_promotion.rst create mode 100644 spec/2023.12/API_specification/utility_functions.rst create mode 100644 spec/2023.12/API_specification/version.rst create mode 100644 spec/2023.12/assumptions.md create mode 100644 spec/2023.12/benchmark_suite.md create mode 100644 spec/2023.12/changelog.rst create mode 100644 spec/2023.12/conf.py create mode 100644 spec/2023.12/design_topics/C_API.rst create mode 100644 spec/2023.12/design_topics/accuracy.rst create mode 100644 spec/2023.12/design_topics/complex_numbers.rst create mode 100644 spec/2023.12/design_topics/copies_views_and_mutation.rst create mode 100644 spec/2023.12/design_topics/data_dependent_output_shapes.rst create mode 100644 spec/2023.12/design_topics/data_interchange.rst create mode 100644 spec/2023.12/design_topics/device_support.rst create mode 100644 spec/2023.12/design_topics/exceptions.rst create mode 100644 spec/2023.12/design_topics/index.rst create mode 100644 spec/2023.12/design_topics/lazy_eager.rst create mode 100644 spec/2023.12/design_topics/parallelism.rst create mode 100644 spec/2023.12/design_topics/static_typing.rst create mode 100644 spec/2023.12/extensions/fourier_transform_functions.rst create mode 100644 spec/2023.12/extensions/index.rst create mode 100644 spec/2023.12/extensions/linear_algebra_functions.rst create mode 100644 spec/2023.12/future_API_evolution.md create mode 100644 spec/2023.12/index.rst create mode 100644 spec/2023.12/license.rst create mode 100644 spec/2023.12/purpose_and_scope.md create mode 100644 spec/2023.12/usage_data.md create mode 100644 spec/2023.12/use_cases.md create mode 100644 spec/2023.12/verification_test_suite.md create mode 100644 src/.DS_Store create mode 100644 src/array_api_stubs/_2023_12/__init__.py create mode 100644 src/array_api_stubs/_2023_12/_types.py create mode 100644 src/array_api_stubs/_2023_12/array_object.py create mode 100644 src/array_api_stubs/_2023_12/constants.py create mode 100644 src/array_api_stubs/_2023_12/creation_functions.py create mode 100644 src/array_api_stubs/_2023_12/data_type_functions.py create mode 100644 src/array_api_stubs/_2023_12/data_types.py create mode 100644 src/array_api_stubs/_2023_12/elementwise_functions.py create mode 100644 src/array_api_stubs/_2023_12/fft.py create mode 100644 src/array_api_stubs/_2023_12/indexing_functions.py create mode 100644 src/array_api_stubs/_2023_12/info.py create mode 100644 src/array_api_stubs/_2023_12/linalg.py create mode 100644 src/array_api_stubs/_2023_12/linear_algebra_functions.py create mode 100644 src/array_api_stubs/_2023_12/manipulation_functions.py create mode 100644 src/array_api_stubs/_2023_12/searching_functions.py create mode 100644 src/array_api_stubs/_2023_12/set_functions.py create mode 100644 src/array_api_stubs/_2023_12/sorting_functions.py create mode 100644 src/array_api_stubs/_2023_12/statistical_functions.py create mode 100644 src/array_api_stubs/_2023_12/utility_functions.py diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..cb2845bfd45fe0b2d4e5d60af22acccb797086ac GIT binary patch literal 6148 zcmeH~F$w}f3`G;&V!>uh%V|7-Hy9Q@ffo=|Y(z!TdXDZ-CJ3(9BJu;tpJXO1`-+{7 zh-iP?&P6&AY2l_avoJ74-pEzXvXjkybvYhR$31FRAH`W)!#f%5$2NroNPq-LfCNb3 zhX~lc4QnS=8A*TyNZ?7pz7Gj*nnO!f|8yYu2mozRcEj3d323qcG>4X|sK7L)2aQ(s zF~sWL4oz_`hnA|fT{MOdjVG&3F)*#|q6rC1vkLuHq@&4g1L!&>UK-q5|WOfMZ}Ffv*yH E03NCmz5oCK literal 0 HcmV?d00001 diff --git a/LICENSE b/LICENSE index e861ffccf..fb491e886 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020-2022 Consortium for Python Data API Standards contributors +Copyright (c) 2020-2024 Consortium for Python Data API Standards contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile index d23e05218..51e1fbf76 100644 --- a/Makefile +++ b/Makefile @@ -23,5 +23,6 @@ spec: touch "$(BUILDDIR)/.nojekyll" sphinx-build "$(SOURCEDIR)/2021.12" "$(BUILDDIR)/2021.12" $(SPHINXOPTS) sphinx-build "$(SOURCEDIR)/2022.12" "$(BUILDDIR)/2022.12" $(SPHINXOPTS) - cp -r "$(BUILDDIR)/2022.12" "$(BUILDDIR)/latest" + sphinx-build "$(SOURCEDIR)/2023.12" "$(BUILDDIR)/2023.12" $(SPHINXOPTS) + cp -r "$(BUILDDIR)/2023.12" "$(BUILDDIR)/latest" sphinx-build "$(SOURCEDIR)/draft" "$(BUILDDIR)/draft" $(SPHINXOPTS) diff --git a/spec/2023.12/API_specification/array_object.rst b/spec/2023.12/API_specification/array_object.rst new file mode 100644 index 000000000..f8a586ade --- /dev/null +++ b/spec/2023.12/API_specification/array_object.rst @@ -0,0 +1,308 @@ +.. _array-object: + +Array object +============ + + Array API specification for array object attributes and methods. + +A conforming implementation of the array API standard must provide and support an array object having the following attributes and methods. + +Furthermore, a conforming implementation of the array API standard must support, at minimum, array objects of rank (i.e., number of dimensions) ``0``, ``1``, ``2``, ``3``, and ``4`` and must explicitly document their maximum supported rank ``N``. + +.. note:: + Conforming implementations must support zero-dimensional arrays. + + Apart from array object attributes, such as ``ndim``, ``device``, and ``dtype``, all operations in this standard return arrays (or tuples of arrays), including those operations, such as ``mean``, ``var``, and ``std``, from which some common array libraries (e.g., NumPy) return scalar values. + + *Rationale: always returning arrays is necessary to (1) support accelerator libraries where non-array return values could force device synchronization and (2) support delayed execution models where an array represents a future value.* + +------------------------------------------------- + +.. _operators: + +Operators +--------- + +A conforming implementation of the array API standard must provide and support an array object supporting the following Python operators. + +Arithmetic Operators +~~~~~~~~~~~~~~~~~~~~ + +A conforming implementation of the array API standard must provide and support an array object supporting the following Python arithmetic operators. + +- ``+x``: :meth:`.array.__pos__` + + - `operator.pos(x) `_ + - `operator.__pos__(x) `_ + +- `-x`: :meth:`.array.__neg__` + + - `operator.neg(x) `_ + - `operator.__neg__(x) `_ + +- `x1 + x2`: :meth:`.array.__add__` + + - `operator.add(x1, x2) `_ + - `operator.__add__(x1, x2) `_ + +- `x1 - x2`: :meth:`.array.__sub__` + + - `operator.sub(x1, x2) `_ + - `operator.__sub__(x1, x2) `_ + +- `x1 * x2`: :meth:`.array.__mul__` + + - `operator.mul(x1, x2) `_ + - `operator.__mul__(x1, x2) `_ + +- `x1 / x2`: :meth:`.array.__truediv__` + + - `operator.truediv(x1,x2) `_ + - `operator.__truediv__(x1, x2) `_ + +- `x1 // x2`: :meth:`.array.__floordiv__` + + - `operator.floordiv(x1, x2) `_ + - `operator.__floordiv__(x1, x2) `_ + +- `x1 % x2`: :meth:`.array.__mod__` + + - `operator.mod(x1, x2) `_ + - `operator.__mod__(x1, x2) `_ + +- `x1 ** x2`: :meth:`.array.__pow__` + + - `operator.pow(x1, x2) `_ + - `operator.__pow__(x1, x2) `_ + +Arithmetic operators should be defined for arrays having real-valued data types. + +Array Operators +~~~~~~~~~~~~~~~ + +A conforming implementation of the array API standard must provide and support an array object supporting the following Python array operators. + +- `x1 @ x2`: :meth:`.array.__matmul__` + + - `operator.matmul(x1, x2) `_ + - `operator.__matmul__(x1, x2) `_ + +The matmul ``@`` operator should be defined for arrays having real-valued data types. + +Bitwise Operators +~~~~~~~~~~~~~~~~~ + +A conforming implementation of the array API standard must provide and support an array object supporting the following Python bitwise operators. + +- `~x`: :meth:`.array.__invert__` + + - `operator.inv(x) `_ + - `operator.invert(x) `_ + - `operator.__inv__(x) `_ + - `operator.__invert__(x) `_ + +- `x1 & x2`: :meth:`.array.__and__` + + - `operator.and(x1, x2) `_ + - `operator.__and__(x1, x2) `_ + +- `x1 | x2`: :meth:`.array.__or__` + + - `operator.or(x1, x2) `_ + - `operator.__or__(x1, x2) `_ + +- `x1 ^ x2`: :meth:`.array.__xor__` + + - `operator.xor(x1, x2) `_ + - `operator.__xor__(x1, x2) `_ + +- `x1 << x2`: :meth:`.array.__lshift__` + + - `operator.lshift(x1, x2) `_ + - `operator.__lshift__(x1, x2) `_ + +- `x1 >> x2`: :meth:`.array.__rshift__` + + - `operator.rshift(x1, x2) `_ + - `operator.__rshift__(x1, x2) `_ + +Bitwise operators should be defined for arrays having integer and boolean data types. + +Comparison Operators +~~~~~~~~~~~~~~~~~~~~ + +A conforming implementation of the array API standard must provide and support an array object supporting the following Python comparison operators. + +- `x1 < x2`: :meth:`.array.__lt__` + + - `operator.lt(x1, x2) `_ + - `operator.__lt__(x1, x2) `_ + +- `x1 <= x2`: :meth:`.array.__le__` + + - `operator.le(x1, x2) `_ + - `operator.__le__(x1, x2) `_ + +- `x1 > x2`: :meth:`.array.__gt__` + + - `operator.gt(x1, x2) `_ + - `operator.__gt__(x1, x2) `_ + +- `x1 >= x2`: :meth:`.array.__ge__` + + - `operator.ge(x1, x2) `_ + - `operator.__ge__(x1, x2) `_ + +- `x1 == x2`: :meth:`.array.__eq__` + + - `operator.eq(x1, x2) `_ + - `operator.__eq__(x1, x2) `_ + +- `x1 != x2`: :meth:`.array.__ne__` + + - `operator.ne(x1, x2) `_ + - `operator.__ne__(x1, x2) `_ + +:meth:`.array.__lt__`, :meth:`.array.__le__`, :meth:`.array.__gt__`, :meth:`.array.__ge__` are only defined for arrays having real-valued data types. Other comparison operators should be defined for arrays having any data type. +For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-dependent (see :ref:`complex-number-ordering`). + +In-place Operators +~~~~~~~~~~~~~~~~~~ + +A conforming implementation of the array API standard must provide and support an array object supporting the following in-place Python operators. + +An in-place operation must not change the data type or shape of the in-place array as a result of :ref:`type-promotion` or :ref:`broadcasting`. + +An in-place operation must have the same behavior (including special cases) as its respective binary (i.e., two operand, non-assignment) operation. For example, after in-place addition ``x1 += x2``, the modified array ``x1`` must always equal the result of the equivalent binary arithmetic operation ``x1 = x1 + x2``. + +.. note:: + In-place operators must be supported as discussed in :ref:`copyview-mutability`. + +Arithmetic Operators +"""""""""""""""""""" + +- ``+=``. May be implemented via ``__iadd__``. +- ``-=``. May be implemented via ``__isub__``. +- ``*=``. May be implemented via ``__imul__``. +- ``/=``. May be implemented via ``__itruediv__``. +- ``//=``. May be implemented via ``__ifloordiv__``. +- ``**=``. May be implemented via ``__ipow__``. +- ``%=``. May be implemented via ``__imod__``. + +Array Operators +""""""""""""""" + +- ``@=``. May be implemented via ``__imatmul__``. + +Bitwise Operators +""""""""""""""""" + +- ``&=``. May be implemented via ``__iand__``. +- ``|=``. May be implemented via ``__ior__``. +- ``^=``. May be implemented via ``__ixor__``. +- ``<<=``. May be implemented via ``__ilshift__``. +- ``>>=``. May be implemented via ``__irshift__``. + +Reflected Operators +~~~~~~~~~~~~~~~~~~~ + +A conforming implementation of the array API standard must provide and support an array object supporting the following reflected operators. + +The results of applying reflected operators must match their non-reflected equivalents. + +.. note:: + All operators for which ``array scalar`` is implemented must have an equivalent reflected operator implementation. + +Arithmetic Operators +"""""""""""""""""""" + +- ``__radd__`` +- ``__rsub__`` +- ``__rmul__`` +- ``__rtruediv__`` +- ``__rfloordiv__`` +- ``__rpow__`` +- ``__rmod__`` + +Array Operators +""""""""""""""" + +- ``__rmatmul__`` + +Bitwise Operators +""""""""""""""""" + +- ``__rand__`` +- ``__ror__`` +- ``__rxor__`` +- ``__rlshift__`` +- ``__rrshift__`` + +------------------------------------------------- + +.. currentmodule:: array_api + +Attributes +---------- +.. + NOTE: please keep the attributes in alphabetical order + + +.. autosummary:: + :toctree: generated + :template: property.rst + + array.dtype + array.device + array.mT + array.ndim + array.shape + array.size + array.T + +------------------------------------------------- + +Methods +------- +.. + NOTE: please keep the methods in alphabetical order + + +.. autosummary:: + :toctree: generated + :template: property.rst + + array.__abs__ + array.__add__ + array.__and__ + array.__array_namespace__ + array.__bool__ + array.__complex__ + array.__dlpack__ + array.__dlpack_device__ + array.__eq__ + array.__float__ + array.__floordiv__ + array.__ge__ + array.__getitem__ + array.__gt__ + array.__index__ + array.__int__ + array.__invert__ + array.__le__ + array.__lshift__ + array.__lt__ + array.__matmul__ + array.__mod__ + array.__mul__ + array.__ne__ + array.__neg__ + array.__or__ + array.__pos__ + array.__pow__ + array.__rshift__ + array.__setitem__ + array.__sub__ + array.__truediv__ + array.__xor__ + array.to_device diff --git a/spec/2023.12/API_specification/broadcasting.rst b/spec/2023.12/API_specification/broadcasting.rst new file mode 100644 index 000000000..abb3ed222 --- /dev/null +++ b/spec/2023.12/API_specification/broadcasting.rst @@ -0,0 +1,128 @@ +.. _broadcasting: + +Broadcasting +============ + + Array API specification for broadcasting semantics. + +Overview +-------- + +**Broadcasting** refers to the automatic (implicit) expansion of array dimensions to be of equal sizes without copying array data for the purpose of making arrays with different shapes have compatible shapes for element-wise operations. + +Broadcasting facilitates user ergonomics by encouraging users to avoid unnecessary copying of array data and can **potentially** enable more memory-efficient element-wise operations through vectorization, reduced memory consumption, and cache locality. + +Algorithm +--------- + +Given an element-wise operation involving two compatible arrays, an array having a singleton dimension (i.e., a dimension whose size is one) is broadcast (i.e., virtually repeated) across an array having a corresponding non-singleton dimension. + +If two arrays are of unequal rank, the array having a lower rank is promoted to a higher rank by (virtually) prepending singleton dimensions until the number of dimensions matches that of the array having a higher rank. + +The results of the element-wise operation must be stored in an array having a shape determined by the following algorithm. + +#. Let ``A`` and ``B`` both be arrays. + +#. Let ``shape1`` be a tuple describing the shape of array ``A``. + +#. Let ``shape2`` be a tuple describing the shape of array ``B``. + +#. Let ``N1`` be the number of dimensions of array ``A`` (i.e., the result of ``len(shape1)``). + +#. Let ``N2`` be the number of dimensions of array ``B`` (i.e., the result of ``len(shape2)``). + +#. Let ``N`` be the maximum value of ``N1`` and ``N2`` (i.e., the result of ``max(N1, N2)``). + +#. Let ``shape`` be a temporary list of length ``N`` for storing the shape of the result array. + +#. Let ``i`` be ``N-1``. + +#. Repeat, while ``i >= 0`` + + #. Let ``n1`` be ``N1 - N + i``. + + #. If ``n1 >= 0``, let ``d1`` be the size of dimension ``n1`` for array ``A`` (i.e., the result of ``shape1[n1]``); else, let ``d1`` be ``1``. + + #. Let ``n2`` be ``N2 - N + i``. + + #. If ``n2 >= 0``, let ``d2`` be the size of dimension ``n2`` for array ``B`` (i.e., the result of ``shape2[n2]``); else, let ``d2`` be ``1``. + + #. If ``d1 == 1``, then set the ``i``\th element of ``shape`` to ``d2``. + + #. Else, if ``d2 == 1``, then + + - set the ``i``\th element of ``shape`` to ``d1``. + + #. Else, if ``d1 == d2``, then + + - set the ``i``\th element of ``shape`` to ``d1``. + + #. Else, throw an exception. + + #. Set ``i`` to ``i-1``. + +#. Let ``tuple(shape)`` be the shape of the result array. + +Examples +~~~~~~~~ + +The following examples demonstrate the application of the broadcasting algorithm for two compatible arrays. + +:: + + A (4d array): 8 x 1 x 6 x 1 + B (3d array): 7 x 1 x 5 + --------------------------------- + Result (4d array): 8 x 7 x 6 x 5 + A (2d array): 5 x 4 + B (1d array): 1 + ------------------------- + Result (2d array): 5 x 4 + A (2d array): 5 x 4 + B (1d array): 4 + ------------------------- + Result (2d array): 5 x 4 + A (3d array): 15 x 3 x 5 + B (3d array): 15 x 1 x 5 + ------------------------------ + Result (3d array): 15 x 3 x 5 + A (3d array): 15 x 3 x 5 + B (2d array): 3 x 5 + ------------------------------ + Result (3d array): 15 x 3 x 5 + A (3d array): 15 x 3 x 5 + B (2d array): 3 x 1 + ------------------------------ + Result (3d array): 15 x 3 x 5 + + +The following examples demonstrate array shapes which do **not** broadcast. + +:: + + A (1d array): 3 + B (1d array): 4 # dimension does not match + + A (2d array): 2 x 1 + B (3d array): 8 x 4 x 3 # second dimension does not match + + A (3d array): 15 x 3 x 5 + B (2d array): 15 x 3 # singleton dimensions can only be prepended, not appended + +In-place Semantics +------------------ + +As implied by the broadcasting algorithm, in-place element-wise operations (including ``__setitem__``) must not change the shape of the in-place array as a result of broadcasting. Such operations should only be supported in the case where the right-hand operand can broadcast to the shape of the left-hand operand, after any indexing operations are performed. + +For example: + +:: + + x = empty((2, 3, 4)) + a = empty((1, 3, 4)) + + # This is OK. The shape of a, (1, 3, 4), can broadcast to the shape of x[...], (2, 3, 4) + x[...] = a + + # This is not allowed. The shape of a, (1, 3, 4), can NOT broadcast to the shape of x[1, ...], (3, 4) + x[1, ...] = a diff --git a/spec/2023.12/API_specification/constants.rst b/spec/2023.12/API_specification/constants.rst new file mode 100644 index 000000000..71cb8688d --- /dev/null +++ b/spec/2023.12/API_specification/constants.rst @@ -0,0 +1,26 @@ +Constants +========= + + Array API specification for constants. + +A conforming implementation of the array API standard must provide and support the following constants adhering to the following conventions. + +- Each constant must have a Python floating-point data type (i.e., ``float``) and be provided as a Python scalar value. + +Objects in API +-------------- + +.. currentmodule:: array_api.constants + +.. + NOTE: please keep the functions in alphabetical order + +.. autosummary:: + :toctree: generated + :template: attribute.rst + + e + inf + nan + newaxis + pi diff --git a/spec/2023.12/API_specification/creation_functions.rst b/spec/2023.12/API_specification/creation_functions.rst new file mode 100644 index 000000000..ff5c06368 --- /dev/null +++ b/spec/2023.12/API_specification/creation_functions.rst @@ -0,0 +1,36 @@ +Creation Functions +================== + + Array API specification for creating arrays. + +A conforming implementation of the array API standard must provide and support the following functions. + + +Objects in API +-------------- + +.. currentmodule:: array_api + +.. + NOTE: please keep the functions in alphabetical order + +.. autosummary:: + :toctree: generated + :template: method.rst + + arange + asarray + empty + empty_like + eye + from_dlpack + full + full_like + linspace + meshgrid + ones + ones_like + tril + triu + zeros + zeros_like diff --git a/spec/2023.12/API_specification/data_type_functions.rst b/spec/2023.12/API_specification/data_type_functions.rst new file mode 100644 index 000000000..d42968c7b --- /dev/null +++ b/spec/2023.12/API_specification/data_type_functions.rst @@ -0,0 +1,26 @@ +Data Type Functions +=================== + + Array API specification for data type functions. + +A conforming implementation of the array API standard must provide and support the following data type functions. + + +Objects in API +-------------- + +.. currentmodule:: array_api + +.. + NOTE: please keep the functions in alphabetical order + +.. autosummary:: + :toctree: generated + :template: method.rst + + astype + can_cast + finfo + iinfo + isdtype + result_type diff --git a/spec/2023.12/API_specification/data_types.rst b/spec/2023.12/API_specification/data_types.rst new file mode 100644 index 000000000..5987dd322 --- /dev/null +++ b/spec/2023.12/API_specification/data_types.rst @@ -0,0 +1,143 @@ +.. _data-types: + +Data Types +========== + + Array API specification for supported data types. + +A conforming implementation of the array API standard must provide and support +the following data types ("dtypes") in its array object, and as data type +objects in its main namespace under the specified names: + ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| dtype object | description | ++==============+============================================================================================================================================================================================+ +| bool | Boolean (``True`` or ``False``). | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| int8 | An 8-bit signed integer whose values exist on the interval ``[-128, +127]``. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| int16 | A 16-bit signed integer whose values exist on the interval ``[−32,767, +32,767]``. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| int32 | A 32-bit signed integer whose values exist on the interval ``[−2,147,483,647, +2,147,483,647]``. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| int64 | A 64-bit signed integer whose values exist on the interval ``[−9,223,372,036,854,775,807, +9,223,372,036,854,775,807]``. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| uint8 | An 8-bit unsigned integer whose values exist on the interval ``[0, +255]``. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| uint16 | A 16-bit unsigned integer whose values exist on the interval ``[0, +65,535]``. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| uint32 | A 32-bit unsigned integer whose values exist on the interval ``[0, +4,294,967,295]``. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| uint64 | A 64-bit unsigned integer whose values exist on the interval ``[0, +18,446,744,073,709,551,615]``. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| float32 | IEEE 754 single-precision (32-bit) binary floating-point number (see IEEE 754-2019). | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| float64 | IEEE 754 double-precision (64-bit) binary floating-point number (see IEEE 754-2019). | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| complex64 | Single-precision (64-bit) complex floating-point number whose real and imaginary components must be IEEE 754 single-precision (32-bit) binary floating-point numbers (see IEEE 754-2019). | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| complex128 | Double-precision (128-bit) complex floating-point number whose real and imaginary components must be IEEE 754 double-precision (64-bit) binary floating-point numbers (see IEEE 754-2019). | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +Data type objects must have the following methods (no attributes are required): + +.. + NOTE: please keep the functions in alphabetical order + +.. currentmodule:: array_api.data_types + +.. autosummary:: + :toctree: generated + :template: method.rst + + __eq__ + + +.. note:: + A conforming implementation of the array API standard may provide and + support additional data types beyond those described in this specification. + It may also support additional methods and attributes on dtype objects. + +.. note:: + IEEE 754-2019 requires support for subnormal (a.k.a., denormal) numbers, which are useful for supporting gradual underflow. However, hardware support for subnormal numbers is not universal, and many platforms (e.g., accelerators) and compilers support toggling denormals-are-zero (DAZ) and/or flush-to-zero (FTZ) behavior to increase performance and to guard against timing attacks. + + Accordingly, subnormal behavior is left unspecified and, thus, implementation-defined. Conforming implementations may vary in their support for subnormal numbers. + + +Use of data type objects +------------------------ + +Data type objects are used as ``dtype`` specifiers in functions and methods +(e.g., ``zeros((2, 3), dtype=float32)``), accessible as ``.dtype`` attribute on +arrays, and used in various casting and introspection functions (e.g., +``isdtype(x.dtype, 'integral')``). + +``dtype`` keywords in functions specify the data type of arrays returned from +functions or methods. ``dtype`` keywords are not required to affect the data +type used for intermediate calculations or results (e.g., implementors are free +to use a higher-precision data type when accumulating values for reductions, as +long as the returned array has the specified data type). + +.. note:: + Implementations may provide other ways to specify data types (e.g., ``zeros((2, 3), dtype='f4')``) which are not described in this specification; however, in order to ensure portability, array library consumers are recommended to use data type objects as provided by specification conforming array libraries. + +See :ref:`type-promotion` for specification guidance describing the rules governing the interaction of two or more data types or data type objects. + + +.. _data-type-defaults: + +Default Data Types +------------------ + +A conforming implementation of the array API standard must define the following default data types. + +- a default real-valued floating-point data type (either ``float32`` or ``float64``). +- a default complex floating-point data type (either ``complex64`` or ``complex128``). +- a default integer data type (either ``int32`` or ``int64``). +- a default array index data type (either ``int32`` or ``int64``). + +The default real-valued floating-point and complex floating-point data types must be the same across platforms. + +The default complex floating-point point data type should match the default real-valued floating-point data type. For example, if the default real-valued floating-point data type is ``float32``, the default complex floating-point data type must be ``complex64``. If the default real-valued floating-point data type is ``float64``, the default complex floating-point data type must be ``complex128``. + +The default integer data type should be the same across platforms, but the default may vary depending on whether Python is 32-bit or 64-bit. + +The default array index data type may be ``int32`` on 32-bit platforms, but the default should be ``int64`` otherwise. + +Note that it is possible that a library supports multiple devices, with not all +those device types supporting the same data types. In this case, the default +integer or floating-point data types may vary with device. If that is the case, +the library should clearly warn about this in its documentation. + +.. note:: + The default data types should be clearly defined in a conforming library's documentation. + + +.. _data-type-categories: + +Data Type Categories +-------------------- + +For the purpose of organizing functions within this specification, the following data type categories are defined. + ++----------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ +| data type category | dtypes | ++============================+========================================================================================================================================================+ +| Numeric | ``int8``, ``int16``, ``int32``, ``int64``, ``uint8``, ``uint16``, ``uint32``, ``uint64``, ``float32``, ``float64``, ``complex64``, and ``complex128``. | ++----------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Real-valued | ``int8``, ``int16``, ``int32``, ``int64``, ``uint8``, ``uint16``, ``uint32``, ``uint64``, ``float32``, and ``float64``. | ++----------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Integer | ``int8``, ``int16``, ``int32``, ``int64``, ``uint8``, ``uint16``, ``uint32``, and ``uint64``. | ++----------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Floating-point | ``float32``, ``float64``, ``complex64``, and ``complex128``. | ++----------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Real-valued floating-point | ``float32`` and ``float64``. | ++----------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Complex floating-point | ``complex64`` and ``complex128``. | ++----------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Boolean | ``bool``. | ++----------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ + + +.. note:: + Conforming libraries are not required to organize data types according to these categories. These categories are only intended for use within this specification. diff --git a/spec/2023.12/API_specification/elementwise_functions.rst b/spec/2023.12/API_specification/elementwise_functions.rst new file mode 100644 index 000000000..4919cff98 --- /dev/null +++ b/spec/2023.12/API_specification/elementwise_functions.rst @@ -0,0 +1,84 @@ +.. _element-wise-functions: + +Element-wise Functions +====================== + + Array API specification for element-wise functions. + +Objects in API +-------------- + +.. currentmodule:: array_api + +.. + NOTE: please keep the functions in alphabetical order + +.. autosummary:: + :toctree: generated + :template: method.rst + + abs + acos + acosh + add + asin + asinh + atan + atan2 + atanh + bitwise_and + bitwise_left_shift + bitwise_invert + bitwise_or + bitwise_right_shift + bitwise_xor + ceil + clip + conj + copysign + cos + cosh + divide + equal + exp + expm1 + floor + floor_divide + greater + greater_equal + hypot + imag + isfinite + isinf + isnan + less + less_equal + log + log1p + log2 + log10 + logaddexp + logical_and + logical_not + logical_or + logical_xor + maximum + minimum + multiply + negative + not_equal + positive + pow + real + remainder + round + sign + signbit + sin + sinh + square + sqrt + subtract + tan + tanh + trunc diff --git a/spec/2023.12/API_specification/function_and_method_signatures.rst b/spec/2023.12/API_specification/function_and_method_signatures.rst new file mode 100644 index 000000000..0eca2ac69 --- /dev/null +++ b/spec/2023.12/API_specification/function_and_method_signatures.rst @@ -0,0 +1,63 @@ +.. _function-and-method-signatures: + +Function and method signatures +============================== + +Function signatures in this standard adhere to the following: + +1. Positional parameters should be `positional-only `_ parameters. + Positional-only parameters have no externally-usable name. When a function + accepting positional-only parameters is called, positional arguments are + mapped to these parameters based solely on their order. + + *Rationale: existing libraries have incompatible conventions, and using names + of positional parameters is not normal/recommended practice.* + + .. note:: + + Positional-only parameters are only available in Python >= 3.8. Libraries + still supporting 3.7 or 3.6 may consider making the API standard-compliant + namespace >= 3.8. Alternatively, they can add guidance to their users in the + documentation to use the functions as if they were positional-only. + +2. Optional parameters should be `keyword-only `_ arguments. + + *Rationale: this leads to more readable code, and it makes it easier to + evolve an API over time by adding keywords without having to worry about + keyword order.* + +3. For functions that have a single positional array parameter, that parameter + is called ``x``. For functions that have multiple array parameters, those + parameters are called ``xi`` with ``i = 1, 2, ...`` (i.e., ``x1``, ``x2``). + +4. Signatures include type annotations. The type annotations are also added to + individual parameter and return value descriptions. For code which aims to + adhere to the standard, adding type annotations is strongly recommended. + +A function signature and description will look like: + +:: + + funcname(x1, x2, /, *, key1=-1, key2=None) -> out: + Parameters + + x1 : array + description + x2 : array + description + key1 : int + description + key2 : Optional[str] + description + + Returns + + out : array + description + + +Method signatures will follow the same conventions modulo the addition of ``self``. + +Note that there are a few exceptions to rules (1) and (2), in cases where +it enhances readability or use of the non-default form of the parameter in +question is commonly used in code written for existing array libraries. diff --git a/spec/2023.12/API_specification/index.rst b/spec/2023.12/API_specification/index.rst new file mode 100644 index 000000000..ffc3d3775 --- /dev/null +++ b/spec/2023.12/API_specification/index.rst @@ -0,0 +1,41 @@ +.. _api-specification: + +API specification +================= + +A conforming implementation of the array API standard must provide and support the APIs and behavior detailed in this specification while adhering to the following conventions. + +- When a function signature includes a `/`, positional parameters must be `positional-only `_ parameters. See :ref:`function-and-method-signatures`. +- When a function signature includes a `*`, optional parameters must be `keyword-only `_ arguments. See :ref:`function-and-method-signatures`. +- Broadcasting semantics must follow the semantics defined in :ref:`broadcasting`. +- Unless stated otherwise, functions must support the data types defined in :ref:`data-types`. +- Functions may only be required for a subset of input data types. Libraries may choose to implement functions for additional data types, but that behavior is not required by the specification. See :ref:`data-type-categories`. +- Unless stated otherwise, functions must adhere to the type promotion rules defined in :ref:`type-promotion`. +- Unless stated otherwise, floating-point operations must adhere to IEEE 754-2019. +- Unless stated otherwise, element-wise mathematical functions must satisfy the minimum accuracy requirements defined in :ref:`accuracy`. + + +.. toctree:: + :caption: API specification + :maxdepth: 3 + + array_object + broadcasting + constants + creation_functions + data_type_functions + data_types + elementwise_functions + function_and_method_signatures + indexing + indexing_functions + inspection + linear_algebra_functions + manipulation_functions + searching_functions + set_functions + sorting_functions + statistical_functions + type_promotion + utility_functions + version diff --git a/spec/2023.12/API_specification/indexing.rst b/spec/2023.12/API_specification/indexing.rst new file mode 100644 index 000000000..eb61c26d5 --- /dev/null +++ b/spec/2023.12/API_specification/indexing.rst @@ -0,0 +1,208 @@ +.. _indexing: + +Indexing +======== + + Array API specification for indexing arrays. + +A conforming implementation of the array API standard must adhere to the following conventions. + +Single-axis Indexing +-------------------- + +To index a single array axis, an array must support standard Python indexing rules. Let ``n`` be the axis (dimension) size. + +- An integer index must be an object satisfying `operator.index `_ (e.g., ``int``). + +- Nonnegative indices must start at ``0`` (i.e., zero-based indexing). + +- **Valid** nonnegative indices must reside on the half-open interval ``[0, n)``. + + .. note:: + This specification does not require bounds checking. The behavior for out-of-bounds integer indices is left unspecified. + +- Negative indices must count backward from the last array index, starting from ``-1`` (i.e., negative-one-based indexing, where ``-1`` refers to the last array index). + + .. note:: + A negative index ``j`` is equivalent to ``n-j``; the former is syntactic sugar for the latter, providing a shorthand for indexing elements that would otherwise need to be specified in terms of the axis (dimension) size. + +- **Valid** negative indices must reside on the closed interval ``[-n, -1]``. + + .. note:: + This specification does not require bounds checking. The behavior for out-of-bounds integer indices is left unspecified. + +- A negative index ``j`` is related to a zero-based nonnegative index ``i`` via ``i = n+j``. + +- Colons ``:`` must be used for `slices `_: ``start:stop:step``, where ``start`` is inclusive and ``stop`` is exclusive. + + .. note:: + The specification does not support returning scalar (i.e., non-array) values from operations, including indexing. In contrast to standard Python indexing rules, for any index, or combination of indices, which select a single value, the result must be a zero-dimensional array containing the selected value. + +Slice Syntax +~~~~~~~~~~~~ + +The basic slice syntax is ``i:j:k`` where ``i`` is the starting index, ``j`` is the stopping index, and ``k`` is the step (``k != 0``). A slice may contain either one or two colons, with either an integer value or nothing on either side of each colon. The following are valid slices. + +:: + + A[:] + A[i:] + A[:j] + A[i:k] + A[::] + A[i::] + A[:j:] + A[::k] + A[i:j:] + A[i::k] + A[:j:k] + A[i::k] + A[i:j:k] + +.. note:: + Slice syntax can be equivalently achieved using the Python built-in `slice() `_ API. From the perspective of ``A``, the behavior of ``A[i:j:k]`` and ``A[slice(i, j, k)]`` is indistinguishable (i.e., both retrieve the same set of items from ``__getitem__``). + +Using a slice to index a single array axis must select ``m`` elements with index values + +:: + + i, i+k, i+2k, i+3k, ..., i+(m-1)k + +where + +:: + + m = q + r + +and ``q`` and ``r`` (``r != 0``) are the quotient and remainder obtained by dividing ``j-i`` by ``k`` + +:: + + j - i = qk + r + +such that + +:: + + j > i + (m-1)k + +.. note:: + For ``i`` on the interval ``[0, n)`` (where ``n`` is the axis size), ``j`` on the interval ``(0, n]``, ``i`` less than ``j``, and positive step ``k``, a starting index ``i`` is **always** included, while the stopping index ``j`` is **always** excluded. This preserves ``x[:i]+x[i:]`` always being equal to ``x``. + +.. note:: + Using a slice to index into a single array axis should select the same elements as using a slice to index a Python list of the same size. + +Slice syntax must have the following defaults. Let ``n`` be the axis (dimension) size. + +- If ``k`` is not provided (e.g., ``0:10``), ``k`` must equal ``1``. +- If ``k`` is greater than ``0`` and ``i`` is not provided (e.g., ``:10:2``), ``i`` must equal ``0``. +- If ``k`` is greater than ``0`` and ``j`` is not provided (e.g., ``0::2``), ``j`` must equal ``n``. +- If ``k`` is less than ``0`` and ``i`` is not provided (e.g., ``:10:-2``), ``i`` must equal ``n-1``. +- If ``k`` is less than ``0`` and ``j`` is not provided (e.g., ``0::-2``), ``j`` must equal ``-n-1``. + +Using a slice to index a single array axis must adhere to the following rules. Let ``n`` be the axis (dimension) size. + +- If ``i`` equals ``j``, a slice must return an empty array, whose axis (dimension) size along the indexed axis is ``0``. + +- Indexing via ``:`` and ``::`` must be equivalent and have defaults derived from the rules above. Both ``:`` and ``::`` indicate to select all elements along a single axis (dimension). + + .. note:: + This specification does not require "clipping" out-of-bounds slice indices. This is in contrast to Python slice semantics where ``0:100`` and ``0:10`` are equivalent on a list of length ``10``. + +The following ranges for the start and stop values of a slice must be supported. Let ``n`` be the axis (dimension) size being sliced. For a slice ``i:j:k``, the behavior specified above should be implemented for the following: + +- ``i`` or ``j`` omitted (``None``). +- ``-n <= i <= n``. +- For ``k > 0`` or ``k`` omitted (``None``), ``-n <= j <= n``. +- For ``k < 0``, ``-n - 1 <= j <= max(0, n - 1)``. + +The behavior outside of these bounds is unspecified. + +.. note:: + *Rationale: this is consistent with bounds checking for integer indexing; the behavior of out-of-bounds indices is left unspecified. Implementations may choose to clip (consistent with Python* ``list`` *slicing semantics), raise an exception, return junk values, or some other behavior depending on device requirements and performance considerations.* + +Multi-axis Indexing +------------------- + +Multi-dimensional arrays must extend the concept of single-axis indexing to multiple axes by applying single-axis indexing rules along each axis (dimension) and supporting the following additional rules. Let ``N`` be the number of dimensions ("rank") of a multi-dimensional array ``A``. + +- Each axis may be independently indexed via single-axis indexing by providing a comma-separated sequence ("selection tuple") of single-axis indexing expressions (e.g., ``A[:, 2:10, :, 5]``). + + .. note:: + In Python, ``A[(exp1, exp2, ..., expN)]`` is equivalent to ``A[exp1, exp2, ..., expN]``; the latter is syntactic sugar for the former. + + Accordingly, if ``A`` has rank ``1``, then ``A[(2:10,)]`` must be equivalent to ``A[2:10]``. If ``A`` has rank ``2``, then ``A[(2:10, :)]`` must be equivalent to ``A[2:10, :]``. And so on and so forth. + +- Providing a single nonnegative integer ``i`` as a single-axis index must index the same elements as the slice ``i:i+1``. + +- Providing a single negative integer ``i`` as a single-axis index must index the same elements as the slice ``n+i:n+i+1``, where ``n`` is the axis (dimension) size. + +- Providing a single integer as a single-axis index must reduce the number of array dimensions by ``1`` (i.e., the array rank must decrease by one; if ``A`` has rank ``2``, ``rank(A)-1 == rank(A[0, :])``). In particular, a selection tuple with the ``m``\th element an integer (and all other entries ``:``) indexes a sub-array with rank ``N-1``. + + .. note:: + When providing a single integer as a single-axis index to an array of rank ``1``, the result should be an array of rank ``0``, not a NumPy scalar. Note that this behavior differs from NumPy. + +- Providing a slice must retain array dimensions (i.e., the array rank must remain the same; ``rank(A) == rank(A[:])``). + +- Providing `ellipsis `_ must apply ``:`` to each dimension necessary to index all dimensions (e.g., if ``A`` has rank ``4``, ``A[1:, ..., 2:5] == A[1:, :, :, 2:5]``). Only a single ellipsis must be allowed. An ``IndexError`` exception must be raised if more than one ellipsis is provided. + +- Providing an empty tuple or an ellipsis to an array of rank ``0`` must result in an array of the same rank (i.e., if ``A`` has rank ``0``, ``A == A[()]`` and ``A == A[...]``). + + .. note:: + This behavior differs from NumPy where providing an empty tuple to an array of rank ``0`` returns a NumPy scalar. + +- Each ``None`` in the selection tuple must expand the dimensions of the resulting selection by one dimension of size ``1``. The position of the added dimension must be the same as the position of ``None`` in the selection tuple. + + .. note:: + Expanding dimensions can be equivalently achieved via repeated invocation of :func:`~array_api.expand_dims`. + + .. note:: + The constant ``newaxis`` is an alias of ``None`` and can thus be used in a similar manner as ``None``. + +- Except in the case of providing a single ellipsis (e.g., ``A[2:10, ...]`` or ``A[1:, ..., 2:5]``), the number of provided single-axis indexing expressions (excluding ``None``) should equal ``N``. For example, if ``A`` has rank ``2``, a single-axis indexing expression should be explicitly provided for both axes (e.g., ``A[2:10, :]``). An ``IndexError`` exception should be raised if the number of provided single-axis indexing expressions (excluding ``None``) is less than ``N``. + + .. note:: + Some libraries, such as SymPy, support flat indexing (i.e., providing a single-axis indexing expression to a higher-dimensional array). That practice is not supported here. + + To perform flat indexing, use ``reshape(x, (-1,))[integer]``. + +- An ``IndexError`` exception must be raised if the number of provided single-axis indexing expressions (excluding ``None``) is greater than ``N``. + + .. note:: + This specification leaves unspecified the behavior of providing a slice which attempts to select elements along a particular axis, but whose starting index is out-of-bounds. + + *Rationale: this is consistent with bounds-checking for single-axis indexing. An implementation may choose to set the axis (dimension) size of the result array to* ``0`` *, raise an exception, return junk values, or some other behavior depending on device requirements and performance considerations.* + +Boolean Array Indexing +---------------------- + +.. admonition:: Data-dependent output shape + :class: admonition important + + For common boolean array use cases (e.g., using a dynamically-sized boolean array mask to filter the values of another array), the shape of the output array is data-dependent; hence, array libraries which build computation graphs (e.g., JAX, Dask, etc.) may find boolean array indexing difficult to implement. Accordingly, such libraries may choose to omit boolean array indexing. See :ref:`data-dependent-output-shapes` section for more details. + +An array must support indexing where the **sole index** is an ``M``-dimensional boolean array ``B`` with shape ``S1 = (s1, ..., sM)`` according to the following rules. Let ``A`` be an ``N``-dimensional array with shape ``S2 = (s1, ..., sM, ..., sN)``. + + .. note:: + The prohibition against combining boolean array indices with other single-axis indexing expressions includes the use of ``None``. To expand dimensions of the returned array, use repeated invocation of :func:`~array_api.expand_dims`. + +- If ``N >= M``, then ``A[B]`` must replace the first ``M`` dimensions of ``A`` with a single dimension having a size equal to the number of ``True`` elements in ``B``. The values in the resulting array must be in row-major (C-style order); this is equivalent to ``A[nonzero(B)]``. + + .. note:: + For example, if ``N == M == 2``, indexing ``A`` via a boolean array ``B`` will return a one-dimensional array whose size is equal to the number of ``True`` elements in ``B``. + +- If ``N < M``, then an ``IndexError`` exception must be raised. + +- The size of each dimension in ``B`` must equal the size of the corresponding dimension in ``A`` or be ``0``, beginning with the first dimension in ``A``. If a dimension size does not equal the size of the corresponding dimension in ``A`` and is not ``0``, then an ``IndexError`` exception must be raised. + +- The elements of a boolean index array must be iterated in row-major, C-style order, with the exception of zero-dimensional boolean arrays. + +- A zero-dimensional boolean index array (equivalent to ``True`` or ``False``) must follow the same axis replacement rules stated above. Namely, a zero-dimensional boolean index array removes zero dimensions and adds a single dimension of length ``1`` if the index array's value is ``True`` and of length ``0`` if the index array's value is ``False``. Accordingly, for a zero-dimensional boolean index array ``B``, the result of ``A[B]`` has shape ``S = (1, s1, ..., sN)`` if the index array's value is ``True`` and has shape ``S = (0, s1, ..., sN)`` if the index array's value is ``False``. + +Return Values +------------- + +The result of an indexing operation (e.g., multi-axis indexing, boolean array indexing, etc) must be an array of the same data type as the indexed array. + +.. note:: + The specified return value behavior includes indexing operations which return a single value (e.g., accessing a single element within a one-dimensional array). diff --git a/spec/2023.12/API_specification/indexing_functions.rst b/spec/2023.12/API_specification/indexing_functions.rst new file mode 100644 index 000000000..aef298566 --- /dev/null +++ b/spec/2023.12/API_specification/indexing_functions.rst @@ -0,0 +1,23 @@ +.. _indexing-functions: + +Indexing Functions +=================== + + Array API specification for functions for indexing arrays. + +A conforming implementation of the array API standard must provide and support the following functions. + + +Objects in API +-------------- + +.. currentmodule:: array_api + +.. + NOTE: please keep the functions in alphabetical order + +.. autosummary:: + :toctree: generated + :template: method.rst + + take diff --git a/spec/2023.12/API_specification/inspection.rst b/spec/2023.12/API_specification/inspection.rst new file mode 100644 index 000000000..04691e712 --- /dev/null +++ b/spec/2023.12/API_specification/inspection.rst @@ -0,0 +1,40 @@ +Inspection +========== + + Array API specification for namespace inspection utilities. + +A conforming implementation of the array API standard must provide and support the following functions and associated inspection APIs. + + +Objects in API +-------------- + +.. currentmodule:: array_api.info + +.. + NOTE: please keep the functions in alphabetical order + +.. autosummary:: + :toctree: generated + :template: method.rst + + __array_namespace_info__ + + +Inspection APIs +--------------- + +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. + +.. + NOTE: please keep the functions in alphabetical order + +.. autosummary:: + :toctree: generated + :template: method.rst + + capabilities + default_device + default_dtypes + devices + dtypes diff --git a/spec/2023.12/API_specification/linear_algebra_functions.rst b/spec/2023.12/API_specification/linear_algebra_functions.rst new file mode 100644 index 000000000..04d36f50a --- /dev/null +++ b/spec/2023.12/API_specification/linear_algebra_functions.rst @@ -0,0 +1,23 @@ +Linear Algebra Functions +======================== + + Array API specification for linear algebra functions. + +A conforming implementation of the array API standard must provide and support the following functions. + + +.. currentmodule:: array_api + +Objects in API +-------------- +.. + NOTE: please keep the functions in alphabetical order + +.. autosummary:: + :toctree: generated + :template: method.rst + + matmul + matrix_transpose + tensordot + vecdot diff --git a/spec/2023.12/API_specification/manipulation_functions.rst b/spec/2023.12/API_specification/manipulation_functions.rst new file mode 100644 index 000000000..395c1c3e2 --- /dev/null +++ b/spec/2023.12/API_specification/manipulation_functions.rst @@ -0,0 +1,34 @@ +Manipulation Functions +====================== + + Array API specification for manipulating arrays. + +A conforming implementation of the array API standard must provide and support the following functions. + + +Objects in API +-------------- + +.. currentmodule:: array_api + +.. + NOTE: please keep the functions in alphabetical order + +.. autosummary:: + :toctree: generated + :template: method.rst + + broadcast_arrays + broadcast_to + concat + expand_dims + flip + moveaxis + permute_dims + repeat + reshape + roll + squeeze + stack + tile + unstack diff --git a/spec/2023.12/API_specification/searching_functions.rst b/spec/2023.12/API_specification/searching_functions.rst new file mode 100644 index 000000000..c952f1aad --- /dev/null +++ b/spec/2023.12/API_specification/searching_functions.rst @@ -0,0 +1,27 @@ +.. _searching-functions: + +Searching Functions +=================== + + Array API specification for functions for searching arrays. + +A conforming implementation of the array API standard must provide and support the following functions. + + +Objects in API +-------------- + +.. currentmodule:: array_api + +.. + NOTE: please keep the functions in alphabetical order + +.. autosummary:: + :toctree: generated + :template: method.rst + + argmax + argmin + nonzero + searchsorted + where diff --git a/spec/2023.12/API_specification/set_functions.rst b/spec/2023.12/API_specification/set_functions.rst new file mode 100644 index 000000000..addf31e1f --- /dev/null +++ b/spec/2023.12/API_specification/set_functions.rst @@ -0,0 +1,24 @@ +Set Functions +============= + + Array API specification for creating and operating on sets. + +A conforming implementation of the array API standard must provide and support the following functions. + + +Objects in API +-------------- + +.. currentmodule:: array_api + +.. + NOTE: please keep the functions in alphabetical order + +.. autosummary:: + :toctree: generated + :template: method.rst + + unique_all + unique_counts + unique_inverse + unique_values diff --git a/spec/2023.12/API_specification/sorting_functions.rst b/spec/2023.12/API_specification/sorting_functions.rst new file mode 100644 index 000000000..ad3af8857 --- /dev/null +++ b/spec/2023.12/API_specification/sorting_functions.rst @@ -0,0 +1,31 @@ +Sorting Functions +================= + + Array API specification for sorting functions. + +A conforming implementation of the array API standard must provide and support the following functions. + + +.. note:: + + For floating-point input arrays, the sort order of NaNs and signed zeros is unspecified and thus implementation-dependent. + + Implementations may choose to sort signed zeros (``-0 < +0``) or may choose to rely solely on value equality (``==``). + + Implementations may choose to sort NaNs (e.g., to the end or to the beginning of a returned array) or leave them in-place. Should an implementation sort NaNs, the sorting convention should be clearly documented in the conforming implementation's documentation. + + While defining a sort order for IEEE 754 floating-point numbers is recommended in order to facilitate reproducible and consistent sort results, doing so is not currently required by this specification. + +.. currentmodule:: array_api + +Objects in API +-------------- +.. + NOTE: please keep the functions in alphabetical order + +.. autosummary:: + :toctree: generated + :template: method.rst + + argsort + sort diff --git a/spec/2023.12/API_specification/statistical_functions.rst b/spec/2023.12/API_specification/statistical_functions.rst new file mode 100644 index 000000000..20e02b3f9 --- /dev/null +++ b/spec/2023.12/API_specification/statistical_functions.rst @@ -0,0 +1,28 @@ +Statistical Functions +===================== + + Array API specification for statistical functions. + +A conforming implementation of the array API standard must provide and support the following functions. + + +Objects in API +-------------- + +.. currentmodule:: array_api + +.. + NOTE: please keep the functions in alphabetical order + +.. autosummary:: + :toctree: generated + :template: method.rst + + cumulative_sum + max + mean + min + prod + std + sum + var diff --git a/spec/2023.12/API_specification/type_promotion.rst b/spec/2023.12/API_specification/type_promotion.rst new file mode 100644 index 000000000..339b90e45 --- /dev/null +++ b/spec/2023.12/API_specification/type_promotion.rst @@ -0,0 +1,148 @@ +.. _type-promotion: + +Type Promotion Rules +==================== + + Array API specification for type promotion rules. + +Type promotion rules can be understood at a high level from the following diagram: + +.. image:: ../../_static/images/dtype_promotion_lattice.png + :target: Type promotion diagram + +*Type promotion diagram. Promotion between any two types is given by their join on this lattice. Only the types of participating arrays matter, not their values. Dashed lines indicate that behavior for Python scalars is undefined on overflow. Boolean, integer and floating-point dtypes are not connected, indicating mixed-kind promotion is undefined.* + +Rules +----- + +A conforming implementation of the array API standard must implement the following type promotion rules governing the common result type for two **array** operands during an arithmetic operation. + +A conforming implementation of the array API standard may support additional type promotion rules beyond those described in this specification. + +.. note:: + Type codes are used here to keep tables readable; they are not part of the standard. In code, use the data type objects specified in :ref:`data-types` (e.g., ``int16`` rather than ``'i2'``). + +.. + Note: please keep table columns aligned + +The following type promotion tables specify the casting behavior for operations involving two array operands. When more than two array operands participate, application of the promotion tables is associative (i.e., the result does not depend on operand order). + +Signed integer type promotion table +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++--------+----+----+----+----+ +| | i1 | i2 | i4 | i8 | ++========+====+====+====+====+ +| **i1** | i1 | i2 | i4 | i8 | ++--------+----+----+----+----+ +| **i2** | i2 | i2 | i4 | i8 | ++--------+----+----+----+----+ +| **i4** | i4 | i4 | i4 | i8 | ++--------+----+----+----+----+ +| **i8** | i8 | i8 | i8 | i8 | ++--------+----+----+----+----+ + +where + +- **i1**: 8-bit signed integer (i.e., ``int8``) +- **i2**: 16-bit signed integer (i.e., ``int16``) +- **i4**: 32-bit signed integer (i.e., ``int32``) +- **i8**: 64-bit signed integer (i.e., ``int64``) + +Unsigned integer type promotion table +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++--------+----+----+----+----+ +| | u1 | u2 | u4 | u8 | ++========+====+====+====+====+ +| **u1** | u1 | u2 | u4 | u8 | ++--------+----+----+----+----+ +| **u2** | u2 | u2 | u4 | u8 | ++--------+----+----+----+----+ +| **u4** | u4 | u4 | u4 | u8 | ++--------+----+----+----+----+ +| **u8** | u8 | u8 | u8 | u8 | ++--------+----+----+----+----+ + +where + +- **u1**: 8-bit unsigned integer (i.e., ``uint8``) +- **u2**: 16-bit unsigned integer (i.e., ``uint16``) +- **u4**: 32-bit unsigned integer (i.e., ``uint32``) +- **u8**: 64-bit unsigned integer (i.e., ``uint64``) + +Mixed unsigned and signed integer type promotion table +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++--------+----+----+----+ +| | u1 | u2 | u4 | ++========+====+====+====+ +| **i1** | i2 | i4 | i8 | ++--------+----+----+----+ +| **i2** | i2 | i4 | i8 | ++--------+----+----+----+ +| **i4** | i4 | i4 | i8 | ++--------+----+----+----+ +| **i8** | i8 | i8 | i8 | ++--------+----+----+----+ + +Floating-point type promotion table +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++---------+-----+-----+-----+-----+ +| | f4 | f8 | c8 | c16 | ++=========+=====+=====+=====+=====+ +| **f4** | f4 | f8 | c8 | c16 | ++---------+-----+-----+-----+-----+ +| **f8** | f8 | f8 | c16 | c16 | ++---------+-----+-----+-----+-----+ +| **c8** | c8 | c16 | c8 | c16 | ++---------+-----+-----+-----+-----+ +| **c16** | c16 | c16 | c16 | c16 | ++---------+-----+-----+-----+-----+ + +where + +- **f4**: single-precision (32-bit) floating-point number (i.e., ``float32``) +- **f8**: double-precision (64-bit) floating-point number (i.e., ``float64``) +- **c8**: single-precision complex floating-point number (i.e., ``complex64``) + composed of two single-precision (32-bit) floating-point numbers +- **c16**: double-precision complex floating-point number (i.e., ``complex128``) + composed of two double-precision (64-bit) floating-point numbers + + +Notes +~~~~~ + +- Type promotion rules must apply when determining the common result type for two **array** operands during an arithmetic operation, regardless of array dimension. Accordingly, zero-dimensional arrays must be subject to the same type promotion rules as dimensional arrays. +- Type promotion of non-numerical data types to numerical data types is unspecified (e.g., ``bool`` to ``intxx`` or ``floatxx``). + +.. note:: + Mixed integer and floating-point type promotion rules are not specified because behavior varies between implementations. + +Mixing arrays with Python scalars +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Using Python scalars (i.e., instances of ``bool``, ``int``, ``float``, ``complex``) together with arrays must be supported for: + +- ``array scalar`` +- ``scalar array`` + +where ```` is a built-in operator (including in-place operators, but excluding the matmul ``@`` operator; see :ref:`operators` for operators supported by the array object) and ``scalar`` has a type and value compatible with the array data type: + +- a Python ``bool`` for a ``bool`` array data type. +- a Python ``int`` within the bounds of the given data type for integer array :ref:`data-types`. +- a Python ``int`` or ``float`` for real-valued floating-point array data types. +- a Python ``int``, ``float``, or ``complex`` for complex floating-point array data types. + +Provided the above requirements are met, the expected behavior is equivalent to: + +1. Convert the scalar to zero-dimensional array with the same data type as that of the array used in the expression. +2. Execute the operation for ``array 0-D array`` (or ``0-D array array`` if ``scalar`` was the left-hand argument). + +.. note:: + Behavior is not specified when mixing a Python ``float`` and an array with an integer data type; this may give ``float32``, ``float64``, or raise an exception. Behavior is implementation-specific. + + Similarly, behavior is not specified when mixing a Python ``complex`` and an array with a real-valued data type; this may give ``complex64``, ``complex128``, or raise an exception. Behavior is implementation-specific. + + Behavior is also not specified for integers outside of the bounds of a given integer data type. Integers outside of bounds may result in overflow or an error. diff --git a/spec/2023.12/API_specification/utility_functions.rst b/spec/2023.12/API_specification/utility_functions.rst new file mode 100644 index 000000000..5105fa3df --- /dev/null +++ b/spec/2023.12/API_specification/utility_functions.rst @@ -0,0 +1,22 @@ +Utility Functions +================= + + Array API specification for utility functions. + +A conforming implementation of the array API standard must provide and support the following functions. + + +Objects in API +-------------- + +.. currentmodule:: array_api + +.. + NOTE: please keep the functions in alphabetical order + +.. autosummary:: + :toctree: generated + :template: method.rst + + all + any diff --git a/spec/2023.12/API_specification/version.rst b/spec/2023.12/API_specification/version.rst new file mode 100644 index 000000000..346395d9a --- /dev/null +++ b/spec/2023.12/API_specification/version.rst @@ -0,0 +1,22 @@ +Version +======= + + Array API specification for versioning. + +A conforming implementation of the array API standard must provide a `__array_api_version__` attribute - see :ref:`api-versioning` for details. + + +Objects in API +-------------- + +.. currentmodule:: array_api + +.. + NOTE: please keep the functions in alphabetical order + +.. autosummary:: + :toctree: generated + :template: attribute.rst + :nosignatures: + + __array_api_version__ diff --git a/spec/2023.12/assumptions.md b/spec/2023.12/assumptions.md new file mode 100644 index 000000000..b11482c5a --- /dev/null +++ b/spec/2023.12/assumptions.md @@ -0,0 +1,77 @@ +(Assumptions)= + +# Assumptions + +## Hardware and software environments + +No assumptions on a specific hardware environment are made. It must be possible +to create an array library adhering to this standard that runs (efficiently) on +a variety of different hardware: CPUs with different architectures, GPUs, +distributed systems and TPUs and other emerging accelerators. + +The same applies to software environments: it must be possible to create an +array library adhering to this standard that runs efficiently independent of +what compilers, build-time or run-time execution environment, or distribution +and install method is employed. Parallel execution, JIT compilation, and +delayed (lazy) evaluation must all be possible. + +The variety of hardware and software environments puts _constraints_ on choices +made in the API standard. For example, JIT compilers may require output dtypes +of functions to be predictable from input dtypes only rather than input values. + + +(assumptions-dependencies)= + +## Dependencies + +The only dependency that's assumed in this standard is that on Python itself. +Python >= 3.8 is assumed, motivated by the use of positional-only parameters +(see [function and method signatures](API_specification/function_and_method_signatures.rst)). + +Importantly, array libraries are not assumed to be aware of each other, or of +a common array-specific layer. The [use cases](use_cases.md) do not require +such a dependency, and building and evolving an array library is easier without +such a coupling. Facilitation support of multiple array types in downstream +libraries is an important use case however, the assumed dependency structure +for that is: + +![dependency assumptions diagram](../_static/images/dependency_assumption_diagram.png) + +Array libraries may know how to interoperate with each other, for example by +constructing their own array type from that of another library or by shared +memory use of an array (see [Data interchange mechanisms](design_topics/data_interchange.rst)). +This can be done without a dependency though - only adherence to a protocol is +enough. + +Array-consuming libraries will have to depend on one or more array libraries. +That could be a "soft dependency" though, meaning retrieving an array library +namespace from array instances that are passed in, but not explicitly doing +`import arraylib_name`. + + +## Backwards compatibility + +The assumption made during creation of this standard is that libraries are +constrained by backwards compatibility guarantees to their users, and are +likely unwilling to make significant backwards-incompatible changes for the +purpose of conforming to this standard. Therefore it is assumed that the +standard will be made available in a new namespace within each library, or the +library will provide a way to retrieve a module or module-like object that +adheres to this standard. See {ref}`how-to-adopt-this-api` for more details. + + +## Production code & interactive use + +It is assumed that the primary use case is writing production code, for example +in array-consuming libraries. As a consequence, making it easy to ensure that +code is written as intended and has unambiguous semantics is preferred - and +clear exceptions must be raised otherwise. + +It is also assumed that this does not significantly detract from the +interactive user experience. However, in case existing libraries differ in +behavior, the more strict version of that behavior is typically preferred. A +good example is array inputs to functions - while NumPy accepts lists, tuples, +generators, and anything else that could be turned into an array, most other +libraries only accept their own array types. This standard follows the latter choice. +It is likely always possible to put a thin "interactive use convenience layer" +on top of a more strict behavior. diff --git a/spec/2023.12/benchmark_suite.md b/spec/2023.12/benchmark_suite.md new file mode 100644 index 000000000..41066c6a4 --- /dev/null +++ b/spec/2023.12/benchmark_suite.md @@ -0,0 +1,3 @@ +# Benchmark suite + +Adding a benchmark suite is planned in the future. diff --git a/spec/2023.12/changelog.rst b/spec/2023.12/changelog.rst new file mode 100644 index 000000000..701a3dbcd --- /dev/null +++ b/spec/2023.12/changelog.rst @@ -0,0 +1,5 @@ +Changelog per API standard version +================================== + +.. include:: ../../CHANGELOG.md + :parser: myst_parser.sphinx_ diff --git a/spec/2023.12/conf.py b/spec/2023.12/conf.py new file mode 100644 index 000000000..f1bee91d4 --- /dev/null +++ b/spec/2023.12/conf.py @@ -0,0 +1,13 @@ +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).parents[2] / "src")) + +from array_api_stubs import _2023_12 as stubs_mod +from _array_api_conf import * + +release = "2023.12" + +nav_title = html_theme_options.get("nav_title") + " v{}".format(release) +html_theme_options.update({"nav_title": nav_title}) +sys.modules["array_api"] = stubs_mod diff --git a/spec/2023.12/design_topics/C_API.rst b/spec/2023.12/design_topics/C_API.rst new file mode 100644 index 000000000..6a44596b0 --- /dev/null +++ b/spec/2023.12/design_topics/C_API.rst @@ -0,0 +1,94 @@ +.. _C-API: + +C API +===== + +Use of a C API is out of scope for this array API, as mentioned in :ref:`Scope`. +There are a lot of libraries that do use such an API - in particular via Cython code +or via direct usage of the NumPy C API. When the maintainers of such libraries +want to use this array API standard to support multiple types of arrays, they +need a way to deal with that issue. This section aims to provide some guidance. + +The assumption in the rest of this section is that performance matters for the library, +and hence the goal is to make other array types work without converting to a +``numpy.ndarray`` or another particular array type. If that's not the case (e.g. for a +visualization package), then other array types can simply be handled by converting +to the supported array type. + +.. note:: + Often a zero-copy conversion to ``numpy.ndarray`` is possible, at least for CPU arrays. + If that's the case, this may be a good way to support other array types. + The main difficulty in that case will be getting the return array type right - however, + this standard does provide a Python-level API for array construction that should allow + doing this. A relevant question is if it's possible to know with + certainty that a conversion will be zero-copy. This may indeed be + possible, see :ref:`data-interchange`. + + +Example situations for C/Cython usage +------------------------------------- + +Situation 1: a Python package that is mostly pure Python, with a limited number of Cython extensions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. note:: + Projects in this situation include Statsmodels, scikit-bio and QuTiP + +Main strategy: documentation. The functionality using Cython code will not support other array types (or only with conversion to/from ``numpy.ndarray``), which can be documented per function. + + +Situation 2: a Python package that contains a lot of Cython code +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. note:: + Projects in this situation include scikit-learn and scikit-image + +Main strategy: add support for other array types *per submodule*. This keeps it manageable to explain to the user which functionality does and doesn't have support. + +Longer term: specific support for particular array types (e.g. ``cupy.ndarray`` can be supported with Python-only code via ``cupy.ElementwiseKernel``). + + +Situation 3: a Python package that uses the NumPy or Python C API directly +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. note:: + Projects in this situation include SciPy and Astropy + +Strategy: similar to *situation 2*, but the number of submodules that can support all array types may be limited. + + +Device support +-------------- + +Supporting non-CPU array types in code using the C API or Cython seems problematic, +this almost inevitably will require custom device-specific code (e.g., CUDA, ROCm) or +something like JIT compilation with Numba. + + +Other longer-term approaches +---------------------------- + +Further Python API standardization +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There may be cases where it makes sense to standardize additional sets of +functions, because they're important enough that array libraries tend to +reimplement them. An example of this may be *special functions*, as provided +by ``scipy.special``. Bessel and gamma functions for example are commonly +reimplemented by array libraries. This may avoid having to drop into a +particular implementation that does use a C API (e.g., one can then rely on +``arraylib.special.gamma`` rather than having to use ``scipy.special.gamma``). + +HPy +~~~ + +`HPy `_ is a new project that will provide a higher-level +C API and ABI than CPython offers. A Cython backend targeting HPy will be provided as well. + +- Better PyPy support +- Universal ABI - single binary for all supported Python versions +- Cython backend generating HPy rather than CPython code + +HPy isn't quite ready for mainstream usage today, but once it is it may +help make supporting multiple array libraries or adding non-CPU device +support to Cython more feasible. diff --git a/spec/2023.12/design_topics/accuracy.rst b/spec/2023.12/design_topics/accuracy.rst new file mode 100644 index 000000000..8c97db698 --- /dev/null +++ b/spec/2023.12/design_topics/accuracy.rst @@ -0,0 +1,77 @@ +.. _accuracy: + +Accuracy +======== + + Array API specification for minimum accuracy requirements. + +Arithmetic Operations +--------------------- + +The results of element-wise arithmetic operations + +- ``+`` +- ``-`` +- ``*`` +- ``/`` +- ``%`` + +including the corresponding element-wise array APIs defined in this standard + +- add +- subtract +- multiply +- divide + +for floating-point operands must return the nearest representable value according to IEEE 754-2019 and a supported rounding mode. By default, the rounding mode should be ``roundTiesToEven`` (i.e., ties rounded toward the nearest value with an even least significant bit). + +Mathematical Functions +---------------------- + +This specification does **not** precisely define the behavior of the following functions + +- acos +- acosh +- asin +- asinh +- atan +- atan2 +- atanh +- cos +- cosh +- exp +- expm1 +- log +- log1p +- log2 +- log10 +- pow +- sin +- sinh +- tan +- tanh + +except to require specific results for certain argument values that represent boundary cases of interest. + +.. note:: + To help readers identify functions lacking precisely defined accuracy behavior, this specification uses the phrase "implementation-dependent approximation" in function descriptions. + +For other argument values, these functions should compute approximations to the results of respective mathematical functions; however, this specification recognizes that array libraries may be constrained by underlying hardware and/or seek to optimize performance over absolute accuracy and, thus, allows some latitude in the choice of approximation algorithms. + +Although the specification leaves the choice of algorithms to the implementation, this specification recommends (but does not specify) that implementations use the approximation algorithms for IEEE 754-2019 arithmetic contained in `FDLIBM `_, the freely distributable mathematical library from Sun Microsystems, or some other comparable IEEE 754-2019 compliant mathematical library. + +.. note:: + With exception of a few mathematical functions, returning results which are indistinguishable from correctly rounded infinitely precise results is difficult, if not impossible, to achieve due to the algorithms involved, the limits of finite-precision, and error propagation. However, this specification recognizes that numerical accuracy alignment among array libraries is desirable in order to ensure portability and reproducibility. Accordingly, for each mathematical function, the specification test suite includes test values which span a function's domain and reports the average and maximum deviation from either a designated standard implementation (e.g., an arbitrary precision arithmetic implementation) or an average computed across a subset of known array library implementations. Such reporting aids users who need to know how accuracy varies among libraries and developers who need to check the validity of their implementations. + +Statistical Functions +--------------------- + +This specification does not specify accuracy requirements for statistical functions; however, this specification does expect that a conforming implementation of the array API standard will make a best-effort attempt to ensure that its implementations are theoretically sound and numerically robust. + +.. note:: + In order for an array library to pass the specification test suite, an array library's statistical function implementations must satisfy certain bare-minimum accuracy requirements (e.g., accurate summation of a small set of positive integers). Unfortunately, imposing more rigorous accuracy requirements is not possible without severely curtailing possible implementation algorithms and unduly increasing implementation complexity. + +Linear Algebra +-------------- + +This specification does not specify accuracy requirements for linear algebra functions; however, this specification does expect that a conforming implementation of the array API standard will make a best-effort attempt to ensure that its implementations are theoretically sound and numerically robust. diff --git a/spec/2023.12/design_topics/complex_numbers.rst b/spec/2023.12/design_topics/complex_numbers.rst new file mode 100644 index 000000000..0eca79e91 --- /dev/null +++ b/spec/2023.12/design_topics/complex_numbers.rst @@ -0,0 +1,61 @@ +.. _complex-numbers: + +Complex Numbers +=============== + +The Complex Plane +----------------- + +Mathematically, equality comparison between complex numbers depends on the choice of topology. For example, the complex plane has a continuum of infinities; however, when the complex plane is projected onto the surface of a sphere (a stereographic projection commonly referred to as the *Riemann sphere*), infinities coalesce into a single *point at infinity*, thus modeling the extended complex plane. For the former, the value :math:`\infty + 3j` is distinct from (i.e., does not equal) :math:`\infty + 4j`, while, for the latter, :math:`\infty + 3j` does equal :math:`\infty + 4j`. + +Modeling complex numbers as a Riemann sphere conveys certain mathematical niceties (e.g., well-behaved division by zero and preservation of the identity :math:`\frac{1}{\frac{1}{z}} = z`); however, translating the model to IEEE 754 floating-point operations can lead to some unexpected results. For example, according to IEEE 754, :math:`+\infty` and :math:`-\infty` are distinct values; hence, for equality comparison, if :math:`x = +\infty` and :math:`y = -\infty`, then :math:`x \neq y`. In contrast, if we convert :math:`x` and :math:`y` to their complex number equivalents :math:`x = +\infty + 0j` and :math:`y = -\infty + 0j` and then interpret within the context of the extended complex plane, we arrive at the opposite result; namely, :math:`x = y`. + +In short, given the constraints of floating-point arithmetic and the subtleties of signed zeros, infinities, NaNs, and their interaction, crafting a specification which always yields intuitive results and satisfies all use cases involving complex numbers is not possible. Instead, this specification attempts to follow precedent (e.g., C99, Python, Julia, NumPy, and elsewhere), while also minimizing surprise. The result is an imperfect balance in which certain APIs may appear to embrace the one-infinity model found in C/C++ for algebraic operations involving complex numbers (e.g., considering :math:`\infty + \operatorname{NaN}\ j` to be infinite, irrespective of the imaginary component's value, including NaN), while other APIs may rely on the complex plane with its multiplicity of infinities (e.g., in transcendental functions). Accordingly, consumers of this specification should expect that certain results involving complex numbers for one operation may not be wholly consistent with results involving complex numbers for another operation. + + +.. _branch-cuts: + +Branch Cuts +----------- + +In the mathematical field of complex analysis, a **branch cut** is a curve in the complex plane across which an analytic multi-valued function is discontinuous. Branch cuts are often taken as lines or line segments, and the choice of any particular branch cut is a matter of convention. + +For example, consider the function :math:`z^2` which maps a complex number :math:`z` to a well-defined number :math:`z^2`. The function's inverse function :math:`\sqrt{z}` does not, however, map to a single value. For example, for :math:`z = 1`, :math:`\sqrt{1} = \pm 1`. While one can choose a unique principal value for this and similar functions (e.g., in this case, the principal square root is :math:`+1`), choices cannot be made continuous over the whole complex plane, as lines of discontinuity must occur. To handle discontinuities, one commonly adopts branch cuts, which are not, in general, unique. Instead, one chooses a branch cut as a matter of convention in order to give simple analytic properties. + +Branch cuts do not arise for single-valued trigonometric, hyperbolic, integer power, or exponential functions; however, branch cuts do arise for their multi-valued inverses. + +In contrast to real-valued floating-point numbers which have well-defined behavior as specified in IEEE 754, complex-valued floating-point numbers have no equivalent specification. Accordingly, this specification chooses to follow C99 conventions for special cases and branch cuts for those functions supporting complex numbers. For those functions which do not have C99 equivalents (e.g., linear algebra APIs), the specification relies on dominant conventions among existing array libraries. + +.. warning:: + All branch cuts documented in this specification are considered **provisional**. While conforming implementations of the array API standard should adopt the branch cuts described in this standard, consumers of array API standard implementations should **not** assume that branch cuts are consistent between implementations. + + Provided no issues arise due to the choice of branch cut, the provisional status is likely to be removed in a future revision of this standard. + + +.. _complex-number-ordering: + +Complex Number Ordering +----------------------- + +Given a set :math:`\{a_1, \ldots, a_n\}`, an order relation must satisfy the following properties: + +1. **Reflexive**: for any :math:`a` in the set, :math:`a \leq a`. +2. **Transitive**: for any :math:`a`, :math:`b`, and :math:`c` in the set, if :math:`a \leq b` and :math:`b \leq c`, then :math:`a \leq c`. +3. **Antisymmetric**: for any :math:`a` and :math:`b` in the set, if :math:`a \leq b` and :math:`b \leq a`, then :math:`a = b`. +4. **Total Order**: in addition to the *partial order* established by 1-3, for any :math:`a` and :math:`b` in the set, either :math:`a \leq b` or :math:`b \leq a` (or both). +5. **Compatible with Addition**: for all :math:`a`, :math:`b`, and :math:`c` in the set, if :math:`a \leq b`, then :math:`a + c \leq b + c`. +6. **Compatible with Multiplication**: for all :math:`a`, :math:`b`, and :math:`c` in the set, if :math:`a \leq b` and :math:`0 \leq c`, then :math:`ac \leq bc`. + +Defining an order relation for complex numbers which satisfies all six properties defined above is not possible. Accordingly, this specification does not require that a conforming implementation of the array API standard adopt any specific complex number order relation. + +In order to satisfy backward compatibility guarantees, conforming implementations of the array API standard may choose to define an ordering for complex numbers (e.g., lexicographic); however, consumers of the array API standard should **not** assume that complex number ordering is consistent between implementations or even supported. + +If a conforming implementation chooses to define an ordering for complex numbers, the ordering must be clearly documented. + + +Valued-based Promotion +---------------------- + +According to the type promotion rules described in this specification (see :ref:`type-promotion`), only the data types of the input arrays participating in an operation matter, not their values. The same principle applies to situations in which one or more results of operations on real-valued arrays are mathematically defined in the complex domain, but not in their real domain. + +By convention, the principal square root of :math:`-1` is :math:`j`, where :math:`j` is the imaginary unit. Despite this convention, for those operations supporting type promotion, conforming implementations must only consider input array data types when determining the data type of the output array. For example, if a real-valued input array is provided to :func:`~array_api.sqrt`, the output array must also be real-valued, even if the input array contains negative values. Accordingly, if a consumer of a conforming implementation of this specification desires for an operation's results to include the complex domain, the consumer should first cast the input array(s) to an appropriate complex floating-point data type before performing the operation. diff --git a/spec/2023.12/design_topics/copies_views_and_mutation.rst b/spec/2023.12/design_topics/copies_views_and_mutation.rst new file mode 100644 index 000000000..52be1c805 --- /dev/null +++ b/spec/2023.12/design_topics/copies_views_and_mutation.rst @@ -0,0 +1,77 @@ +.. _copyview-mutability: + +Copy-view behaviour and mutability +================================== + +.. admonition:: Mutating views + :class: important + + Array API consumers are *strongly* advised to avoid *any* mutating operations when an array object may be either a "view" (i.e., an array whose data refers to memory that belongs to another array) or own memory of which one or more other array objects may be views. This admonition may become more strict in the future (e.g., this specification may require that view mutation be prohibited and trigger an exception). Accordingly, only perform mutation operations (e.g., in-place assignment) when absolutely confident that array data belongs to one, and only one, array object. + +Strided array implementations (e.g. NumPy, PyTorch, CuPy, MXNet) typically +have the concept of a "view", meaning an array containing data in memory that +belongs to another array (i.e. a different "view" on the original data). +Views are useful for performance reasons - not copying data to a new location +saves memory and is faster than copying - but can also affect the semantics +of code. This happens when views are combined with *mutating* operations. +This simple example illustrates that: + +.. code-block:: python + + x = ones(1) + y = x[:] # `y` *may* be a view on the data of `x` + y -= 1 # if `y` is a view, this modifies `x` + +Code as simple as the above example will not be portable between array +libraries - for NumPy/PyTorch/CuPy/MXNet ``x`` will contain the value ``0``, +while for TensorFlow/JAX/Dask it will contain the value ``1``. The combination +of views and mutability is fundamentally problematic here if the goal is to +be able to write code with unambiguous semantics. + +Views are necessary for getting good performance out of the current strided +array libraries. It is not always clear however when a library will return a +view, and when it will return a copy. This API standard does not attempt to +specify this - libraries can do either. + +There are several types of operations that do in-place mutation of data +contained in arrays. These include: + +1. Inplace operators (e.g. ``*=``) +2. Item assignment (e.g. ``x[0] = 1``) +3. Slice assignment (e.g., ``x[:2, :] = 3``) +4. The `out=` keyword present in some strided array libraries (e.g. ``sin(x, out=y)``) + +Libraries like TensorFlow and JAX tend to support inplace operators, provide +alternative syntax for item and slice assignment (e.g. an ``update_index`` +function or ``x.at[idx].set(y)``), and have no need for ``out=``. + +A potential solution could be to make views read-only, or use copy-on-write +semantics. Both are hard to implement and would present significant issues +for backwards compatibility for current strided array libraries. Read-only +views would also not be a full solution, given that mutating the original +(base) array will also result in ambiguous semantics. Hence this API standard +does not attempt to go down this route. + +Both inplace operators and item/slice assignment can be mapped onto +equivalent functional expressions (e.g. ``x[idx] = val`` maps to +``x.at[idx].set(val)``), and given that both inplace operators and item/slice +assignment are very widely used in both library and end user code, this +standard chooses to include them. + +The situation with ``out=`` is slightly different - it's less heavily used, and +easier to avoid. It's also not an optimal API, because it mixes an +"efficiency of implementation" consideration ("you're allowed to do this +inplace") with the semantics of a function ("the output _must_ be placed into +this array). There are libraries that do some form of tracing or abstract +interpretation over a language that does not support mutation (to make +analysis easier); in those cases implementing ``out=`` with correct handling of +views may even be impossible to do. There's alternatives, for example the +donated arguments in JAX or working buffers in LAPACK, that allow the user to +express "you _may_ overwrite this data, do whatever is fastest". Given that +those alternatives aren't widely used in array libraries today, this API +standard chooses to (a) leave out ``out=``, and (b) not specify another method +of reusing arrays that are no longer needed as buffers. + +This leaves the problem of the initial example - with this API standard it +remains possible to write code that will not work the same for all array +libraries. This is something that the user must be careful about. diff --git a/spec/2023.12/design_topics/data_dependent_output_shapes.rst b/spec/2023.12/design_topics/data_dependent_output_shapes.rst new file mode 100644 index 000000000..43daa9765 --- /dev/null +++ b/spec/2023.12/design_topics/data_dependent_output_shapes.rst @@ -0,0 +1,15 @@ +.. _data-dependent-output-shapes: + +Data-dependent output shapes +============================ + +Array libraries which build computation graphs commonly employ static analysis that relies upon known shapes. For example, JAX requires known array sizes when compiling code, in order to perform static memory allocation. Functions and operations which are value-dependent present difficulties for such libraries, as array sizes cannot be inferred ahead of time without also knowing the contents of the respective arrays. + +While value-dependent functions and operations are not impossible to implement for array libraries which build computation graphs, this specification does not want to impose an undue burden on such libraries and permits omission of value-dependent operations. All other array libraries are expected, however, to implement the value-dependent operations included in this specification in order to be array specification compliant. + +Value-dependent operations are demarcated in this specification using an admonition similar to the following: + +.. admonition:: Data-dependent output shape + :class: important + + The shape of the output array for this function/operation depends on the data values in the input array; hence, array libraries which build computation graphs (e.g., JAX, Dask, etc.) may find this function/operation difficult to implement without knowing array values. Accordingly, such libraries may choose to omit this function. See :ref:`data-dependent-output-shapes` section for more details. diff --git a/spec/2023.12/design_topics/data_interchange.rst b/spec/2023.12/design_topics/data_interchange.rst new file mode 100644 index 000000000..3b3040672 --- /dev/null +++ b/spec/2023.12/design_topics/data_interchange.rst @@ -0,0 +1,105 @@ +.. _data-interchange: + +Data interchange mechanisms +=========================== + +This section discusses the mechanism to convert one type of array into another. +As discussed in the :ref:`assumptions-dependencies ` section, +*functions* provided by an array library are not expected to operate on +*array types* implemented by another library. Instead, the array can be +converted to a "native" array type. + +The interchange mechanism must offer the following: + +1. Data access via a protocol that describes the memory layout of the array + in an implementation-independent manner. + + *Rationale: any number of libraries must be able to exchange data, and no + particular package must be needed to do so.* + +2. Support for all dtypes in this API standard (see :ref:`data-types`). + +3. Device support. It must be possible to determine on what device the array + that is to be converted lives. + + *Rationale: there are CPU-only, GPU-only, and multi-device array types; + it's best to support these with a single protocol (with separate + per-device protocols it's hard to figure out unambiguous rules for which + protocol gets used, and the situation will get more complex over time + as TPU's and other accelerators become more widely available).* + +4. Zero-copy semantics where possible, making a copy only if needed (e.g. + when data is not contiguous in memory). + + *Rationale: performance.* + +5. A Python-side and a C-side interface, the latter with a stable C ABI. + + *Rationale: all prominent existing array libraries are implemented in + C/C++, and are released independently from each other. Hence a stable C + ABI is required for packages to work well together.* + +DLPack: An in-memory tensor structure +------------------------------------- + +The best candidate for this protocol is +`DLPack `_, and hence that is what this +standard has chosen as the primary/recommended protocol. Note that the +``asarray`` function also supports the Python buffer protocol (CPU-only) to +support libraries that already implement buffer protocol support. + +.. note:: + The main alternatives to DLPack are device-specific methods: + + - The `buffer protocol `_ on CPU + - ``__cuda_array_interface__`` for CUDA, specified in the Numba documentation + `here `_ + (Python-side only at the moment) + + An issue with device-specific protocols are: if two libraries both + support multiple device types, in which order should the protocols be + tried? A growth in the number of protocols to support each time a new + device gets supported by array libraries (e.g. TPUs, AMD GPUs, emerging + hardware accelerators) also seems undesirable. + + In addition to the above argument, it is also clear from adoption + patterns that DLPack has the widest support. The buffer protocol, despite + being a lot older and standardized as part of Python itself via PEP 3118, + hardly has any support from array libraries. CPU interoperability is + mostly dealt with via the NumPy-specific ``__array__`` (which, when called, + means the object it is attached to must return a ``numpy.ndarray`` + containing the data the object holds). + + See the `RFC to adopt DLPack `_ + for discussion that preceded the adoption of DLPack. + +DLPack's documentation can be found at: https://dmlc.github.io/dlpack/latest/. + +The `Python specification of DLPack `__ +page gives a high-level specification for data exchange in Python using DLPack. + +.. note:: + DLPack is a standalone protocol/project and can therefore be used outside of + this standard. Python libraries that want to implement only DLPack support + are recommended to do so using the same syntax and semantics as outlined + below. They are not required to return an array object from ``from_dlpack`` + which conforms to this standard. + +Non-supported use cases +----------------------- + +Use of DLPack requires that the data can be represented by a strided, in-memory +layout on a single device. This covers usage by a large range of, but not all, +known and possible array libraries. Use cases that are not supported by DLPack +include: + +- Distributed arrays, i.e., the data residing on multiple nodes or devices, +- Sparse arrays, i.e., sparse representations where a data value (typically + zero) is implicit. + +There may be other reasons why it is not possible or desirable for an +implementation to materialize the array as strided data in memory. In such +cases, the implementation may raise a `BufferError` in the `__dlpack__` or +`__dlpack_device__` method. In case an implementation is never able to export +its array data via DLPack, it may omit `__dlpack__` and `__dlpack_device__` +completely, and hence `from_dlpack` may raise an `AttributeError`. diff --git a/spec/2023.12/design_topics/device_support.rst b/spec/2023.12/design_topics/device_support.rst new file mode 100644 index 000000000..593b0b9fa --- /dev/null +++ b/spec/2023.12/design_topics/device_support.rst @@ -0,0 +1,112 @@ +.. _device-support: + +Device support +============== + +For libraries that support execution on more than a single hardware device - e.g. CPU and GPU, or multiple GPUs - it is important to be able to control on which device newly created arrays get placed and where execution happens. Attempting to be fully implicit doesn't always scale well to situations with multiple GPUs. + +Existing libraries employ one or more of these three methods to exert such control over data placement: + +1. A global default device, which may be fixed or user-switchable. +2. A context manager to control device assignment within its scope. +3. Local control for data allocation target device via explicit keywords, and a method to transfer arrays to another device. + +Libraries differ in how execution is controlled, via a context manager or with the convention that execution takes place on the same device where all argument arrays are allocated. And they may or may not allow mixing arrays on different devices via implicit data transfers. + +This standard chooses to add support for method 3 (local control), with the convention that execution takes place on the same device where all argument arrays are allocated. The rationale for choosing method 3 is because it's the most explicit and granular, with its only downside being verbosity. A context manager may be added in the future - see :ref:`device-out-of-scope` for details. + +Intended usage +-------------- + +The intended usage for the device support in the current version of the +standard is *device handling in library code*. The assumed pattern is that +users create arrays (for which they can use all the relevant device syntax +that the library they use provides), and that they then pass those arrays +into library code which may have to do the following: + +- Create new arrays on the same device as an array that's passed in. +- Determine whether two input arrays are present on the same device or not. +- Move an array from one device to another. +- Create output arrays on the same device as the input arrays. +- Pass on a specified device to other library code. + +.. note:: + Given that there is not much that's currently common in terms of + device-related syntax between different array libraries, the syntax included + in the standard is kept as minimal as possible while enabling the + above-listed use cases. + +Syntax for device assignment +---------------------------- + +The array API provides the following syntax for device assignment and +cross-device data transfer: + +1. A ``.device`` property on the array object, which returns a ``Device`` object + representing the device the data in the array is stored on, and supports + comparing devices for equality with ``==`` and ``!=`` within the same library + (e.g., by implementing ``__eq__``); comparing device objects from different + libraries is out of scope). +2. A ``device=None`` keyword for array creation functions, which takes an + instance of a ``Device`` object. +3. A ``.to_device`` method on the array object to copy an array to a different device. + +.. note:: + The current API standard does **not** include a universal ``Device`` object + recognized by all compliant libraries. Accordingly, the standard does not + provide a means of instantiating a ``Device`` object to point to a specific + physical or logical device. + + The choice to not include a standardized ``Device`` object may be revisited + in a future revision of this standard. + + For array libraries which concern themselves with multi-device support, + including CPU and GPU, they are free to expose a library-specific device + object (e.g., for creating an array on a particular device). While a + library-specific device object can be used as input to ``to_device``, beware + that this will mean non-portability as code will be specific to that + library. + +Semantics +--------- + +Handling devices is complex, and some frameworks have elaborate policies for +handling device placement. Therefore this section only gives recommendations, +rather than hard requirements: + +- Respect explicit device assignment (i.e. if the input to the ``device=`` keyword is not ``None``, guarantee that the array is created on the given device, and raise an exception otherwise). +- Preserve device assignment as much as possible (e.g. output arrays from a function are expected to be on the same device as input arrays to the function). +- Raise an exception if an operation involves arrays on different devices (i.e. avoid implicit data transfer between devices). +- Use a default for ``device=None`` which is consistent between functions within the same library. +- If a library has multiple ways of controlling device placement, the most explicit method should have the highest priority. For example: + + 1. If ``device=`` keyword is specified, that always takes precedence + + 2. If ``device=None``, then use the setting from a context manager, if set. + + 3. If no context manager was used, then use the global default device/strategy + +.. _device-out-of-scope: + +Out of scope for device support +------------------------------- + +Individual libraries may offers APIs for one or more of the following topics, +however those are out of scope for this standard: + +- Identifying a specific physical or logical device across libraries +- Setting a default device globally +- Stream/queue control +- Distributed allocation +- Memory pinning +- A context manager for device control + +.. note:: + A context manager for controlling the default device is present in most existing array + libraries (NumPy being the exception). There are concerns with using a + context manager however. A context manager can be tricky to use at a high + level, since it may affect library code below function calls (non-local + effects). See, e.g., `this PyTorch issue `_ + for a discussion on a good context manager API. + + Adding a context manager may be considered in a future version of this API standard. diff --git a/spec/2023.12/design_topics/exceptions.rst b/spec/2023.12/design_topics/exceptions.rst new file mode 100644 index 000000000..570fe56e3 --- /dev/null +++ b/spec/2023.12/design_topics/exceptions.rst @@ -0,0 +1,28 @@ +.. _exceptions: + +Exceptions +========== + +This standard specifies expected syntax and semantics for a set of APIs. When +inputs to an API do not match what is expected, libraries may emit warnings, +raise exceptions, or misbehave in unexpected ways. In general, it is not +possible to foresee or specify all the ways in which unexpected or invalid +inputs are provided. Therefore, this standard does not attempt to specify +exception or warning types to the extent needed in order to do exception +handling in a portable manner. In general, it is expected that array library +implementers follow `the guidance given by the documentation of the Python +language `__, and either use +builtin exception or warning types that are appropriate for the +situation or use custom exceptions or warnings that derive from those builtin +ones. + +In specific cases, it may be useful to provide guidance to array library +authors regarding what an appropriate exception is. That guidance will be +phrased as *should* rather than *must* (typically in a *Raises* section), +because (a) there may be reasons for an implementer to deviate, and (b) more +often than not, existing array library implementation already differ in their +choices, and it may not be worth them breaking backward compatibility in order +to comply with a "must" in this standard. + +In other cases, this standard will only specify that an exception should or +must be raised, but not mention what type of exception that is. diff --git a/spec/2023.12/design_topics/index.rst b/spec/2023.12/design_topics/index.rst new file mode 100644 index 000000000..548eda90c --- /dev/null +++ b/spec/2023.12/design_topics/index.rst @@ -0,0 +1,18 @@ +Design topics & constraints +=========================== + +.. toctree:: + :caption: Design topics & constraints + :maxdepth: 1 + + copies_views_and_mutation + data_dependent_output_shapes + lazy_eager + data_interchange + device_support + static_typing + accuracy + exceptions + complex_numbers + C_API + parallelism diff --git a/spec/2023.12/design_topics/lazy_eager.rst b/spec/2023.12/design_topics/lazy_eager.rst new file mode 100644 index 000000000..63297ac73 --- /dev/null +++ b/spec/2023.12/design_topics/lazy_eager.rst @@ -0,0 +1,43 @@ +.. _lazy-eager: + +Lazy vs. eager execution +======================== + +While the execution model for implementations is out of scope of this standard, +there are a few aspects of lazy (or graph-based) execution as contrasted to +eager execution that may have an impact on the prescribed semantics of +individual APIs, and will therefore show up in the API specification. + +One important difference is data-dependent or value-dependent behavior, as +described in :ref:`data-dependent-output-shapes`. Because such behavior is hard +to implement, implementers may choose to omit such APIs from their library. + +Another difference is when the Python language itself prescribes that a +specific type *must* be returned. For those cases, it is not possible to return +a lazy/delayed kind of object to avoid computing a value. This is the case for +five dunder methods: `__bool__`, `__int__`, `__float__`, `__complex__` and +`__index__`. Each implementation has only two choices when one of these methods +is called: + +1. Compute a value of the required type (a Python scalar of type `bool`, `int`, + `float` or `complex`), or +2. Raise an exception. + +When an implementation is 100% lazy, for example when it serializes a +computation graph, computing the value is not possible and hence such an +implementation has no choice but to raise an exception. For a "mostly lazy" +implementation, it may make sense to trigger execution instead - but it is not +required to, both choices are valid. + +A common code construct where this happens is conditional logic, e.g.:: + + vals = compute_something() + if all(vals): + # The if-statement will make Python call the __bool__ method + # on the result of `all(vals)`. + do_something_else() + +Note that the API does not contain control flow constructs, as of now, that +would allow avoiding the implicit `__bool__` call in the example above. The +only control flow-like function is `where`, but there's no function like `cond` +to replace an `if`-statement. diff --git a/spec/2023.12/design_topics/parallelism.rst b/spec/2023.12/design_topics/parallelism.rst new file mode 100644 index 000000000..f013a9cf9 --- /dev/null +++ b/spec/2023.12/design_topics/parallelism.rst @@ -0,0 +1,24 @@ +Parallelism +=========== + +Parallelism is mostly, but not completely, an execution or runtime concern +rather than an API concern. Execution semantics are out of scope for this API +standard, and hence won't be discussed further here. The API related part +involves how libraries allow users to exercise control over the parallelism +they offer, such as: + +- Via environment variables. This is the method of choice for BLAS libraries and libraries using OpenMP. +- Via a keyword to individual functions or methods. Examples include the ``n_jobs`` keyword used in scikit-learn and the ``workers`` keyword used in SciPy. +- Build-time settings to enable a parallel or distributed backend. +- Via letting the user set chunk sizes. Dask uses this approach. + +When combining multiple libraries, one has to deal with auto-parallelization +semantics and nested parallelism. Two things that could help improve the +coordination of parallelization behavior in a stack of Python libraries are: + +1. A common API pattern for enabling parallelism +2. A common library providing a parallelization layer + +Option (1) may possibly fit in a future version of this array API standard. +`array-api issue 4 `_ contains +more detailed discussion on the topic of parallelism. diff --git a/spec/2023.12/design_topics/static_typing.rst b/spec/2023.12/design_topics/static_typing.rst new file mode 100644 index 000000000..26a1fb901 --- /dev/null +++ b/spec/2023.12/design_topics/static_typing.rst @@ -0,0 +1,50 @@ +Static typing +============= + +Good support for static typing both in array libraries and array-consuming +code is desirable. Therefore the exact type or set of types for each +parameter, keyword and return value is specified for functions and methods - +see :ref:`function-and-method-signatures`. That section specifies arrays +simply as ``array``; what that means is dealt with in this section. + +Introducing type annotations in libraries became more relevant only when +Python 2.7 support was dropped at the start of 2020. As a consequence, using +type annotations with array libraries is largely still a work in progress. +This version of the API standard does not deal with trying to type *array +properties* like shape, dimensionality or dtype, because that's not a solved +problem in individual array libraries yet. + +An ``array`` type annotation can mean either the type of one specific array +object, or some superclass or typing Protocol - as long as it is consistent +with the array object specified in :ref:`array-object`. To illustrate by +example: + +.. code-block:: python + + # `Array` is a particular class in the library + def sin(x: Array, / ...) -> Array: + ... + +and + +.. code-block:: python + + # There's some base class `_BaseArray`, and there may be multiple + # array subclasses inside the library + A = TypeVar('A', bound=_BaseArray) + def sin(x: A, / ...) -> A: + ... + +should both be fine. There may be other variations possible. Also note that +this standard does not require that input and output array types are the same +(they're expected to be defined in the same library though). Given that +array libraries don't have to be aware of other types of arrays defined in +other libraries (see :ref:`assumptions-dependencies`), this should be enough +for a single array library. + +That said, an array-consuming library aiming to support multiple array types +may need more - for example a protocol to enable structural subtyping. This +API standard currently takes the position that it does not provide any +reference implementation or package that can or should be relied on at +runtime, hence no such protocol is defined here. This may be dealt with in a +future version of this standard. diff --git a/spec/2023.12/extensions/fourier_transform_functions.rst b/spec/2023.12/extensions/fourier_transform_functions.rst new file mode 100644 index 000000000..170ae390b --- /dev/null +++ b/spec/2023.12/extensions/fourier_transform_functions.rst @@ -0,0 +1,45 @@ +Fourier transform Functions +=========================== + + Array API specification for Fourier transform functions. + +Extension name and usage +------------------------ + +The name of the namespace providing the extension must be: ``fft``. + +If implemented, this ``fft`` extension must be retrievable via:: + + >>> xp = x.__array_namespace__() + >>> if hasattr(xp, 'fft'): + >>> # Use `xp.fft` + + +Objects in API +-------------- + +A conforming implementation of this ``fft`` extension must provide and support the following functions. + +.. currentmodule:: array_api.fft + +.. + NOTE: please keep the functions and their inverse together + +.. autosummary:: + :toctree: generated + :template: method.rst + + fft + ifft + fftn + ifftn + rfft + irfft + rfftn + irfftn + hfft + ihfft + fftfreq + rfftfreq + fftshift + ifftshift diff --git a/spec/2023.12/extensions/index.rst b/spec/2023.12/extensions/index.rst new file mode 100644 index 000000000..3b9409954 --- /dev/null +++ b/spec/2023.12/extensions/index.rst @@ -0,0 +1,34 @@ +.. _extensions: + +Extensions +========== + +Extensions are coherent sets of functionality that are commonly implemented +across array libraries. Each array library supporting this standard may, but is +not required to, implement an extension. If an extension is supported, it +must be accessible inside the main array API supporting namespace as a separate +namespace. + +Extension module implementors must aim to provide all functions and other +public objects in an extension. The rationale for this is that downstream usage +can then check whether or not the extension is present (using ``hasattr(xp, +'extension_name')`` should be enough), and can then assume that functions are +implemented. This in turn makes it also easy for array-consuming libraries to +document which array libraries they support - e.g., "all libraries implementing +the array API standard and its linear algebra extension". + +The mechanism through which the extension namespace is made available is up to +the implementer, e.g. via a regular submodule that is imported under the +``linalg`` name, or via a module-level ``__getattr__``. + +The functions in an extension must adhere to the same conventions as those in +the array API standard. See :ref:`api-specification`. + +------------------------------------------------------------------------------ + +.. toctree:: + :caption: Extension modules: + :maxdepth: 1 + + fourier_transform_functions + linear_algebra_functions diff --git a/spec/2023.12/extensions/linear_algebra_functions.rst b/spec/2023.12/extensions/linear_algebra_functions.rst new file mode 100644 index 000000000..6759b2260 --- /dev/null +++ b/spec/2023.12/extensions/linear_algebra_functions.rst @@ -0,0 +1,116 @@ +.. _linear-algebra-extension: + +Linear Algebra Extension +======================== + + Array API specification for linear algebra functions. + +Extension name and usage +------------------------ + +The name of the namespace providing the extension must be: ``linalg``. + +If implemented, this ``linalg`` extension must be retrievable via:: + + >>> xp = x.__array_namespace__() + >>> if hasattr(xp, 'linalg'): + >>> # Use `xp.linalg` + + +Design Principles +----------------- + +A principal goal of this specification is to standardize commonly implemented interfaces among array libraries. While this specification endeavors to avoid straying too far from common practice, this specification does, with due restraint, seek to address design decisions arising more from historical accident than first principles. This is especially true for linear algebra APIs, which have arisen and evolved organically over time and have often been tied to particular underlying implementations (e.g., to BLAS and LAPACK). + +Accordingly, the standardization process affords the opportunity to reduce interface complexity among linear algebra APIs by inferring and subsequently codifying common design themes, thus allowing more consistent APIs. What follows is the set of design principles governing the APIs which follow: + +1. **Batching**: if an operation is explicitly defined in terms of matrices (i.e., two-dimensional arrays), then the associated interface should support "batching" (i.e., the ability to perform the operation over a "stack" of matrices). Example operations include: + + - ``inv``: computing the multiplicative inverse of a square matrix. + - ``cholesky``: performing Cholesky decomposition. + - ``matmul``: performing matrix multiplication. + +2. **Data types**: if an operation requires decimal operations and :ref:`type-promotion` semantics are undefined (e.g., as is the case for mixed-kind promotions), then the associated interface should be specified as being restricted to floating-point data types. While the specification uses the term "SHOULD" rather than "MUST", a conforming implementation of the array API standard should only ignore the restriction provided overly compelling reasons for doing so. Example operations which should be limited to floating-point data types include: + + - ``inv``: computing the multiplicative inverse. + - ``slogdet``: computing the natural logarithm of the absolute value of the determinant. + - ``norm``: computing the matrix or vector norm. + + Certain operations are solely comprised of multiplications and additions. Accordingly, associated interfaces need not be restricted to floating-point data types. However, careful consideration should be given to overflow, and use of floating-point data types may be more prudent in practice. Example operations include: + + - ``matmul``: performing matrix multiplication. + - ``trace``: computing the sum along the diagonal. + - ``cross``: computing the vector cross product. + + Lastly, certain operations may be performed independent of data type, and, thus, the associated interfaces should support all data types specified in this standard. Example operations include: + + - ``matrix_transpose``: computing the transpose. + - ``diagonal``: returning the diagonal. + +3. **Return values**: if an interface has more than one return value, the interface should return a namedtuple consisting of each value. + + In general, interfaces should avoid polymorphic return values (e.g., returning an array **or** a namedtuple, dependent on, e.g., an optional keyword argument). Dedicated interfaces for each return value type are preferred, as dedicated interfaces are easier to reason about at both the implementation level and user level. Example interfaces which could be combined into a single overloaded interface, but are not, include: + + - ``eig``: computing both eigenvalues and eignvectors. + - ``eigvals``: computing only eigenvalues. + +4. **Implementation agnosticism**: a standardized interface should eschew parameterization (including keyword arguments) biased toward particular implementations. + + Historically, at a time when all array computing happened on CPUs, BLAS and LAPACK underpinned most numerical computing libraries and environments. Naturally, language and library abstractions catered to the parameterization of those libraries, often exposing low-level implementation details verbatim in their higher-level interfaces, even if such choices would be considered poor or ill-advised by today's standards (e.g., NumPy's use of `UPLO` in `eigh`). However, the present day is considerably different. While still important, BLAS and LAPACK no longer hold a monopoly over linear algebra operations, especially given the proliferation of devices and hardware on which such operations must be performed. Accordingly, interfaces must be conservative in the parameterization they support in order to best ensure universality. Such conservatism applies even to performance optimization parameters afforded by certain hardware. + +5. **Orthogonality**: an interface should have clearly defined and delineated functionality which, ideally, has no overlap with the functionality of other interfaces in the specification. Providing multiple interfaces which can all perform the same operation creates unnecessary confusion regarding interface applicability (i.e., which interface is best at which time) and decreases readability of both library and user code. Where overlap is possible, the specification must be parsimonious in the number of interfaces, ensuring that each interface provides a unique and compelling abstraction. Examples of related interfaces which provide distinct levels of abstraction (and generality) include: + + - ``vecdot``: computing the dot product of two vectors. + - ``matmul``: performing matrix multiplication (including between two vectors and thus the dot product). + - ``tensordot``: computing tensor contractions (generalized sum-products). + - ``einsum``: expressing operations in terms of Einstein summation convention, including dot products and tensor contractions. + + The above can be contrasted with, e.g., NumPy, which provides the following interfaces for computing the dot product or related operations: + + - ``dot``: dot product, matrix multiplication, and tensor contraction. + - ``inner``: dot product. + - ``vdot``: dot product with flattening and complex conjugation. + - ``multi_dot``: chained dot product. + - ``tensordot``: tensor contraction. + - ``matmul``: matrix multiplication (dot product for two vectors). + - ``einsum``: Einstein summation convention. + + where ``dot`` is overloaded based on input array dimensionality and ``vdot`` and ``inner`` exhibit a high degree of overlap with other interfaces. By consolidating interfaces and more clearly delineating behavior, this specification aims to ensure that each interface has a unique purpose and defined use case. + +.. currentmodule:: array_api.linalg + +Objects in API +-------------- + +A conforming implementation of this ``linalg`` extension must provide and support the following functions. + +.. + NOTE: please keep the functions in alphabetical order + +.. autosummary:: + :toctree: generated + :template: method.rst + + cholesky + cross + det + diagonal + eigh + eigvalsh + inv + matmul + matrix_norm + matrix_power + matrix_rank + matrix_transpose + outer + pinv + qr + slogdet + solve + svd + svdvals + tensordot + trace + vecdot + vector_norm diff --git a/spec/2023.12/future_API_evolution.md b/spec/2023.12/future_API_evolution.md new file mode 100644 index 000000000..443f683d5 --- /dev/null +++ b/spec/2023.12/future_API_evolution.md @@ -0,0 +1,60 @@ +(future-API-evolution)= + +# Future API standard evolution + +## Scope extensions + +Proposals for scope extensions in a future version of the API standard will follow +the process documented at https://github.com/data-apis/governance/blob/master/process_document.md + +In summary, proposed new APIs go through several maturity stages, and will only be +accepted in a future version of this API standard once they have reached the "Final" +maturity stage, which means multiple array libraries have compliant implementations +and real-world experience from use of those implementations is available. + + +## Backwards compatibility + +Functions, objects, keywords and specified behavior are added to this API standard +only if those are already present in multiple existing array libraries, and if there is +data that those APIs are used. Therefore it is highly unlikely that future versions +of this standard will make backwards-incompatible changes. + +The aim is for future versions to be 100% backwards compatible with older versions. +Any exceptions must have strong rationales and be clearly documented in the updated +API specification. + + +(api-versioning)= + +## Versioning + +This API standard uses the following versioning scheme: + +- The version is date-based, in the form `yyyy.mm` (e.g., `2020.12`). +- The version shall not include a standard way to do `alpha`/`beta`/`rc` or + `.post`/`.dev` type versions. + _Rationale: that's for Python packages, not for a standard._ +- The version must be made available at runtime via an attribute + `__array_api_version__` by a compliant implementation, in `'yyyy.mm'` format + as a string, in the namespace that implements the API standard. + _Rationale: dunder version strings are the standard way of doing this._ + +No utilities for dealing with version comparisons need to be provided; given +the format simple string comparisons with Python operators (`=-`, `<`, `>=`, +etc.) will be enough. + +```{note} + +Rationale for the `yyyy.mm` versioning scheme choice: +the API will be provided as part of a library, which already has a versioning +scheme (typically PEP 440 compliant and in the form `major.minor.bugfix`), +and a way to access it via `module.__version__`. The API standard version is +completely independent from the package version. Given the standardization +process, it resembles a C/C++ versioning scheme (e.g. `C99`, `C++14`) more +than Python package versioning. +``` + +The frequency of releasing a new version of an API standard will likely be at +regular intervals and on the order of one year, however no assumption on +frequency of new versions appearing must be made. diff --git a/spec/2023.12/index.rst b/spec/2023.12/index.rst new file mode 100644 index 000000000..3e51cc68e --- /dev/null +++ b/spec/2023.12/index.rst @@ -0,0 +1,37 @@ +Python array API standard +========================= + +Contents +-------- + +.. toctree:: + :caption: Context + :maxdepth: 1 + + purpose_and_scope + use_cases + assumptions + +.. toctree:: + :caption: API + :maxdepth: 1 + + design_topics/index + future_API_evolution + API_specification/index + extensions/index + +.. toctree:: + :caption: Methodology and Usage + :maxdepth: 1 + + usage_data + verification_test_suite + benchmark_suite + +.. toctree:: + :caption: Other + :maxdepth: 1 + + changelog + license diff --git a/spec/2023.12/license.rst b/spec/2023.12/license.rst new file mode 100644 index 000000000..06ec75dfc --- /dev/null +++ b/spec/2023.12/license.rst @@ -0,0 +1,9 @@ +License +======= + +All content on this website and the corresponding +`GitHub repository `__ is licensed +under the following license: + + .. include:: ../../LICENSE + :parser: myst_parser.sphinx_ diff --git a/spec/2023.12/purpose_and_scope.md b/spec/2023.12/purpose_and_scope.md new file mode 100644 index 000000000..f375c9512 --- /dev/null +++ b/spec/2023.12/purpose_and_scope.md @@ -0,0 +1,470 @@ +# Purpose and scope + +## Introduction + +Python users have a wealth of choice for libraries and frameworks for +numerical computing, data science, machine learning, and deep learning. New +frameworks pushing forward the state of the art in these fields are appearing +every year. One unintended consequence of all this activity and creativity +has been fragmentation in multidimensional array (a.k.a. tensor) libraries - +which are the fundamental data structure for these fields. Choices include +NumPy, Tensorflow, PyTorch, Dask, JAX, CuPy, MXNet, Xarray, and others. + +The APIs of each of these libraries are largely similar, but with enough +differences that it's quite difficult to write code that works with multiple +(or all) of these libraries. This array API standard aims to address that +issue, by specifying an API for the most common ways arrays are constructed +and used. + +Why not simply pick an existing API and bless that as the standard? In short, +because there are often good reasons for the current inconsistencies between +libraries. The most obvious candidate for that existing API is NumPy. However +NumPy was not designed with non-CPU devices, graph-based libraries, or JIT +compilers in mind. Other libraries often deviate from NumPy for good +(necessary) reasons. Choices made in this API standard are often the same +ones NumPy makes, or close to it, but are different where necessary to make +sure all existing array libraries can adopt this API. + + +### This API standard + +This document aims to standardize functionality that exists in most/all array +libraries and either is commonly used or is needed for +consistency/completeness. Usage is determined via analysis of downstream +libraries, see {ref}`usage-data`. An example of consistency is: there are +functional equivalents for all Python operators (including the rarely used +ones). + +Beyond usage and consistency, there's a set of use cases that inform the API +design to ensure it's fit for a wide range of users and situations - see +{ref}`use-cases`. + +A question that may arise when reading this document is: _"what about +functionality that's not present in this document?_ This: + +- means that there is no guarantee the functionality is present in libraries + adhering to the standard +- does _not_ mean that that functionality is unimportant +- may indicate that that functionality, if present in a particular array + library, is unlikely to be present in all other libraries + +### History + +The first library for numerical and scientific computing in Python was +Numeric, developed in the mid-1990s. In the early 2000s a second, similar +library, Numarray, was created. In 2005 NumPy was written, superceding both +Numeric and Numarray and resolving the fragmentation at that time. For +roughly a decade, NumPy was the only widely used array library. Over the past +~5 years, mainly due to the emergence of new hardware and the rise of deep +learning, many other libraries have appeared, leading to more severe +fragmentation. Concepts and APIs in newer libraries were often inspired by +(or copied from) those in older ones - and then changed or improved upon to +fit new needs and use cases. Individual library authors discussed ideas, +however there was never (before this array API standard) a serious attempt +to coordinate between all libraries to avoid fragmentation and arrive at a +common API standard. + +The idea for this array API standard grew gradually out of many conversations +between maintainers during 2019-2020. It quickly became clear that any +attempt to write a new "reference library" to fix the current fragmentation +was infeasible - unlike in 2005, there are now too many different use cases +and too many stakeholders, and the speed of innovation is too high. In May +2020 an initial group of maintainers was assembled in the [Consortium for +Python Data API Standards](https://data-apis.org/) to start drafting a +specification for an array API that could be adopted by each of the existing +array and tensor libraries. That resulted in this document, describing that +API. + + +(Scope)= + +## Scope (includes out-of-scope / non-goals) + +This section outlines what is in scope and out of scope for this API standard. + +### In scope + +The scope of the array API standard includes: + +- Functionality which needs to be included in an array library for it to adhere + to this standard. +- Names of functions, methods, classes and other objects. +- Function signatures, including type annotations. +- Semantics of functions and methods. I.e. expected outputs including precision + for and dtypes of numerical results. +- Semantics in the presence of `nan`'s, `inf`'s, empty arrays (i.e. arrays + including one or more dimensions of size `0`). +- Casting rules, broadcasting, indexing +- Data interchange. I.e. protocols to convert one type of array into another + type, potentially sharing memory. +- Device support. + +Furthermore, meta-topics included in this standard include: + +- Use cases for the API standard and assumptions made in it +- API standard adoption +- API standard versioning +- Future API standard evolution +- Array library and API standard versioning +- Verification of API standard conformance + +The concrete set of functionality that is in scope for this version of the +standard is shown in this diagram: + +![Scope of array API](../_static/images/scope_of_array_API.png) + + +**Goals** for the API standard include: + +- Make it possible for array-consuming libraries to start using multiple types + of arrays as inputs. +- Enable more sharing and reuse of code built on top of the core functionality + in the API standard. +- For authors of new array libraries, provide a concrete API that can be + adopted as is, rather than each author having to decide what to borrow from + where and where to deviate. +- Make the learning curve for users less steep when they switch from one array + library to another one. + + +### Out of scope + +1. Implementations of the standard are out of scope. + + _Rationale: the standard will consist of a document and an accompanying test + suite with which the conformance of an implementation can be verified. Actual + implementations will live in array libraries; no reference implementation is + planned._ + +2. Execution semantics are out of scope. This includes single-threaded vs. + parallel execution, task scheduling and synchronization, eager vs. delayed + evaluation, performance characteristics of a particular implementation of the + standard, and other such topics. + + _Rationale: execution is the domain of implementations. Attempting to specify + execution behavior in a standard is likely to require much more fine-grained + coordination between developers of implementations, and hence is likely to + become an obstacle to adoption._ + +3. Non-Python API standardization (e.g., Cython or NumPy C APIs) + + _Rationale: this is an important topic for some array-consuming libraries, + but there is no widely shared C/Cython API and hence it doesn't make sense at + this point in time to standardize anything. See + the [C API section](design_topics/C_API.rst) for more details._ + +4. Standardization of these dtypes is out of scope: bfloat16, extended + precision floating point, datetime, string, object and void dtypes. + + _Rationale: these dtypes aren't uniformly supported, and their inclusion at + this point in time could put a significant implementation burden on + libraries. It is expected that some of these dtypes - in particular + `bfloat16` - will be included in a future version of the standard._ + +5. The following topics are out of scope: I/O, polynomials, error handling, + testing routines, building and packaging related functionality, methods of + binding compiled code (e.g., `cffi`, `ctypes`), subclassing of an array + class, masked arrays, and missing data. + + _Rationale: these topics are not core functionality for an array library, + and/or are too tied to implementation details._ + +6. NumPy (generalized) universal functions, i.e. ufuncs and gufuncs. + + _Rationale: these are NumPy-specific concepts, and are mostly just a + particular way of building regular functions with a few extra + methods/properties._ + +7. Behaviour for unexpected/invalid input to functions and methods. + + _Rationale: there are a huge amount of ways in which users can provide + invalid or unspecified input to functionality in the standard. Exception + types or other resulting behaviour cannot be completely covered and would + be hard to make consistent between libraries._ + + +**Non-goals** for the API standard include: + +- Making array libraries identical so they can be merged. + + _Each library will keep having its own particular strength, whether it's + offering functionality beyond what's in the standard, performance advantages + for a given use case, specific hardware or software environment support, or + more._ + +- Implement a backend or runtime switching system to be able to switch from one + array library to another with a single setting or line of code. + + _This may be feasible, however it's assumed that when an array-consuming + library switches from one array type to another, some testing and possibly + code adjustment for performance or other reasons may be needed._ + +- Making it possible to mix multiple array libraries in function calls. + + _Most array libraries do not know about other libraries, and the functions + they implement may try to convert "foreign" input, or raise an exception. + This behaviour is hard to specify; ensuring only a single array type is + used is best left to the end user._ + + +### Implications of in/out of scope + +If something is out of scope and therefore will not be part of (the current +version of) the API standard, that means that there are no guarantees that that +functionality works the same way, or even exists at all, across the set of +array libraries that conform to the standard. It does _not_ imply that this +functionality is less important or should not be used. + + +## Stakeholders + +Arrays are fundamental to scientific computing, data science, and machine +learning and deep learning. Hence there are many stakeholders for an array API +standard. The _direct_ stakeholders of this standard are **authors/maintainers of +Python array libraries**. There are many more types of _indirect_ stakeholders +though, including: + +- maintainers of libraries and other programs which depend on array libraries + (called "array-consuming libraries" in the rest of this document) +- authors of non-Python array libraries +- developers of compilers and runtimes with array-specific functionality +- end users + +Libraries that are being actively considered - in terms of current behaviour and +API surface - during the creation of the first version of this standard +include: + +- [NumPy](https://numpy.org) +- [TensorFlow](https://www.tensorflow.org/) +- [PyTorch](https://pytorch.org/) +- [MXNet](https://numpy.mxnet.io/) +- [JAX](https://github.com/google/jax) +- [Dask](https://dask.org/) +- [CuPy](https://cupy.chainer.org/) + +Other Python array libraries that are currently under active development and +could adopt this API standard include: + +- [xarray](https://xarray.pydata.org/) +- [PyData/Sparse](https://sparse.pydata.org) +- [Weld](https://github.com/weld-project/weld) +- [Bohrium](https://bohrium.readthedocs.io/) +- [Arkouda](https://github.com/mhmerrill/arkouda) +- [Legate](https://research.nvidia.com/publication/2019-11_Legate-NumPy%3A-Accelerated) + +There are a huge amount of array-consuming libraries; some of the most +prominent ones that are being taken into account - in terms of current array +API usage or impact of design decisions on them - include (this list is likely +to grow it over time): + +- [Pandas](https://pandas.pydata.org/) +- [SciPy](https://github.com/scipy/scipy) +- [scikit-learn](https://scikit-learn.org/) +- [Matplotlib](https://matplotlib.org/) +- [scikit-image](https://scikit-image.org/) +- [NetworkX](https://networkx.github.io/) + +Array libraries in other languages, some of which may grow a Python API in the +future or have taken inspiration from NumPy or other array libraries, include: + +- [Xtensor](https://xtensor.readthedocs.io) (C++, cross-language) +- [XND](https://xnd.io/) (C, cross-language) +- [stdlib](https://stdlib.io/) (JavaScript) +- [rust-ndarray](https://github.com/rust-ndarray/ndarray) (Rust) +- [rray](https://github.com/r-lib/rray) (R) +- [ND4J](https://github.com/deeplearning4j/nd4j) (JVM) +- [NumSharp](https://github.com/SciSharp/NumSharp) (C#) + +Compilers, runtimes, and dispatching layers for which this API standard may be +relevant: + +- [Cython](https://cython.org/) +- [Numba](http://numba.pydata.org/) +- [Pythran](https://pythran.readthedocs.io/en/latest/) +- [Transonic](https://transonic.readthedocs.io) +- [ONNX](https://onnx.ai/) +- [Apache TVM](https://tvm.apache.org/) +- [MLIR](https://mlir.llvm.org/) +- [TACO](https://github.com/tensor-compiler/taco) +- [unumpy](https://github.com/Quansight-Labs/unumpy) +- [einops](https://github.com/arogozhnikov/einops) +- [Apache Arrow](https://arrow.apache.org/) + + + +## How to read this document + +For guidance on how to read and understand the type annotations included in this specification, consult the Python [documentation](https://docs.python.org/3/library/typing.html). + + +(how-to-adopt-this-api)= + +## How to adopt this API + +Most (all) existing array libraries will find something in this API standard +that is incompatible with a current implementation, and that they cannot +change due to backwards compatibility concerns. Therefore we expect that each +of those libraries will want to offer a standard-compliant API in a _new +namespace_. The question then becomes: how does a user access this namespace? + +The simplest method is: document the import to use to directly access the +namespace (e.g. `import package_name.array_api`). This has two issues though: + +1. Array-consuming libraries that want to support multiple array libraries + then have to explicitly import each library. +2. It is difficult to _version_ the array API standard implementation (see + {ref}`api-versioning`). + +To address both issues, a uniform way must be provided by a conforming +implementation to access the API namespace, namely a [method on the array +object](array.__array_namespace__): + +``` +xp = x.__array_namespace__() +``` + +The method must take one keyword, `api_version=None`, to make it possible to +request a specific API version: + +``` +xp = x.__array_namespace__(api_version='2020.10') +``` + +The `xp` namespace must contain all functionality specified in +{ref}`api-specification`. The namespace may contain other functionality; however, +including additional functionality is not recommended as doing so may hinder +portability and inter-operation of array libraries within user code. + +### Checking an array object for Compliance + +Array-consuming libraries are likely to want a mechanism for determining +whether a provided array is specification compliant. The recommended approach +to check for compliance is by checking whether an array object has an +`__array_namespace__` attribute, as this is the one distinguishing feature of +an array-compliant object. + +Checking for an `__array_namespace__` attribute can be implemented as a small +utility function similar to the following. + +```python +def is_array_api_obj(x): + return hasattr(x, '__array_namespace__') +``` + +```{note} +Providing a "reference library" on which people depend is out-of-scope for +the standard. Hence the standard cannot, e.g., provide an array ABC from +which libraries can inherit to enable an `isinstance` check. However, note +that the `numpy.array_api` implementation aims to provide a reference +implementation with only the behavior specified in this standard - it may +prove useful for verifying one is writing portable code. +``` + +### Discoverability of conforming implementations + +It may be useful to have a way to discover all packages in a Python +environment which provide a conforming array API implementation, and the +namespace that that implementation resides in. +To assist array-consuming libraries which need to create arrays originating +from multiple conforming array implementations, or developers who want to perform +for example cross-library testing, libraries may provide an +{pypa}`entry point ` in order to make an array API +namespace discoverable. + +:::{admonition} Optional feature +Given that entry points typically require build system & package installer +specific implementation, this standard chooses to recommend rather than +mandate providing an entry point. +::: + +The following code is an example for how one can discover installed +conforming libraries: + +```python +from importlib.metadata import entry_points + +try: + eps = entry_points()['array_api'] + ep = next(ep for ep in eps if ep.name == 'package_name') +except TypeError: + # The dict interface for entry_points() is deprecated in py3.10, + # supplanted by a new select interface. + ep = entry_points(group='array_api', name='package_name') + +xp = ep.load() +``` + +An entry point must have the following properties: + +- **group**: equal to `array_api`. +- **name**: equal to the package name. +- **object reference**: equal to the array API namespace import path. + + +* * * + +## Conformance + +A conforming implementation of the array API standard must provide and support +all the functions, arguments, data types, syntax, and semantics described in +this specification. + +A conforming implementation of the array API standard may provide additional +values, objects, properties, data types, and functions beyond those described +in this specification. + +Libraries which aim to provide a conforming implementation but haven't yet +completed such an implementation may, and are encouraged to, provide details on +the level of (non-)conformance. For details on how to do this, see +[Verification - measuring conformance](verification_test_suite.md). + + +* * * + +## Terms and Definitions + +For the purposes of this specification, the following terms and definitions apply. + + + +**array**: +a (usually fixed-size) multidimensional container of items of the same type and size. + +**axis**: +an array dimension. + +**branch cut**: +a curve in the complex plane across which a given complex function fails to be continuous. + +**broadcast**: +automatic (implicit) expansion of array dimensions to be of equal sizes without copying array data for the purpose of making arrays with different shapes have compatible shapes for element-wise operations. + +**compatible**: +two arrays whose dimensions are compatible (i.e., where the size of each dimension in one array is either equal to one or to the size of the corresponding dimension in a second array). + +**element-wise**: +an operation performed element-by-element, in which individual array elements are considered in isolation and independently of other elements within the same array. + +**matrix**: +a two-dimensional array. + +**rank**: +number of array dimensions (not to be confused with the number of linearly independent columns of a matrix). + +**shape**: +a tuple of `N` non-negative integers that specify the sizes of each dimension and where `N` corresponds to the number of dimensions. + +**singleton dimension**: +a dimension whose size is one. + +**vector**: +a one-dimensional array. + +* * * + +## Normative References + +The following referenced documents are indispensable for the application of this specification. + +- __IEEE 754-2019: IEEE Standard for Floating-Point Arithmetic.__ Institute of Electrical and Electronic Engineers, New York (2019). +- Scott Bradner. 1997. "Key words for use in RFCs to Indicate Requirement Levels". RFC 2119. doi:[10.17487/rfc2119](https://tools.ietf.org/html/rfc2119). diff --git a/spec/2023.12/usage_data.md b/spec/2023.12/usage_data.md new file mode 100644 index 000000000..c2dcd5d65 --- /dev/null +++ b/spec/2023.12/usage_data.md @@ -0,0 +1,86 @@ +(usage-data)= + +# Usage Data + +> Summary of existing array API design and usage. + +## Introduction + +With rare exception, technical standardization ("standardization") occurs neither in a vacuum nor from first principles. Instead, standardization finds its origins in two or more, sometimes competing, implementations differing in design and behavior. These differences introduce friction as those (e.g., downstream end-users and library authors) who operate at higher levels of abstraction must either focus on an implementation subset (e.g., only NumPy-like array libraries) or accommodate variation through increased complexity (e.g., if NumPy array, call method `.foo()`; else if Dask array, call method `.bar()`). + +Standardization aspires to reduce this friction and is a process which codifies that which is common, while still encouraging experimentation and innovation. Through the process of standardization, implementations can align around a subset of established practices and channel development resources toward that which is new and novel. In short, standardization aims to thwart reinventing the proverbial wheel. + +A foundational step in standardization is articulating a subset of established practices and defining those practices in unambiguous terms. To this end, the standardization process must approach the problem from two directions: **design** and **usage**. The former direction seeks to understand + +- current implementation design (APIs, names, signatures, classes, and objects) +- current implementation semantics (calling conventions and behavior) + +while the latter direction seeks to quantify API + +- consumers (e.g., which downstream libraries utilize an API?) +- usage frequency (e.g., how often is an API consumed?) +- consumption patterns (e.g., which optional arguments are provided and in what context?) + +By analyzing both design and usage, the standardization process grounds specification decisions in empirical data and analysis. + +## Design + +To understand API design, standardization follows the following process. + +- Identify a representative sample of commonly used Python array libraries (e.g., NumPy, Dask Array, CuPy, MXNet, JAX, TensorFlow, and PyTorch). +- Acquire public APIs (e.g., by analyzing module exports and scraping public documentation). +- Unify and standardize public API data representation for subsequent analysis. +- Extract commonalities and differences by analyzing the intersection and complement of available APIs. +- Derive a common API subset suitable for standardization (based on prevalence and ease of implementation), where such a subset may include attribute names, method names, and positional and keyword arguments. +- Leverage usage data to validate API need and to inform naming conventions, supported data types, and/or optional arguments. +- Summarize findings and provide tooling for additional analysis and exploration. + +See the [`array-api-comparison`](https://github.com/data-apis/array-api-comparison) +repository for design data and summary analysis. + +## Usage + +To understand usage patterns, standardization follows the following process. + +- Identify a representative sample of commonly used Python libraries ("downstream libraries") which consume the subset of array libraries identified during design analysis (e.g., pandas, Matplotlib, SciPy, Xarray, scikit-learn, and scikit-image). +- Instrument downstream libraries in order to record Python array API calls. +- Collect traces while running downstream library test suites. +- Transform trace data into structured data (e.g., as JSON) for subsequent analysis. +- Generate empirical APIs based on provided arguments and associated types, noting which downstream library called which empirical API and at what frequency. +- Derive a single inferred API which unifies the individual empirical API calling semantics. +- Organize API results in human-readable form as type definition files. +- Compare the inferred API to the documented API. + +The following is an inferred API for `numpy.arange`. The docstring includes the number of lines of code that invoked this function for each downstream library when running downstream library test suites. + +```python +def arange( + _0: object, + /, + *_args: object, + dtype: Union[type, str, numpy.dtype, None] = ..., + step: Union[int, float] = ..., + stop: int = ..., +): + """ + usage.dask: 347 + usage.matplotlib: 359 + usage.pandas: 894 + usage.sample-usage: 4 + usage.scipy: 1173 + usage.skimage: 174 + usage.sklearn: 373 + usage.xarray: 666 + """ + ... +``` + +See the [`python-record-api`](https://github.com/data-apis/python-record-api) repository for source code, usage data, and analysis. To perform a similar analysis on additional downstream libraries, including those not publicly released, see the published PyPI [package](https://pypi.org/project/record_api/). + +## Use in Decision-Making + +Design and usage data support specification decision-making in the following ways. + +- Validate user stories to ensure that proposals satisfy existing needs. +- Define scope to ensure that proposals address general array library design requirements (i.e., proposals must have broad applicability and be possible to implement with a reasonable amount of effort). +- Inform technical design discussions to ensure that proposals are grounded in empirical data. diff --git a/spec/2023.12/use_cases.md b/spec/2023.12/use_cases.md new file mode 100644 index 000000000..e24aa50db --- /dev/null +++ b/spec/2023.12/use_cases.md @@ -0,0 +1,235 @@ +(use-cases)= + +# Use cases + +Use cases inform the requirements for, and design choices made in, this array +API standard. This section first discusses what types of use cases are +considered, and then works out a few concrete use cases in more detail. + +## Types of use cases + +- Packages that depend on a specific array library currently, and would like + to support multiple of them (e.g. for GPU or distributed array support, for + improved performance, or for reaching a wider user base). +- Writing new libraries/tools that wrap multiple array libraries. +- Projects that implement new types of arrays with, e.g., hardware-specific + optimizations or auto-parallelization behavior, and need an API to put on + top that is familiar to end users. +- End users that want to switch from one library to another without learning + about all the small differences between those libraries. + + +## Concrete use cases + +- {ref}`use-case-scipy` +- {ref}`use-case-einops` +- {ref}`use-case-xtensor` +- {ref}`use-case-numba` + + +(use-case-scipy)= + +### Use case 1: add hardware accelerator and distributed support to SciPy + +When surveying a representative set of advanced users and research software +engineers in 2019 (for [this NSF proposal](https://figshare.com/articles/Mid-Scale_Research_Infrastructure_-_The_Scientific_Python_Ecosystem/8009441)), +the single most common pain point brought up about SciPy was performance. + +SciPy heavily relies on NumPy (its only non-optional runtime dependency). +NumPy provides an array implementation that's in-memory, CPU-only and +single-threaded. Common performance-related wishes users have are: + +- parallel algorithms (can be multi-threaded or multiprocessing based) +- support for distributed arrays (with Dask in particular) +- support for GPUs and other hardware accelerators (shortened to just "GPU" + in the rest of this use case) + +Some parallelism can be supported in SciPy, it has a `workers` keyword +(similar to scikit-learn's `n_jobs` keyword) that allows specifying to use +parallelism in some algorithms. However SciPy itself will not directly start +depending on a GPU or distributed array implementation, or contain (e.g.) +CUDA code - that's not maintainable given the resources for development. +_However_, there is a way to provide distributed or GPU support. Part of the +solution is provided by NumPy's "array protocols" (see [gh-1](https://github.com/data-apis/array-api/issues/1)), that allow +dispatching to other array implementations. The main problem then becomes how +to know whether this will work with a particular distributed or GPU array +implementation - given that there are zero other array implementations that +are even close to providing full NumPy compatibility - without adding that +array implementation as a dependency. + +It's clear that SciPy functionality that relies on compiled extensions (C, +C++, Cython, Fortran) directly can't easily be run on another array library +than NumPy (see [C API](design_topics/C_API.rst) for more details about this topic). Pure Python +code can work though. There's two main possibilities: + +1. Testing with another package, manually or in CI, and simply provide a list + of functionality that is found to work. Then make ad-hoc fixes to expand + the set that works. +2. Start relying on a well-defined subset of the NumPy API (or a new + NumPy-like API), for which compatibility is guaranteed. + +Option (2) seems strongly preferable, and that "well-defined subset" is _what +an API standard should provide_. Testing will still be needed, to ensure there +are no critical corner cases or bugs between array implementations, however +that's then a very tractable task. + +As a concrete example, consider the spectral analysis functions in `scipy.signal`. +All of those functions (e.g., `periodogram`, `spectrogram`, `csd`, `welch`, `stft`, +`istft`) are pure Python - with the exception of `lombscargle` which is ~40 +lines of Cython - and uses NumPy function calls, array attributes and +indexing. The beginning of each function could be changed to retrieve the +module that implements the array API standard for the given input array type, +and then functions from that module could be used instead of NumPy functions. + +If the user has another array type, say a CuPy or PyTorch array `x` on their +GPU, doing: +``` +from scipy import signal + +signal.welch(x) +``` +will result in: +``` +# For CuPy +ValueError: object __array__ method not producing an array + +# For PyTorch +TypeError: can't convert cuda:0 device type tensor to numpy. +``` +and therefore the user will have to explicitly convert to and from a +`numpy.ndarray` (which is quite inefficient): +``` +# For CuPy +x_np = cupy.asnumpy(x) +freq, Pxx = (cupy.asarray(res) for res in signal.welch(x_np)) + +# For PyTorch +x_np = x.cpu().numpy() +# Note: ends up with tensors on CPU, may still have to move them back +freq, Pxx = (torch.tensor(res) for res in signal.welch(x_np)) +``` +This code will look a little different for each array library. The end goal +here is to be able to write this instead as: +``` +freq, Pxx = signal.welch(x) +``` +and have `freq`, `Pxx` be arrays of the same type and on the same device as `x`. + +```{note} + +This type of use case applies to many other libraries, from scikit-learn +and scikit-image to domain-specific libraries like AstroPy and +scikit-bio, to code written for a single purpose or user. +``` + +(use-case-einops)= + +### Use case 2: simplify einops by removing the backend system + +[einops](https://github.com/arogozhnikov/einops) is a library that provides flexible tensor operations and supports many array libraries (NumPy, TensorFlow, PyTorch, CuPy, MXNet, JAX). +Most of the code in `einops` is: + +- [einops.py](https://github.com/arogozhnikov/einops/blob/master/einops/einops.py) + contains the functions it offers as public API (`rearrange`, `reduce`, `repeat`). +- [_backends.py](https://github.com/arogozhnikov/einops/blob/master/einops/_backends.py) + contains the glue code needed to support that many array libraries. + +The amount of code in each of those two files is almost the same (~550 LoC each). +The typical pattern in `einops.py` is: +``` +def some_func(x): + ... + backend = get_backend(x) + shape = backend.shape(x) + result = backend.reduce(x) + ... +``` +With a standard array API, the `_backends.py` glue layer could almost completely disappear, +because the purpose it serves (providing a unified interface to array operations from each +of the supported backends) is already addressed by the array API standard. +Hence the complete `einops` code base could be close to 50% smaller, and easier to maintain or add to. + +```{note} + +Other libraries that have a similar backend system to support many array libraries +include [TensorLy](https://github.com/tensorly/tensorly), the (now discontinued) +multi-backend version of [Keras](https://github.com/keras-team/keras), +[Unumpy](https://github.com/Quansight-Labs/unumpy) and +[EagerPy](https://github.com/jonasrauber/eagerpy). Many end users and +organizations will also have such glue code - it tends to be needed whenever +one tries to support multiple array types in a single API. +``` + + +(use-case-xtensor)= + +### Use case 3: adding a Python API to xtensor + +[xtensor](https://github.com/xtensor-stack/xtensor) is a C++ array library +that is NumPy-inspired and provides lazy arrays. It has Python (and Julia and R) +bindings, however it does not have a Python array API. + +Xtensor aims to follow NumPy closely, however it only implements a subset of functionality +and documents some API differences in +[Notable differences with NumPy](https://xtensor.readthedocs.io/en/latest/numpy-differences.html). + +Note that other libraries document similar differences, see for example +[this page for JAX](https://jax.readthedocs.io/en/latest/jax.numpy.html) and +[this page for TensorFlow](https://www.tensorflow.org/guide/tf_numpy). + +Each time an array library author designs a new API, they have to choose (a) +what subset of NumPy makes sense to implement, and (b) where to deviate +because NumPy's API for a particular function is suboptimal or the semantics +don't fit their execution model. + +This array API standard aims to provide an API that can be readily adopted, +without having to make the above-mentioned choices. + +```{note} + +XND is another array library, written in C, that still needs a Python API. +Array implementations in other languages are often in a similar situation, +and could translate this array API standard 1:1 to their language. +``` + + +(use-case-numba)= + +### Use case 4: make JIT compilation of array computations easier and more robust + +[Numba](https://github.com/numba/numba) is a Just-In-Time (JIT) compiler for +numerical functions in Python; it is NumPy-aware. [PyPy](https://pypy.org) +is an implementation of Python with a JIT at its core; its NumPy support relies +on running NumPy itself through a compatibility layer (`cpyext`), while a +previous attempt to implement NumPy support directly was unsuccessful. + +Other array libraries may have an internal JIT (e.g., TensorFlow, PyTorch, +JAX, MXNet) or work with an external JIT like +[XLA](https://www.tensorflow.org/xla) or [VTA](https://tvm.apache.org/docs/vta/index.html). + +Numba currently has to jump through some hoops to accommodate NumPy's casting rules +and may not attain full compatibility with NumPy in some cases - see, e.g., +[this](https://github.com/numba/numba/issues/4749) or +[this](https://github.com/numba/numba/issues/5907) example issue regarding (array) scalar +return values. + +An [explicit suggestion from a Numba developer](https://twitter.com/esc___/status/1295389487485333505) +for this array API standard was: + +> for JIT compilers (e.g. Numba) it will be important, that the type of the + returned value(s) depends only on the *types* of the input but not on the + *values*. + +A concrete goal for this use case is to have better matching between +JIT-compiled and non-JIT execution. Here is an example from the Numba code +base, the need for which should be avoided in the future: + +``` +def check(x, y): + got = cfunc(x, y) + np.testing.assert_array_almost_equal(got, pyfunc(x, y)) + # Check the power operation conserved the input's dtype + # (this is different from Numpy, whose behaviour depends on + # the *values* of the arguments -- see PyArray_CanCastArrayTo). + self.assertEqual(got.dtype, x.dtype) +``` diff --git a/spec/2023.12/verification_test_suite.md b/spec/2023.12/verification_test_suite.md new file mode 100644 index 000000000..cbe770e48 --- /dev/null +++ b/spec/2023.12/verification_test_suite.md @@ -0,0 +1,62 @@ +# Verification - test suite + +## Measuring conformance + +In addition to the specification documents, a test suite is being developed to +aid library developers check conformance to the spec. **NOTE: The test suite +is still a work in progress.** It can be found at +. + +It is important to note that while the aim of the array API test suite is to +cover as much of the spec as possible, there are necessarily some aspects of +the spec that are not covered by the test suite, typically because they are +impossible to effectively test. Furthermore, if the test suite appears to +diverge in any way from what the spec documents say, this should be considered +a bug in the test suite. The specification is the ground source of truth. + +## Running the tests + +To run the tests, first clone the [test suite +repo](https://github.com/data-apis/array-api-tests), and install the testing +dependencies, + + pip install pytest hypothesis + +or + + conda install pytest hypothesis + +as well as the array libraries that you want to test. To run the tests, you +need to specify the array library that is to be tested. There are two ways to +do this. One way is to set the `ARRAY_API_TESTS_MODULE` environment variable. +For example + + ARRAY_API_TESTS_MODULE=numpy pytest + +Alternatively, edit the `array_api_tests/_array_module.py` file and change the +line + +```py +array_module = None +``` + +to + +```py +import numpy as array_module +``` + +(replacing `numpy` with the array module namespace to be tested). + +In either case, the tests should be run with the `pytest` command. + +Aside from the two testing dependencies (`pytest` and `hypothesis`), the test +suite has no dependencies. In particular, it does not depend on any specific +array libraries such as NumPy. All tests are run using only the array library +that is being tested, comparing results against the behavior as defined in the +spec. The test suite is designed to be standalone so that it can easily be vendored. + +See the +[README](https://github.com/data-apis/array-api-tests/blob/master/README.md) +in the test suite repo for more information about how to run and interpret the +test suite results. diff --git a/spec/_ghpages/versions.json b/spec/_ghpages/versions.json index ad38507bb..04777bd78 100644 --- a/spec/_ghpages/versions.json +++ b/spec/_ghpages/versions.json @@ -1,6 +1,7 @@ { "2021.12": "2021.12", "2022.12": "2022.12", + "2023.12": "2023.12", "latest": "latest", "draft": "draft" } diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..dd19daea5d1a7b84e37b40dd1eb49bfc451dd50b GIT binary patch literal 6148 zcmeHKOG-mQ5UkcL0xrzbzd)!;&mE;Ee}t(zyiRW?uaiBQ}gHU6T7I45$QbR6ZhEQ9WTW5%(>U(?D*S^_)rPCZTp zSWi@x0#abCz-4Y1-v96DC+7b#NjoVZ1^$%+He0P%OTJR|*2&9xuWj@Py4QTu-M9`4 nL$qUJv}10(9p6Mz)-_-Ac`qCigU)==iTX3(y2zx!Un}qfHa!=x literal 0 HcmV?d00001 diff --git a/src/_array_api_conf.py b/src/_array_api_conf.py index 9233df3c4..ec5d56d58 100644 --- a/src/_array_api_conf.py +++ b/src/_array_api_conf.py @@ -17,7 +17,7 @@ # -- Project information ----------------------------------------------------- project = "Python array API standard" -copyright = "2020-2022, Consortium for Python Data API Standards" +copyright = "2020-2024, Consortium for Python Data API Standards" author = "Consortium for Python Data API Standards" # -- General configuration --------------------------------------------------- diff --git a/src/array_api_stubs/_2023_12/__init__.py b/src/array_api_stubs/_2023_12/__init__.py new file mode 100644 index 000000000..8415f2765 --- /dev/null +++ b/src/array_api_stubs/_2023_12/__init__.py @@ -0,0 +1,25 @@ +"""Function stubs and API documentation for the array API standard.""" + +from .array_object import * +from .constants import * +from .creation_functions import * +from .data_type_functions import * +from . import data_types as dtype +from .elementwise_functions import * +from .indexing_functions import * +from .linear_algebra_functions import * +from .manipulation_functions import * +from .searching_functions import * +from .set_functions import * +from .sorting_functions import * +from .statistical_functions import * +from .utility_functions import * +from . import linalg +from . import fft +from . import info + + +__array_api_version__: str = "YYYY.MM" +""" +String representing the version of the array API specification which the conforming implementation adheres to. +""" diff --git a/src/array_api_stubs/_2023_12/_types.py b/src/array_api_stubs/_2023_12/_types.py new file mode 100644 index 000000000..7c3d903d7 --- /dev/null +++ b/src/array_api_stubs/_2023_12/_types.py @@ -0,0 +1,144 @@ +""" +Types for type annotations used in the array API standard. + +The type variables should be replaced with the actual types for a given +library, e.g., for NumPy TypeVar('array') would be replaced with ndarray. +""" +from __future__ import annotations + +__all__ = [ + "Any", + "List", + "Literal", + "NestedSequence", + "Optional", + "PyCapsule", + "SupportsBufferProtocol", + "SupportsDLPack", + "Tuple", + "Union", + "Sequence", + "array", + "device", + "dtype", + "ellipsis", + "finfo_object", + "iinfo_object", + "Enum", + "DefaultDataTypes", + "DataTypes", + "Capabilities", + "Info", +] + +from dataclasses import dataclass +from typing import ( + Any, + List, + Literal, + Optional, + Sequence, + Tuple, + TypedDict, + TypeVar, + Union, + Protocol, +) +from enum import Enum + +array = TypeVar("array") +device = TypeVar("device") +dtype = TypeVar("dtype") +SupportsDLPack = TypeVar("SupportsDLPack") +SupportsBufferProtocol = TypeVar("SupportsBufferProtocol") +PyCapsule = TypeVar("PyCapsule") +# ellipsis cannot actually be imported from anywhere, so include a dummy here +# to keep pyflakes happy. https://github.com/python/typeshed/issues/3556 +ellipsis = TypeVar("ellipsis") + + +@dataclass +class finfo_object: + """Dataclass returned by `finfo`.""" + + bits: int + eps: float + max: float + min: float + smallest_normal: float + dtype: dtype + + +@dataclass +class iinfo_object: + """Dataclass returned by `iinfo`.""" + + bits: int + max: int + min: int + dtype: dtype + + +_T_co = TypeVar("_T_co", covariant=True) + + +class NestedSequence(Protocol[_T_co]): + def __getitem__(self, key: int, /) -> Union[_T_co, NestedSequence[_T_co]]: + ... + + def __len__(self, /) -> int: + ... + + +class Info(Protocol): + """Namespace returned by `__array_namespace_info__`.""" + + def capabilities(self) -> Capabilities: + ... + + def default_device(self) -> device: + ... + + def default_dtypes(self, *, device: Optional[device]) -> DefaultDataTypes: + ... + + def devices(self) -> List[device]: + ... + + def dtypes( + self, *, device: Optional[device], kind: Optional[Union[str, Tuple[str, ...]]] + ) -> DataTypes: + ... + + +DefaultDataTypes = TypedDict( + "DefaultDataTypes", + { + "real floating": dtype, + "complex floating": dtype, + "integral": dtype, + "indexing": dtype, + }, +) +DataTypes = TypedDict( + "DataTypes", + { + "bool": dtype, + "float32": dtype, + "float64": dtype, + "complex64": dtype, + "complex128": dtype, + "int8": dtype, + "int16": dtype, + "int32": dtype, + "int64": dtype, + "uint8": dtype, + "uint16": dtype, + "uint32": dtype, + "uint64": dtype, + }, + total=False, +) +Capabilities = TypedDict( + "Capabilities", {"boolean indexing": bool, "data-dependent shapes": bool} +) diff --git a/src/array_api_stubs/_2023_12/array_object.py b/src/array_api_stubs/_2023_12/array_object.py new file mode 100644 index 000000000..6dd70c278 --- /dev/null +++ b/src/array_api_stubs/_2023_12/array_object.py @@ -0,0 +1,1219 @@ +from __future__ import annotations + +__all__ = ["array"] + +from ._types import ( + array, + dtype as Dtype, + device as Device, + Optional, + Tuple, + Union, + Any, + PyCapsule, + Enum, + ellipsis, +) + + +class _array: + def __init__(self: array) -> None: + """Initialize the attributes for the array object class.""" + + @property + def dtype(self: array) -> Dtype: + """ + Data type of the array elements. + + Returns + ------- + out: dtype + array data type. + """ + + @property + def device(self: array) -> Device: + """ + Hardware device the array data resides on. + + Returns + ------- + out: device + a ``device`` object (see :ref:`device-support`). + """ + + @property + def mT(self: array) -> array: + """ + Transpose of a matrix (or a stack of matrices). + + If an array instance has fewer than two dimensions, an error should be raised. + + Returns + ------- + out: array + array whose last two dimensions (axes) are permuted in reverse order relative to original array (i.e., for an array instance having shape ``(..., M, N)``, the returned array must have shape ``(..., N, M)``). The returned array must have the same data type as the original array. + """ + + @property + def ndim(self: array) -> int: + """ + Number of array dimensions (axes). + + Returns + ------- + out: int + number of array dimensions (axes). + """ + + @property + def shape(self: array) -> Tuple[Optional[int], ...]: + """ + Array dimensions. + + Returns + ------- + out: Tuple[Optional[int], ...] + array dimensions. An array dimension must be ``None`` if and only if a dimension is unknown. + + + .. note:: + For array libraries having graph-based computational models, array dimensions may be unknown due to data-dependent operations (e.g., boolean indexing; ``A[:, B > 0]``) and thus cannot be statically resolved without knowing array contents. + + .. note:: + The returned value should be a tuple; however, where warranted, an array library may choose to return a custom shape object. If an array library returns a custom shape object, the object must be immutable, must support indexing for dimension retrieval, and must behave similarly to a tuple. + """ + + @property + def size(self: array) -> Optional[int]: + """ + Number of elements in an array. + + .. note:: + This must equal the product of the array's dimensions. + + Returns + ------- + out: Optional[int] + number of elements in an array. The returned value must be ``None`` if and only if one or more array dimensions are unknown. + + + .. note:: + For array libraries having graph-based computational models, an array may have unknown dimensions due to data-dependent operations. + """ + + @property + def T(self: array) -> array: + """ + Transpose of the array. + + The array instance must be two-dimensional. If the array instance is not two-dimensional, an error should be raised. + + Returns + ------- + out: array + two-dimensional array whose first and last dimensions (axes) are permuted in reverse order relative to original array. The returned array must have the same data type as the original array. + + + .. note:: + Limiting the transpose to two-dimensional arrays (matrices) deviates from the NumPy et al practice of reversing all axes for arrays having more than two-dimensions. This is intentional, as reversing all axes was found to be problematic (e.g., conflicting with the mathematical definition of a transpose which is limited to matrices; not operating on batches of matrices; et cetera). In order to reverse all axes, one is recommended to use the functional ``permute_dims`` interface found in this specification. + """ + + def __abs__(self: array, /) -> array: + """ + Calculates the absolute value for each element of an array instance. + + For real-valued input arrays, the element-wise result has the same magnitude as the respective element in ``x`` but has positive sign. + + .. note:: + For signed integer data types, the absolute value of the minimum representable integer is implementation-dependent. + + Parameters + ---------- + self: array + array instance. Should have a numeric data type. + + Returns + ------- + out: array + an array containing the element-wise absolute value. If ``self`` has a real-valued data type, the returned array must have the same data type as ``self``. If ``self`` has a complex floating-point data type, the returned arrayed must have a real-valued floating-point data type whose precision matches the precision of ``self`` (e.g., if ``self`` is ``complex128``, then the returned array must have a ``float64`` data type). + + Notes + ----- + + .. note:: + Element-wise results, including special cases, must equal the results returned by the equivalent element-wise function :func:`~array_api.abs`. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + def __add__(self: array, other: Union[int, float, array], /) -> array: + """ + Calculates the sum for each element of an array instance with the respective element of the array ``other``. + + Parameters + ---------- + self: array + array instance (augend array). Should have a numeric data type. + other: Union[int, float, array] + addend array. Must be compatible with ``self`` (see :ref:`broadcasting`). Should have a numeric data type. + + Returns + ------- + out: array + an array containing the element-wise sums. The returned array must have a data type determined by :ref:`type-promotion`. + + Notes + ----- + + .. note:: + Element-wise results, including special cases, must equal the results returned by the equivalent element-wise function :func:`~array_api.add`. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + def __and__(self: array, other: Union[int, bool, array], /) -> array: + """ + Evaluates ``self_i & other_i`` for each element of an array instance with the respective element of the array ``other``. + + Parameters + ---------- + self: array + array instance. Should have an integer or boolean data type. + other: Union[int, bool, array] + other array. Must be compatible with ``self`` (see :ref:`broadcasting`). Should have an integer or boolean data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type determined by :ref:`type-promotion`. + + + .. note:: + Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.bitwise_and`. + """ + + def __array_namespace__( + self: array, /, *, api_version: Optional[str] = None + ) -> Any: + """ + Returns an object that has all the array API functions on it. + + Parameters + ---------- + self: array + array instance. + api_version: Optional[str] + string representing the version of the array API specification to be returned, in ``'YYYY.MM'`` form, for example, ``'2020.10'``. If it is ``None``, it should return the namespace corresponding to latest version of the array API specification. If the given version is invalid or not implemented for the given module, an error should be raised. Default: ``None``. + + Returns + ------- + out: Any + an object representing the array API namespace. It should have every top-level function defined in the specification as an attribute. It may contain other public names as well, but it is recommended to only include those names that are part of the specification. + """ + + def __bool__(self: array, /) -> bool: + """ + Converts a zero-dimensional array to a Python ``bool`` object. + + Parameters + ---------- + self: array + zero-dimensional array instance. + + Returns + ------- + out: bool + a Python ``bool`` object representing the single element of the array. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If ``self`` is ``NaN``, the result is ``True``. + - If ``self`` is either ``+infinity`` or ``-infinity``, the result is ``True``. + - If ``self`` is either ``+0`` or ``-0``, the result is ``False``. + + For complex floating-point operands, special cases must be handled as if the operation is implemented as the logical AND of ``bool(real(self))`` and ``bool(imag(self))``. + + **Lazy implementations** + + The Python language requires the return value to be of type ``bool``. Lazy implementations are therefore not able to return any kind of lazy/delayed object here and should raise a ``ValueError`` instead. + + .. versionchanged:: 2022.12 + Added boolean and complex data type support. + + .. versionchanged:: 2023.12 + Allowed lazy implementations to error. + """ + + def __complex__(self: array, /) -> complex: + """ + Converts a zero-dimensional array to a Python ``complex`` object. + + Parameters + ---------- + self: array + zero-dimensional array instance. + + Returns + ------- + out: complex + a Python ``complex`` object representing the single element of the array instance. + + Notes + ----- + + **Special cases** + + For boolean operands, + + - If ``self`` is ``True``, the result is ``1+0j``. + - If ``self`` is ``False``, the result is ``0+0j``. + + For real-valued floating-point operands, + + - If ``self`` is ``NaN``, the result is ``NaN + NaN j``. + - If ``self`` is ``+infinity``, the result is ``+infinity + 0j``. + - If ``self`` is ``-infinity``, the result is ``-infinity + 0j``. + - If ``self`` is a finite number, the result is ``self + 0j``. + + **Lazy implementations** + + The Python language requires the return value to be of type ``complex``. Lazy implementations are therefore not able to return any kind of lazy/delayed object here and should raise a ``ValueError`` instead. + + .. versionadded:: 2022.12 + + .. versionchanged:: 2023.12 + Allowed lazy implementations to error. + """ + + def __dlpack__( + self: array, + /, + *, + stream: Optional[Union[int, Any]] = None, + max_version: Optional[tuple[int, int]] = None, + dl_device: Optional[tuple[Enum, int]] = None, + copy: Optional[bool] = None, + ) -> PyCapsule: + """ + Exports the array for consumption by :func:`~array_api.from_dlpack` as a DLPack capsule. + + Parameters + ---------- + self: array + array instance. + stream: Optional[Union[int, Any]] + for CUDA and ROCm, a Python integer representing a pointer to a stream, on devices that support streams. ``stream`` is provided by the consumer to the producer to instruct the producer to ensure that operations can safely be performed on the array (e.g., by inserting a dependency between streams via "wait for event"). The pointer must be an integer larger than or equal to ``-1`` (see below for allowed values on each platform). If ``stream`` is ``-1``, the value may be used by the consumer to signal "producer must not perform any synchronization". The ownership of the stream stays with the consumer. On CPU and other device types without streams, only ``None`` is accepted. + + For other device types which do have a stream, queue, or similar synchronization/ordering mechanism, the most appropriate type to use for ``stream`` is not yet determined. E.g., for SYCL one may want to use an object containing an in-order ``cl::sycl::queue``. This is allowed when libraries agree on such a convention, and may be standardized in a future version of this API standard. + + .. note:: + Support for a ``stream`` value other than ``None`` is optional and implementation-dependent. + + Device-specific values of ``stream`` for CUDA: + + - ``None``: producer must assume the legacy default stream (default). + - ``1``: the legacy default stream. + - ``2``: the per-thread default stream. + - ``> 2``: stream number represented as a Python integer. + - ``0`` is disallowed due to its ambiguity: ``0`` could mean either ``None``, ``1``, or ``2``. + + Device-specific values of ``stream`` for ROCm: + + - ``None``: producer must assume the legacy default stream (default). + - ``0``: the default stream. + - ``> 2``: stream number represented as a Python integer. + - Using ``1`` and ``2`` is not supported. + + .. note:: + When ``dl_device`` is provided explicitly, ``stream`` must be a valid + construct for the specified device type. In particular, when ``kDLCPU`` + is in use, ``stream`` must be ``None`` and a synchronization must be + performed to ensure data safety. + + .. admonition:: Tip + :class: important + + It is recommended that implementers explicitly handle streams. If + they use the legacy default stream, specifying ``1`` (CUDA) or ``0`` + (ROCm) is preferred. ``None`` is a safe default for developers who do + not want to think about stream handling at all, potentially at the + cost of more synchronizations than necessary. + max_version: Optional[tuple[int, int]] + the maximum DLPack version that the *consumer* (i.e., the caller of + ``__dlpack__``) supports, in the form of a 2-tuple ``(major, minor)``. + This method may return a capsule of version ``max_version`` (recommended + if it does support that), or of a different version. + This means the consumer must verify the version even when + `max_version` is passed. + dl_device: Optional[tuple[enum.Enum, int]] + the DLPack device type. Default is ``None``, meaning the exported capsule + should be on the same device as ``self`` is. When specified, the format + must be a 2-tuple, following that of the return value of :meth:`array.__dlpack_device__`. + If the device type cannot be handled by the producer, this function must + raise ``BufferError``. + + The v2023.12 standard only mandates that a compliant library should offer a way for + ``__dlpack__`` to return a capsule referencing an array whose underlying memory is + accessible to the Python interpreter (represented by the ``kDLCPU`` enumerator in DLPack). + If a copy must be made to enable this support but ``copy`` is set to ``False``, the + function must raise ``ValueError``. + + Other device kinds will be considered for standardization in a future version of this + API standard. + copy: Optional[bool] + boolean indicating whether or not to copy the input. If ``True``, the + function must always copy (performed by the producer). If ``False``, the + function must never copy, and raise a ``BufferError`` in case a copy is + deemed necessary (e.g. if a cross-device data movement is requested, and + it is not possible without a copy). If ``None``, the function must reuse + the existing memory buffer if possible and copy otherwise. Default: ``None``. + + When a copy happens, the ``DLPACK_FLAG_BITMASK_IS_COPIED`` flag must be set. + + .. note:: + If a copy happens, and if the consumer-provided ``stream`` and ``dl_device`` + can be understood by the producer, the copy must be performed over ``stream``. + + Returns + ------- + capsule: PyCapsule + a DLPack capsule for the array. See :ref:`data-interchange` for details. + + Raises + ------ + BufferError + Implementations should raise ``BufferError`` when the data cannot + be exported as DLPack (e.g., incompatible dtype or strides). Other + errors are raised when export fails for other reasons (e.g., incorrect + arguments passed or out of memory). + + Notes + ----- + The DLPack version scheme is SemVer, where the major DLPack versions + represent ABI breaks, and minor versions represent ABI-compatible additions + (e.g., new enum values for new data types or device types). + + The ``max_version`` keyword was introduced in v2023.12, and goes + together with the ``DLManagedTensorVersioned`` struct added in DLPack + 1.0. This keyword may not be used by consumers until a later time after + introduction, because producers may implement the support at a different + point in time. + + It is recommended for the producer to use this logic in the implementation + of ``__dlpack__``: + + .. code:: python + + if max_version is None: + # Keep and use the DLPack 0.X implementation + # Note: from March 2025 onwards (but ideally as late as + # possible), it's okay to raise BufferError here + else: + # We get to produce `DLManagedTensorVersioned` now. Note that + # our_own_dlpack_version is the max version that the *producer* + # supports and fills in the `DLManagedTensorVersioned::version` + # field + if max_version >= our_own_dlpack_version: + # Consumer understands us, just return a Capsule with our max version + elif max_version[0] == our_own_dlpack_version[0]: + # major versions match, we should still be fine here - + # return our own max version + else: + # if we're at a higher major version internally, did we + # keep an implementation of the older major version around? + # For example, if the producer is on DLPack 1.x and the consumer + # is 0.y, can the producer still export a capsule containing + # DLManagedTensor and not DLManagedTensorVersioned? + # If so, use that. Else, the producer should raise a BufferError + # here to tell users that the consumer's max_version is too + # old to allow the data exchange to happen. + + And this logic for the consumer in :func:`~array_api.from_dlpack`: + + .. code:: python + + try: + x.__dlpack__(max_version=(1, 0), ...) + # if it succeeds, store info from the capsule named "dltensor_versioned", + # and need to set the name to "used_dltensor_versioned" when we're done + except TypeError: + x.__dlpack__(...) + + This logic is also applicable to handling of the new ``dl_device`` and ``copy`` + keywords. + + DLPack 1.0 added a flag to indicate that the array is read-only + (``DLPACK_FLAG_BITMASK_READ_ONLY``). A consumer that does not support + read-only arrays should ignore this flag (this is preferred over + raising an exception; the user is then responsible for ensuring the + memory isn't modified). + + .. versionchanged:: 2022.12 + Added BufferError. + + .. versionchanged:: 2023.12 + Added the ``max_version``, ``dl_device``, and ``copy`` keywords. + + .. versionchanged:: 2023.12 + Added recommendation for handling read-only arrays. + """ + + def __dlpack_device__(self: array, /) -> Tuple[Enum, int]: + """ + Returns device type and device ID in DLPack format. Meant for use within :func:`~array_api.from_dlpack`. + + Parameters + ---------- + self: array + array instance. + + Returns + ------- + device: Tuple[Enum, int] + a tuple ``(device_type, device_id)`` in DLPack format. Valid device type enum members are: + + :: + + CPU = 1 + CUDA = 2 + CPU_PINNED = 3 + OPENCL = 4 + VULKAN = 7 + METAL = 8 + VPI = 9 + ROCM = 10 + CUDA_MANAGED = 13 + ONE_API = 14 + """ + + def __eq__(self: array, other: Union[int, float, bool, array], /) -> array: + r""" + Computes the truth value of ``self_i == other_i`` for each element of an array instance with the respective element of the array ``other``. + + Parameters + ---------- + self: array + array instance. May have any data type. + other: Union[int, float, bool, array] + other array. Must be compatible with ``self`` (see :ref:`broadcasting`). May have any data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type of ``bool``. + + + .. note:: + Element-wise results, including special cases, must equal the results returned by the equivalent element-wise function :func:`~array_api.equal`. + """ + + def __float__(self: array, /) -> float: + """ + Converts a zero-dimensional array to a Python ``float`` object. + + .. note:: + Casting integer values outside the representable bounds of Python's float type is not specified and is implementation-dependent. + + Parameters + ---------- + self: array + zero-dimensional array instance. Should have a real-valued or boolean data type. If ``self`` has a complex floating-point data type, the function must raise a ``TypeError``. + + Returns + ------- + out: float + a Python ``float`` object representing the single element of the array instance. + + Notes + ----- + + **Special cases** + + For boolean operands, + + - If ``self`` is ``True``, the result is ``1``. + - If ``self`` is ``False``, the result is ``0``. + + **Lazy implementations** + + The Python language requires the return value to be of type ``float``. Lazy implementations are therefore not able to return any kind of lazy/delayed object here and should raise a ``ValueError`` instead. + + .. versionchanged:: 2022.12 + Added boolean and complex data type support. + + .. versionchanged:: 2023.12 + Allowed lazy implementations to error. + """ + + def __floordiv__(self: array, other: Union[int, float, array], /) -> array: + """ + Evaluates ``self_i // other_i`` for each element of an array instance with the respective element of the array ``other``. + + .. note:: + For input arrays which promote to an integer data type, the result of division by zero is unspecified and thus implementation-defined. + + Parameters + ---------- + self: array + array instance. Should have a real-valued data type. + other: Union[int, float, array] + other array. Must be compatible with ``self`` (see :ref:`broadcasting`). Should have a real-valued data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type determined by :ref:`type-promotion`. + + + .. note:: + Element-wise results, including special cases, must equal the results returned by the equivalent element-wise function :func:`~array_api.floor_divide`. + """ + + def __ge__(self: array, other: Union[int, float, array], /) -> array: + """ + Computes the truth value of ``self_i >= other_i`` for each element of an array instance with the respective element of the array ``other``. + + .. note:: + For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-dependent (see :ref:`complex-number-ordering`). + + Parameters + ---------- + self: array + array instance. Should have a real-valued data type. + other: Union[int, float, array] + other array. Must be compatible with ``self`` (see :ref:`broadcasting`). Should have a real-valued data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type of ``bool``. + + + .. note:: + Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.greater_equal`. + """ + + def __getitem__( + self: array, + key: Union[ + int, + slice, + ellipsis, + None, + Tuple[Union[int, slice, ellipsis, None], ...], + array, + ], + /, + ) -> array: + """ + Returns ``self[key]``. + + Parameters + ---------- + self: array + array instance. + key: Union[int, slice, ellipsis, None, Tuple[Union[int, slice, ellipsis, None], ...], array] + index key. + + Returns + ------- + out: array + an array containing the accessed value(s). The returned array must have the same data type as ``self``. + """ + + def __gt__(self: array, other: Union[int, float, array], /) -> array: + """ + Computes the truth value of ``self_i > other_i`` for each element of an array instance with the respective element of the array ``other``. + + .. note:: + For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-dependent (see :ref:`complex-number-ordering`). + + Parameters + ---------- + self: array + array instance. Should have a real-valued data type. + other: Union[int, float, array] + other array. Must be compatible with ``self`` (see :ref:`broadcasting`). Should have a real-valued data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type of ``bool``. + + + .. note:: + Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.greater`. + """ + + def __index__(self: array, /) -> int: + """ + Converts a zero-dimensional integer array to a Python ``int`` object. + + .. note:: + This method is called to implement `operator.index() `_. See also `PEP 357 `_. + + Parameters + ---------- + self: array + zero-dimensional array instance. Should have an integer data type. If ``self`` has a floating-point data type, the function must raise a ``TypeError``. + + Returns + ------- + out: int + a Python ``int`` object representing the single element of the array instance. + + Notes + ----- + + **Lazy implementations** + + The Python language requires the return value to be of type ``int``. Lazy implementations are therefore not able to return any kind of lazy/delayed object here and should raise a ``ValueError`` instead. + + .. versionchanged:: 2023.12 + Allowed lazy implementations to error. + """ + + def __int__(self: array, /) -> int: + """ + Converts a zero-dimensional array to a Python ``int`` object. + + Parameters + ---------- + self: array + zero-dimensional array instance. Should have a real-valued or boolean data type. If ``self`` has a complex floating-point data type, the function must raise a ``TypeError``. + + Returns + ------- + out: int + a Python ``int`` object representing the single element of the array instance. + + Notes + ----- + + **Special cases** + + For boolean operands, + + - If ``self`` is ``True``, the result is ``1``. + - If ``self`` is ``False``, the result is ``0``. + + For floating-point operands, + + - If ``self`` is a finite number, the result is the integer part of ``self``. + - If ``self`` is ``-0``, the result is ``0``. + + **Raises** + + For floating-point operands, + + - If ``self`` is either ``+infinity`` or ``-infinity``, raise ``OverflowError``. + - If ``self`` is ``NaN``, raise ``ValueError``. + + Notes + ----- + + **Lazy implementations** + + The Python language requires the return value to be of type ``int``. Lazy implementations are therefore not able to return any kind of lazy/delayed object here and should raise a ``ValueError`` instead. + + .. versionchanged:: 2022.12 + Added boolean and complex data type support. + + .. versionchanged:: 2023.12 + Allowed lazy implementations to error. + """ + + def __invert__(self: array, /) -> array: + """ + Evaluates ``~self_i`` for each element of an array instance. + + Parameters + ---------- + self: array + array instance. Should have an integer or boolean data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have the same data type as `self`. + + + .. note:: + Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.bitwise_invert`. + """ + + def __le__(self: array, other: Union[int, float, array], /) -> array: + """ + Computes the truth value of ``self_i <= other_i`` for each element of an array instance with the respective element of the array ``other``. + + .. note:: + For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-dependent (see :ref:`complex-number-ordering`). + + Parameters + ---------- + self: array + array instance. Should have a real-valued data type. + other: Union[int, float, array] + other array. Must be compatible with ``self`` (see :ref:`broadcasting`). Should have a real-valued data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type of ``bool``. + + + .. note:: + Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.less_equal`. + """ + + def __lshift__(self: array, other: Union[int, array], /) -> array: + """ + Evaluates ``self_i << other_i`` for each element of an array instance with the respective element of the array ``other``. + + Parameters + ---------- + self: array + array instance. Should have an integer data type. + other: Union[int, array] + other array. Must be compatible with ``self`` (see :ref:`broadcasting`). Should have an integer data type. Each element must be greater than or equal to ``0``. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have the same data type as ``self``. + + + .. note:: + Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.bitwise_left_shift`. + """ + + def __lt__(self: array, other: Union[int, float, array], /) -> array: + """ + Computes the truth value of ``self_i < other_i`` for each element of an array instance with the respective element of the array ``other``. + + .. note:: + For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-dependent (see :ref:`complex-number-ordering`). + + Parameters + ---------- + self: array + array instance. Should have a real-valued data type. + other: Union[int, float, array] + other array. Must be compatible with ``self`` (see :ref:`broadcasting`). Should have a real-valued data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type of ``bool``. + + + .. note:: + Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.less`. + """ + + def __matmul__(self: array, other: array, /) -> array: + """ + Computes the matrix product. + + .. note:: + The ``matmul`` function must implement the same semantics as the built-in ``@`` operator (see `PEP 465 `_). + + Parameters + ---------- + self: array + array instance. Should have a numeric data type. Must have at least one dimension. If ``self`` is one-dimensional having shape ``(M,)`` and ``other`` has more than one dimension, ``self`` must be promoted to a two-dimensional array by prepending ``1`` to its dimensions (i.e., must have shape ``(1, M)``). After matrix multiplication, the prepended dimensions in the returned array must be removed. If ``self`` has more than one dimension (including after vector-to-matrix promotion), ``shape(self)[:-2]`` must be compatible with ``shape(other)[:-2]`` (after vector-to-matrix promotion) (see :ref:`broadcasting`). If ``self`` has shape ``(..., M, K)``, the innermost two dimensions form matrices on which to perform matrix multiplication. + other: array + other array. Should have a numeric data type. Must have at least one dimension. If ``other`` is one-dimensional having shape ``(N,)`` and ``self`` has more than one dimension, ``other`` must be promoted to a two-dimensional array by appending ``1`` to its dimensions (i.e., must have shape ``(N, 1)``). After matrix multiplication, the appended dimensions in the returned array must be removed. If ``other`` has more than one dimension (including after vector-to-matrix promotion), ``shape(other)[:-2]`` must be compatible with ``shape(self)[:-2]`` (after vector-to-matrix promotion) (see :ref:`broadcasting`). If ``other`` has shape ``(..., K, N)``, the innermost two dimensions form matrices on which to perform matrix multiplication. + + + .. note:: + If either ``x1`` or ``x2`` has a complex floating-point data type, neither argument must be complex-conjugated or transposed. If conjugation and/or transposition is desired, these operations should be explicitly performed prior to computing the matrix product. + + Returns + ------- + out: array + - if both ``self`` and ``other`` are one-dimensional arrays having shape ``(N,)``, a zero-dimensional array containing the inner product as its only element. + - if ``self`` is a two-dimensional array having shape ``(M, K)`` and ``other`` is a two-dimensional array having shape ``(K, N)``, a two-dimensional array containing the `conventional matrix product `_ and having shape ``(M, N)``. + - if ``self`` is a one-dimensional array having shape ``(K,)`` and ``other`` is an array having shape ``(..., K, N)``, an array having shape ``(..., N)`` (i.e., prepended dimensions during vector-to-matrix promotion must be removed) and containing the `conventional matrix product `_. + - if ``self`` is an array having shape ``(..., M, K)`` and ``other`` is a one-dimensional array having shape ``(K,)``, an array having shape ``(..., M)`` (i.e., appended dimensions during vector-to-matrix promotion must be removed) and containing the `conventional matrix product `_. + - if ``self`` is a two-dimensional array having shape ``(M, K)`` and ``other`` is an array having shape ``(..., K, N)``, an array having shape ``(..., M, N)`` and containing the `conventional matrix product `_ for each stacked matrix. + - if ``self`` is an array having shape ``(..., M, K)`` and ``other`` is a two-dimensional array having shape ``(K, N)``, an array having shape ``(..., M, N)`` and containing the `conventional matrix product `_ for each stacked matrix. + - if either ``self`` or ``other`` has more than two dimensions, an array having a shape determined by :ref:`broadcasting` ``shape(self)[:-2]`` against ``shape(other)[:-2]`` and containing the `conventional matrix product `_ for each stacked matrix. + - The returned array must have a data type determined by :ref:`type-promotion`. + + Notes + ----- + + .. note:: + Results must equal the results returned by the equivalent function :func:`~array_api.matmul`. + + **Raises** + + - if either ``self`` or ``other`` is a zero-dimensional array. + - if ``self`` is a one-dimensional array having shape ``(K,)``, ``other`` is a one-dimensional array having shape ``(L,)``, and ``K != L``. + - if ``self`` is a one-dimensional array having shape ``(K,)``, ``other`` is an array having shape ``(..., L, N)``, and ``K != L``. + - if ``self`` is an array having shape ``(..., M, K)``, ``other`` is a one-dimensional array having shape ``(L,)``, and ``K != L``. + - if ``self`` is an array having shape ``(..., M, K)``, ``other`` is an array having shape ``(..., L, N)``, and ``K != L``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + def __mod__(self: array, other: Union[int, float, array], /) -> array: + """ + Evaluates ``self_i % other_i`` for each element of an array instance with the respective element of the array ``other``. + + .. note:: + For input arrays which promote to an integer data type, the result of division by zero is unspecified and thus implementation-defined. + + Parameters + ---------- + self: array + array instance. Should have a real-valued data type. + other: Union[int, float, array] + other array. Must be compatible with ``self`` (see :ref:`broadcasting`). Should have a real-valued data type. + + Returns + ------- + out: array + an array containing the element-wise results. Each element-wise result must have the same sign as the respective element ``other_i``. The returned array must have a real-valued floating-point data type determined by :ref:`type-promotion`. + + + .. note:: + Element-wise results, including special cases, must equal the results returned by the equivalent element-wise function :func:`~array_api.remainder`. + """ + + def __mul__(self: array, other: Union[int, float, array], /) -> array: + r""" + Calculates the product for each element of an array instance with the respective element of the array ``other``. + + .. note:: + Floating-point multiplication is not always associative due to finite precision. + + Parameters + ---------- + self: array + array instance. Should have a numeric data type. + other: Union[int, float, array] + other array. Must be compatible with ``self`` (see :ref:`broadcasting`). Should have a numeric data type. + + Returns + ------- + out: array + an array containing the element-wise products. The returned array must have a data type determined by :ref:`type-promotion`. + + Notes + ----- + + .. note:: + Element-wise results, including special cases, must equal the results returned by the equivalent element-wise function :func:`~array_api.multiply`. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + def __ne__(self: array, other: Union[int, float, bool, array], /) -> array: + """ + Computes the truth value of ``self_i != other_i`` for each element of an array instance with the respective element of the array ``other``. + + Parameters + ---------- + self: array + array instance. May have any data type. + other: Union[int, float, bool, array] + other array. Must be compatible with ``self`` (see :ref:`broadcasting`). May have any data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type of ``bool`` (i.e., must be a boolean array). + + + Notes + ----- + + .. note:: + Element-wise results, including special cases, must equal the results returned by the equivalent element-wise function :func:`~array_api.not_equal`. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + def __neg__(self: array, /) -> array: + """ + Evaluates ``-self_i`` for each element of an array instance. + + .. note:: + For signed integer data types, the numerical negative of the minimum representable integer is implementation-dependent. + + .. note:: + If ``self`` has a complex floating-point data type, both the real and imaginary components for each ``self_i`` must be negated (a result which follows from the rules of complex number multiplication). + + Parameters + ---------- + self: array + array instance. Should have a numeric data type. + + Returns + ------- + out: array + an array containing the evaluated result for each element in ``self``. The returned array must have a data type determined by :ref:`type-promotion`. + + Notes + ----- + + .. note:: + Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.negative`. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + def __or__(self: array, other: Union[int, bool, array], /) -> array: + """ + Evaluates ``self_i | other_i`` for each element of an array instance with the respective element of the array ``other``. + + Parameters + ---------- + self: array + array instance. Should have an integer or boolean data type. + other: Union[int, bool, array] + other array. Must be compatible with ``self`` (see :ref:`broadcasting`). Should have an integer or boolean data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type determined by :ref:`type-promotion`. + + + .. note:: + Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.bitwise_or`. + """ + + def __pos__(self: array, /) -> array: + """ + Evaluates ``+self_i`` for each element of an array instance. + + Parameters + ---------- + self: array + array instance. Should have a numeric data type. + + Returns + ------- + out: array + an array containing the evaluated result for each element. The returned array must have the same data type as ``self``. + + Notes + ----- + + .. note:: + Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.positive`. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + def __pow__(self: array, other: Union[int, float, array], /) -> array: + r""" + Calculates an implementation-dependent approximation of exponentiation by raising each element (the base) of an array instance to the power of ``other_i`` (the exponent), where ``other_i`` is the corresponding element of the array ``other``. + + .. note:: + If both ``self`` and ``other`` have integer data types, the result of ``__pow__`` when `other_i` is negative (i.e., less than zero) is unspecified and thus implementation-dependent. + + If ``self`` has an integer data type and ``other`` has a floating-point data type, behavior is implementation-dependent, as type promotion between data type "kinds" (e.g., integer versus floating-point) is unspecified. + + Parameters + ---------- + self: array + array instance whose elements correspond to the exponentiation base. Should have a numeric data type. + other: Union[int, float, array] + other array whose elements correspond to the exponentiation exponent. Must be compatible with ``self`` (see :ref:`broadcasting`). Should have a numeric data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type determined by :ref:`type-promotion`. + + Notes + ----- + + .. note:: + Element-wise results, including special cases, must equal the results returned by the equivalent element-wise function :func:`~array_api.pow`. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + def __rshift__(self: array, other: Union[int, array], /) -> array: + """ + Evaluates ``self_i >> other_i`` for each element of an array instance with the respective element of the array ``other``. + + Parameters + ---------- + self: array + array instance. Should have an integer data type. + other: Union[int, array] + other array. Must be compatible with ``self`` (see :ref:`broadcasting`). Should have an integer data type. Each element must be greater than or equal to ``0``. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have the same data type as ``self``. + + + .. note:: + Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.bitwise_right_shift`. + """ + + def __setitem__( + self: array, + key: Union[ + int, slice, ellipsis, Tuple[Union[int, slice, ellipsis], ...], array + ], + value: Union[int, float, bool, array], + /, + ) -> None: + """ + Sets ``self[key]`` to ``value``. + + Parameters + ---------- + self: array + array instance. + key: Union[int, slice, ellipsis, Tuple[Union[int, slice, ellipsis], ...], array] + index key. + value: Union[int, float, bool, array] + value(s) to set. Must be compatible with ``self[key]`` (see :ref:`broadcasting`). + + + .. note:: + + Setting array values must not affect the data type of ``self``. + + When ``value`` is a Python scalar (i.e., ``int``, ``float``, ``bool``), behavior must follow specification guidance on mixing arrays with Python scalars (see :ref:`type-promotion`). + + When ``value`` is an ``array`` of a different data type than ``self``, how values are cast to the data type of ``self`` is implementation defined. + """ + + def __sub__(self: array, other: Union[int, float, array], /) -> array: + """ + Calculates the difference for each element of an array instance with the respective element of the array ``other``. + + The result of ``self_i - other_i`` must be the same as ``self_i + (-other_i)`` and must be governed by the same floating-point rules as addition (see :meth:`array.__add__`). + + Parameters + ---------- + self: array + array instance (minuend array). Should have a numeric data type. + other: Union[int, float, array] + subtrahend array. Must be compatible with ``self`` (see :ref:`broadcasting`). Should have a numeric data type. + + Returns + ------- + out: array + an array containing the element-wise differences. The returned array must have a data type determined by :ref:`type-promotion`. + + Notes + ----- + + .. note:: + Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.subtract`. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + def __truediv__(self: array, other: Union[int, float, array], /) -> array: + r""" + Evaluates ``self_i / other_i`` for each element of an array instance with the respective element of the array ``other``. + + .. note:: + If one or both of ``self`` and ``other`` have integer data types, the result is implementation-dependent, as type promotion between data type "kinds" (e.g., integer versus floating-point) is unspecified. + + Specification-compliant libraries may choose to raise an error or return an array containing the element-wise results. If an array is returned, the array must have a real-valued floating-point data type. + + Parameters + ---------- + self: array + array instance. Should have a numeric data type. + other: Union[int, float, array] + other array. Must be compatible with ``self`` (see :ref:`broadcasting`). Should have a numeric data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array should have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + .. note:: + Element-wise results, including special cases, must equal the results returned by the equivalent element-wise function :func:`~array_api.divide`. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + def __xor__(self: array, other: Union[int, bool, array], /) -> array: + """ + Evaluates ``self_i ^ other_i`` for each element of an array instance with the respective element of the array ``other``. + + Parameters + ---------- + self: array + array instance. Should have an integer or boolean data type. + other: Union[int, bool, array] + other array. Must be compatible with ``self`` (see :ref:`broadcasting`). Should have an integer or boolean data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type determined by :ref:`type-promotion`. + + + .. note:: + Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.bitwise_xor`. + """ + + def to_device( + self: array, device: Device, /, *, stream: Optional[Union[int, Any]] = None + ) -> array: + """ + Copy the array from the device on which it currently resides to the specified ``device``. + + Parameters + ---------- + self: array + array instance. + device: device + a ``device`` object (see :ref:`device-support`). + stream: Optional[Union[int, Any]] + stream object to use during copy. In addition to the types supported in :meth:`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 ``self`` and located on the specified ``device``. + + + Notes + ----- + + - When a provided ``device`` object corresponds to the same device on which an array instance resides, implementations may choose to perform an explicit copy or return ``self``. + - If ``stream`` is provided, 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 array library's documentation. + + .. versionchanged:: 2023.12 + Clarified behavior when a provided ``device`` object corresponds to the device on which an array instance resides. + """ + + +array = _array diff --git a/src/array_api_stubs/_2023_12/constants.py b/src/array_api_stubs/_2023_12/constants.py new file mode 100644 index 000000000..c5735d09f --- /dev/null +++ b/src/array_api_stubs/_2023_12/constants.py @@ -0,0 +1,30 @@ +__all__ = ["e", "inf", "nan", "newaxis", "pi"] + +e = 2.718281828459045 +""" +IEEE 754 floating-point representation of Euler's constant. + +``e = 2.71828182845904523536028747135266249775724709369995...`` +""" + +inf = float("inf") +""" +IEEE 754 floating-point representation of (positive) infinity. +""" + +nan = float("nan") +""" +IEEE 754 floating-point representation of Not a Number (``NaN``). +""" + +newaxis = None +""" +An alias for ``None`` which is useful for indexing arrays. +""" + +pi = 3.141592653589793 +""" +IEEE 754 floating-point representation of the mathematical constant ``π``. + +``pi = 3.1415926535897932384626433...`` +""" diff --git a/src/array_api_stubs/_2023_12/creation_functions.py b/src/array_api_stubs/_2023_12/creation_functions.py new file mode 100644 index 000000000..6de79268e --- /dev/null +++ b/src/array_api_stubs/_2023_12/creation_functions.py @@ -0,0 +1,647 @@ +__all__ = [ + "arange", + "asarray", + "empty", + "empty_like", + "eye", + "from_dlpack", + "full", + "full_like", + "linspace", + "meshgrid", + "ones", + "ones_like", + "tril", + "triu", + "zeros", + "zeros_like", +] + + +from ._types import ( + List, + NestedSequence, + Optional, + SupportsBufferProtocol, + Tuple, + Union, + array, + device, + dtype, +) + + +def arange( + start: Union[int, float], + /, + stop: Optional[Union[int, float]] = None, + step: Union[int, float] = 1, + *, + dtype: Optional[dtype] = None, + device: Optional[device] = None, +) -> array: + """ + Returns evenly spaced values within the half-open interval ``[start, stop)`` as a one-dimensional array. + + Parameters + ---------- + start: Union[int, float] + if ``stop`` is specified, the start of interval (inclusive); otherwise, the end of the interval (exclusive). If ``stop`` is not specified, the default starting value is ``0``. + stop: Optional[Union[int, float]] + the end of the interval. Default: ``None``. + step: Union[int, float] + the distance between two adjacent elements (``out[i+1] - out[i]``). Must not be ``0``; may be negative, this results in an empty array if ``stop >= start``. Default: ``1``. + dtype: Optional[dtype] + output array data type. If ``dtype`` is ``None``, the output array data type must be inferred from ``start``, ``stop`` and ``step``. If those are all integers, the output array dtype must be the default integer dtype; if one or more have type ``float``, then the output array dtype must be the default real-valued floating-point data type. Default: ``None``. + device: Optional[device] + device on which to place the created array. Default: ``None``. + + + .. note:: + This function cannot guarantee that the interval does not include the ``stop`` value in those cases where ``step`` is not an integer and floating-point rounding errors affect the length of the output array. + + Returns + ------- + out: array + a one-dimensional array containing evenly spaced values. The length of the output array must be ``ceil((stop-start)/step)`` if ``stop - start`` and ``step`` have the same sign, and length ``0`` otherwise. + """ + + +def asarray( + obj: Union[ + array, bool, int, float, complex, NestedSequence, SupportsBufferProtocol + ], + /, + *, + dtype: Optional[dtype] = None, + device: Optional[device] = None, + copy: Optional[bool] = None, +) -> array: + r""" + Convert the input to an array. + + Parameters + ---------- + obj: Union[array, bool, int, float, complex, NestedSequence[bool | int | float | complex], SupportsBufferProtocol] + object to be converted to an array. May be a Python scalar, a (possibly nested) sequence of Python scalars, or an object supporting the Python buffer protocol. + + .. admonition:: Tip + :class: important + + An object supporting the buffer protocol can be turned into a memoryview through ``memoryview(obj)``. + + dtype: Optional[dtype] + output array data type. If ``dtype`` is ``None``, the output array data type must be inferred from the data type(s) in ``obj``. If all input values are Python scalars, then, in order of precedence, + + - if all values are of type ``bool``, the output data type must be ``bool``. + - if all values are of type ``int`` or are a mixture of ``bool`` and ``int``, the output data type must be the default integer data type. + - if one or more values are ``complex`` numbers, the output data type must be the default complex floating-point data type. + - if one or more values are ``float``\s, the output data type must be the default real-valued floating-point data type. + + Default: ``None``. + + .. admonition:: Note + :class: note + + If ``dtype`` is not ``None``, then array conversions should obey :ref:`type-promotion` rules. Conversions not specified according to :ref:`type-promotion` rules may or may not be permitted by a conforming array library. To perform an explicit cast, use :func:`array_api.astype`. + + .. note:: + If an input value exceeds the precision of the resolved output array data type, behavior is left unspecified and, thus, implementation-defined. + + device: Optional[device] + device on which to place the created array. If ``device`` is ``None`` and ``obj`` is an array, the output array device must be inferred from ``obj``. Default: ``None``. + copy: Optional[bool] + boolean indicating whether or not to copy the input. If ``True``, the function must always copy. If ``False``, the function must never copy for input which supports the buffer protocol and must raise a ``ValueError`` in case a copy would be necessary. If ``None``, the function must reuse existing memory buffer if possible and copy otherwise. Default: ``None``. + + Returns + ------- + out: array + an array containing the data from ``obj``. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def empty( + shape: Union[int, Tuple[int, ...]], + *, + dtype: Optional[dtype] = None, + device: Optional[device] = None, +) -> array: + """ + Returns an uninitialized array having a specified `shape`. + + Parameters + ---------- + shape: Union[int, Tuple[int, ...]] + output array shape. + dtype: Optional[dtype] + output array data type. If ``dtype`` is ``None``, the output array data type must be the default real-valued floating-point data type. Default: ``None``. + device: Optional[device] + device on which to place the created array. Default: ``None``. + + Returns + ------- + out: array + an array containing uninitialized data. + """ + + +def empty_like( + x: array, /, *, dtype: Optional[dtype] = None, device: Optional[device] = None +) -> array: + """ + Returns an uninitialized array with the same ``shape`` as an input array ``x``. + + Parameters + ---------- + x: array + input array from which to derive the output array shape. + dtype: Optional[dtype] + output array data type. If ``dtype`` is ``None``, the output array data type must be inferred from ``x``. Default: ``None``. + device: Optional[device] + device on which to place the created array. If ``device`` is ``None``, the output array device must be inferred from ``x``. Default: ``None``. + + Returns + ------- + out: array + an array having the same shape as ``x`` and containing uninitialized data. + """ + + +def eye( + n_rows: int, + n_cols: Optional[int] = None, + /, + *, + k: int = 0, + dtype: Optional[dtype] = None, + device: Optional[device] = None, +) -> array: + r""" + Returns a two-dimensional array with ones on the ``k``\th diagonal and zeros elsewhere. + + .. note:: + An output array having a complex floating-point data type must have the value ``1 + 0j`` along the ``k``\th diagonal and ``0 + 0j`` elsewhere. + + Parameters + ---------- + n_rows: int + number of rows in the output array. + n_cols: Optional[int] + number of columns in the output array. If ``None``, the default number of columns in the output array is equal to ``n_rows``. Default: ``None``. + k: int + index of the diagonal. A positive value refers to an upper diagonal, a negative value to a lower diagonal, and ``0`` to the main diagonal. Default: ``0``. + dtype: Optional[dtype] + output array data type. If ``dtype`` is ``None``, the output array data type must be the default real-valued floating-point data type. Default: ``None``. + device: Optional[device] + device on which to place the created array. Default: ``None``. + + Returns + ------- + out: array + an array where all elements are equal to zero, except for the ``k``\th diagonal, whose values are equal to one. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def from_dlpack( + x: object, + /, + *, + device: Optional[device] = None, + copy: Optional[bool] = None, +) -> array: + """ + Returns a new array containing the data from another (array) object with a ``__dlpack__`` method. + + Parameters + ---------- + x: object + input (array) object. + device: Optional[device] + device on which to place the created array. If ``device`` is ``None`` and ``x`` supports DLPack, the output array must be on the same device as ``x``. Default: ``None``. + + The v2023.12 standard only mandates that a compliant library should offer a way for ``from_dlpack`` to return an array + whose underlying memory is accessible to the Python interpreter, when the corresponding ``device`` is provided. If the + array library does not support such cases at all, the function must raise ``BufferError``. If a copy must be made to + enable this support but ``copy`` is set to ``False``, the function must raise ``ValueError``. + + Other device kinds will be considered for standardization in a future version of this API standard. + copy: Optional[bool] + boolean indicating whether or not to copy the input. If ``True``, the function must always copy. If ``False``, the function must never copy, and raise ``BufferError`` in case a copy is deemed necessary (e.g. if a cross-device data movement is requested, and it is not possible without a copy). If ``None``, the function must reuse the existing memory buffer if possible and copy otherwise. Default: ``None``. + + Returns + ------- + out: array + an array containing the data in ``x``. + + .. admonition:: Note + :class: note + + The returned array may be either a copy or a view. See :ref:`data-interchange` for details. + + Raises + ------ + BufferError + The ``__dlpack__`` and ``__dlpack_device__`` methods on the input array + may raise ``BufferError`` when the data cannot be exported as DLPack + (e.g., incompatible dtype, strides, or device). It may also raise other errors + when export fails for other reasons (e.g., not enough memory available + to materialize the data). ``from_dlpack`` must propagate such + exceptions. + AttributeError + If the ``__dlpack__`` and ``__dlpack_device__`` methods are not present + on the input array. This may happen for libraries that are never able + to export their data with DLPack. + ValueError + If data exchange is possible via an explicit copy but ``copy`` is set to ``False``. + + Notes + ----- + See :meth:`array.__dlpack__` for implementation suggestions for `from_dlpack` in + order to handle DLPack versioning correctly. + + A way to move data from two array libraries to the same device (assumed supported by both libraries) in + a library-agnostic fashion is illustrated below: + + .. code:: python + + def func(x, y): + xp_x = x.__array_namespace__() + xp_y = y.__array_namespace__() + + # Other functions than `from_dlpack` only work if both arrays are from the same library. So if + # `y` is from a different one than `x`, let's convert `y` into an array of the same type as `x`: + if not xp_x == xp_y: + y = xp_x.from_dlpack(y, copy=True, device=x.device) + + # From now on use `xp_x.xxxxx` functions, as both arrays are from the library `xp_x` + ... + + + .. versionchanged:: 2023.12 + Required exceptions to address unsupported use cases. + + .. versionchanged:: 2023.12 + Added device and copy support. + """ + + +def full( + shape: Union[int, Tuple[int, ...]], + fill_value: Union[bool, int, float, complex], + *, + dtype: Optional[dtype] = None, + device: Optional[device] = None, +) -> array: + """ + Returns a new array having a specified ``shape`` and filled with ``fill_value``. + + Parameters + ---------- + shape: Union[int, Tuple[int, ...]] + output array shape. + fill_value: Union[bool, int, float, complex] + fill value. + dtype: Optional[dtype] + output array data type. If ``dtype`` is ``None``, the output array data type must be inferred from ``fill_value`` according to the following rules: + + - If the fill value is an ``int``, the output array data type must be the default integer data type. + - If the fill value is a ``float``, the output array data type must be the default real-valued floating-point data type. + - If the fill value is a ``complex`` number, the output array data type must be the default complex floating-point data type. + - If the fill value is a ``bool``, the output array must have a boolean data type. Default: ``None``. + + .. note:: + If the ``fill_value`` exceeds the precision of the resolved default output array data type, behavior is left unspecified and, thus, implementation-defined. + + device: Optional[device] + device on which to place the created array. Default: ``None``. + + Returns + ------- + out: array + an array where every element is equal to ``fill_value``. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def full_like( + x: array, + /, + fill_value: Union[bool, int, float, complex], + *, + dtype: Optional[dtype] = None, + device: Optional[device] = None, +) -> array: + """ + Returns a new array filled with ``fill_value`` and having the same ``shape`` as an input array ``x``. + + Parameters + ---------- + x: array + input array from which to derive the output array shape. + fill_value: Union[bool, int, float, complex] + fill value. + dtype: Optional[dtype] + output array data type. If ``dtype`` is ``None``, the output array data type must be inferred from ``x``. Default: ``None``. + + .. note:: + If the ``fill_value`` exceeds the precision of the resolved output array data type, behavior is unspecified and, thus, implementation-defined. + + .. note:: + If the ``fill_value`` has a data type which is not of the same data type kind (boolean, integer, or floating-point) as the resolved output array data type (see :ref:`type-promotion`), behavior is unspecified and, thus, implementation-defined. + + device: Optional[device] + device on which to place the created array. If ``device`` is ``None``, the output array device must be inferred from ``x``. Default: ``None``. + + Returns + ------- + out: array + an array having the same shape as ``x`` and where every element is equal to ``fill_value``. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def linspace( + start: Union[int, float, complex], + stop: Union[int, float, complex], + /, + num: int, + *, + dtype: Optional[dtype] = None, + device: Optional[device] = None, + endpoint: bool = True, +) -> array: + r""" + Returns evenly spaced numbers over a specified interval. + + Let :math:`N` be the number of generated values (which is either ``num`` or ``num+1`` depending on whether ``endpoint`` is ``True`` or ``False``, respectively). For real-valued output arrays, the spacing between values is given by + + .. math:: + \Delta_{\textrm{real}} = \frac{\textrm{stop} - \textrm{start}}{N - 1} + + For complex output arrays, let ``a = real(start)``, ``b = imag(start)``, ``c = real(stop)``, and ``d = imag(stop)``. The spacing between complex values is given by + + .. math:: + \Delta_{\textrm{complex}} = \frac{c-a}{N-1} + \frac{d-b}{N-1} j + + Parameters + ---------- + start: Union[int, float, complex] + the start of the interval. + stop: Union[int, float, complex] + the end of the interval. If ``endpoint`` is ``False``, the function must generate a sequence of ``num+1`` evenly spaced numbers starting with ``start`` and ending with ``stop`` and exclude the ``stop`` from the returned array such that the returned array consists of evenly spaced numbers over the half-open interval ``[start, stop)``. If ``endpoint`` is ``True``, the output array must consist of evenly spaced numbers over the closed interval ``[start, stop]``. Default: ``True``. + + .. note:: + The step size changes when `endpoint` is `False`. + + num: int + number of samples. Must be a nonnegative integer value. + dtype: Optional[dtype] + output array data type. Should be a floating-point data type. If ``dtype`` is ``None``, + + - if either ``start`` or ``stop`` is a ``complex`` number, the output data type must be the default complex floating-point data type. + - if both ``start`` and ``stop`` are real-valued, the output data type must be the default real-valued floating-point data type. + + Default: ``None``. + + .. admonition:: Note + :class: note + + If ``dtype`` is not ``None``, conversion of ``start`` and ``stop`` should obey :ref:`type-promotion` rules. Conversions not specified according to :ref:`type-promotion` rules may or may not be permitted by a conforming array library. + + device: Optional[device] + device on which to place the created array. Default: ``None``. + endpoint: bool + boolean indicating whether to include ``stop`` in the interval. Default: ``True``. + + Returns + ------- + out: array + a one-dimensional array containing evenly spaced values. + + Notes + ----- + + .. note:: + While this specification recommends that this function only return arrays having a floating-point data type, specification-compliant array libraries may choose to support output arrays having an integer data type (e.g., due to backward compatibility concerns). However, function behavior when generating integer output arrays is unspecified and, thus, is implementation-defined. Accordingly, using this function to generate integer output arrays is not portable. + + .. note:: + As mixed data type promotion is implementation-defined, behavior when ``start`` or ``stop`` exceeds the maximum safe integer of an output floating-point data type is implementation-defined. An implementation may choose to overflow or raise an exception. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def meshgrid(*arrays: array, indexing: str = "xy") -> List[array]: + """ + Returns coordinate matrices from coordinate vectors. + + Parameters + ---------- + arrays: array + an arbitrary number of one-dimensional arrays representing grid coordinates. Each array should have the same numeric data type. + indexing: str + Cartesian ``'xy'`` or matrix ``'ij'`` indexing of output. If provided zero or one one-dimensional vector(s) (i.e., the zero- and one-dimensional cases, respectively), the ``indexing`` keyword has no effect and should be ignored. Default: ``'xy'``. + + Returns + ------- + out: List[array] + list of N arrays, where ``N`` is the number of provided one-dimensional input arrays. Each returned array must have rank ``N``. For ``N`` one-dimensional arrays having lengths ``Ni = len(xi)``, + + - if matrix indexing ``ij``, then each returned array must have the shape ``(N1, N2, N3, ..., Nn)``. + - if Cartesian indexing ``xy``, then each returned array must have shape ``(N2, N1, N3, ..., Nn)``. + + Accordingly, for the two-dimensional case with input one-dimensional arrays of length ``M`` and ``N``, if matrix indexing ``ij``, then each returned array must have shape ``(M, N)``, and, if Cartesian indexing ``xy``, then each returned array must have shape ``(N, M)``. + + Similarly, for the three-dimensional case with input one-dimensional arrays of length ``M``, ``N``, and ``P``, if matrix indexing ``ij``, then each returned array must have shape ``(M, N, P)``, and, if Cartesian indexing ``xy``, then each returned array must have shape ``(N, M, P)``. + + Each returned array should have the same data type as the input arrays. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def ones( + shape: Union[int, Tuple[int, ...]], + *, + dtype: Optional[dtype] = None, + device: Optional[device] = None, +) -> array: + """ + Returns a new array having a specified ``shape`` and filled with ones. + + .. note:: + An output array having a complex floating-point data type must contain complex numbers having a real component equal to one and an imaginary component equal to zero (i.e., ``1 + 0j``). + + Parameters + ---------- + shape: Union[int, Tuple[int, ...]] + output array shape. + dtype: Optional[dtype] + output array data type. If ``dtype`` is ``None``, the output array data type must be the default real-valued floating-point data type. Default: ``None``. + device: Optional[device] + device on which to place the created array. Default: ``None``. + + Returns + ------- + out: array + an array containing ones. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def ones_like( + x: array, /, *, dtype: Optional[dtype] = None, device: Optional[device] = None +) -> array: + """ + Returns a new array filled with ones and having the same ``shape`` as an input array ``x``. + + .. note:: + An output array having a complex floating-point data type must contain complex numbers having a real component equal to one and an imaginary component equal to zero (i.e., ``1 + 0j``). + + Parameters + ---------- + x: array + input array from which to derive the output array shape. + dtype: Optional[dtype] + output array data type. If ``dtype`` is ``None``, the output array data type must be inferred from ``x``. Default: ``None``. + device: Optional[device] + device on which to place the created array. If ``device`` is ``None``, the output array device must be inferred from ``x``. Default: ``None``. + + Returns + ------- + out: array + an array having the same shape as ``x`` and filled with ones. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def tril(x: array, /, *, k: int = 0) -> array: + """ + Returns the lower triangular part of a matrix (or a stack of matrices) ``x``. + + .. note:: + The lower triangular part of the matrix is defined as the elements on and below the specified diagonal ``k``. + + Parameters + ---------- + x: array + input array having shape ``(..., M, N)`` and whose innermost two dimensions form ``MxN`` matrices. + k: int + diagonal above which to zero elements. If ``k = 0``, the diagonal is the main diagonal. If ``k < 0``, the diagonal is below the main diagonal. If ``k > 0``, the diagonal is above the main diagonal. Default: ``0``. + + .. note:: + The main diagonal is defined as the set of indices ``{(i, i)}`` for ``i`` on the interval ``[0, min(M, N) - 1]``. + + Returns + ------- + out: array + an array containing the lower triangular part(s). The returned array must have the same shape and data type as ``x``. All elements above the specified diagonal ``k`` must be zeroed. The returned array should be allocated on the same device as ``x``. + """ + + +def triu(x: array, /, *, k: int = 0) -> array: + """ + Returns the upper triangular part of a matrix (or a stack of matrices) ``x``. + + .. note:: + The upper triangular part of the matrix is defined as the elements on and above the specified diagonal ``k``. + + Parameters + ---------- + x: array + input array having shape ``(..., M, N)`` and whose innermost two dimensions form ``MxN`` matrices. + k: int + diagonal below which to zero elements. If ``k = 0``, the diagonal is the main diagonal. If ``k < 0``, the diagonal is below the main diagonal. If ``k > 0``, the diagonal is above the main diagonal. Default: ``0``. + + .. note:: + The main diagonal is defined as the set of indices ``{(i, i)}`` for ``i`` on the interval ``[0, min(M, N) - 1]``. + + Returns + ------- + out: array + an array containing the upper triangular part(s). The returned array must have the same shape and data type as ``x``. All elements below the specified diagonal ``k`` must be zeroed. The returned array should be allocated on the same device as ``x``. + """ + + +def zeros( + shape: Union[int, Tuple[int, ...]], + *, + dtype: Optional[dtype] = None, + device: Optional[device] = None, +) -> array: + """ + Returns a new array having a specified ``shape`` and filled with zeros. + + Parameters + ---------- + shape: Union[int, Tuple[int, ...]] + output array shape. + dtype: Optional[dtype] + output array data type. If ``dtype`` is ``None``, the output array data type must be the default real-valued floating-point data type. Default: ``None``. + device: Optional[device] + device on which to place the created array. Default: ``None``. + + Returns + ------- + out: array + an array containing zeros. + """ + + +def zeros_like( + x: array, /, *, dtype: Optional[dtype] = None, device: Optional[device] = None +) -> array: + """ + Returns a new array filled with zeros and having the same ``shape`` as an input array ``x``. + + Parameters + ---------- + x: array + input array from which to derive the output array shape. + dtype: Optional[dtype] + output array data type. If ``dtype`` is ``None``, the output array data type must be inferred from ``x``. Default: ``None``. + device: Optional[device] + device on which to place the created array. If ``device`` is ``None``, the output array device must be inferred from ``x``. Default: ``None``. + + Returns + ------- + out: array + an array having the same shape as ``x`` and filled with zeros. + """ diff --git a/src/array_api_stubs/_2023_12/data_type_functions.py b/src/array_api_stubs/_2023_12/data_type_functions.py new file mode 100644 index 000000000..e12d349c6 --- /dev/null +++ b/src/array_api_stubs/_2023_12/data_type_functions.py @@ -0,0 +1,228 @@ +__all__ = ["astype", "can_cast", "finfo", "iinfo", "isdtype", "result_type"] + +from ._types import ( + Union, + Tuple, + array, + dtype, + finfo_object, + iinfo_object, + device, + Optional, +) + + +def astype( + x: array, dtype: dtype, /, *, copy: bool = True, device: Optional[device] = None +) -> array: + """ + Copies an array to a specified data type irrespective of :ref:`type-promotion` rules. + + .. note:: + Casting floating-point ``NaN`` and ``infinity`` values to integral data types is not specified and is implementation-dependent. + + .. note:: + Casting a complex floating-point array to a real-valued data type should not be permitted. + + Historically, when casting a complex floating-point array to a real-valued data type, libraries such as NumPy have discarded imaginary components such that, for a complex floating-point array ``x``, ``astype(x)`` equals ``astype(real(x))``). This behavior is considered problematic as the choice to discard the imaginary component is arbitrary and introduces more than one way to achieve the same outcome (i.e., for a complex floating-point array ``x``, ``astype(x)`` and ``astype(real(x))`` versus only ``astype(imag(x))``). Instead, in order to avoid ambiguity and to promote clarity, this specification requires that array API consumers explicitly express which component should be cast to a specified real-valued data type. + + .. note:: + When casting a boolean input array to a real-valued data type, a value of ``True`` must cast to a real-valued number equal to ``1``, and a value of ``False`` must cast to a real-valued number equal to ``0``. + + When casting a boolean input array to a complex floating-point data type, a value of ``True`` must cast to a complex number equal to ``1 + 0j``, and a value of ``False`` must cast to a complex number equal to ``0 + 0j``. + + .. note:: + When casting a real-valued input array to ``bool``, a value of ``0`` must cast to ``False``, and a non-zero value must cast to ``True``. + + When casting a complex floating-point array to ``bool``, a value of ``0 + 0j`` must cast to ``False``, and all other values must cast to ``True``. + + Parameters + ---------- + x: array + array to cast. + dtype: dtype + desired data type. + copy: bool + specifies whether to copy an array when the specified ``dtype`` matches the data type of the input array ``x``. If ``True``, a newly allocated array must always be returned. If ``False`` and the specified ``dtype`` matches the data type of the input array, the input array must be returned; otherwise, a newly allocated array must be returned. Default: ``True``. + device: Optional[device] + device on which to place the returned array. If ``device`` is ``None``, the output array device must be inferred from ``x``. Default: ``None``. + + Returns + ------- + out: array + an array having the specified data type. The returned array must have the same shape as ``x``. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + + .. versionchanged:: 2023.12 + Added device keyword argument support. + """ + + +def can_cast(from_: Union[dtype, array], to: dtype, /) -> bool: + """ + Determines if one data type can be cast to another data type according :ref:`type-promotion` rules. + + Parameters + ---------- + from_: Union[dtype, array] + input data type or array from which to cast. + to: dtype + desired data type. + + Returns + ------- + out: bool + ``True`` if the cast can occur according to :ref:`type-promotion` rules; otherwise, ``False``. + """ + + +def finfo(type: Union[dtype, array], /) -> finfo_object: + """ + Machine limits for floating-point data types. + + Parameters + ---------- + type: Union[dtype, array] + the kind of floating-point data-type about which to get information. If complex, the information is about its component data type. + + .. note:: + Complex floating-point data types are specified to always use the same precision for both its real and imaginary components, so the information should be true for either component. + + Returns + ------- + out: finfo object + an object having the following attributes: + + - **bits**: *int* + + number of bits occupied by the real-valued floating-point data type. + + - **eps**: *float* + + difference between 1.0 and the next smallest representable real-valued floating-point number larger than 1.0 according to the IEEE-754 standard. + + - **max**: *float* + + largest representable real-valued number. + + - **min**: *float* + + smallest representable real-valued number. + + - **smallest_normal**: *float* + + smallest positive real-valued floating-point number with full precision. + + - **dtype**: dtype + + real-valued floating-point data type. + + .. versionadded:: 2022.12 + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def iinfo(type: Union[dtype, array], /) -> iinfo_object: + """ + Machine limits for integer data types. + + Parameters + ---------- + type: Union[dtype, array] + the kind of integer data-type about which to get information. + + Returns + ------- + out: iinfo object + an object having the following attributes: + + - **bits**: *int* + + number of bits occupied by the type. + + - **max**: *int* + + largest representable number. + + - **min**: *int* + + smallest representable number. + + - **dtype**: dtype + + integer data type. + + .. versionadded:: 2022.12 + """ + + +def isdtype( + dtype: dtype, kind: Union[dtype, str, Tuple[Union[dtype, str], ...]] +) -> bool: + """ + Returns a boolean indicating whether a provided dtype is of a specified data type "kind". + + Parameters + ---------- + dtype: dtype + the input dtype. + kind: Union[str, dtype, Tuple[Union[str, dtype], ...]] + data type kind. + + - If ``kind`` is a dtype, the function must return a boolean indicating whether the input ``dtype`` is equal to the dtype specified by ``kind``. + - If ``kind`` is a string, the function must return a boolean indicating whether the input ``dtype`` is of a specified data type kind. The following dtype kinds must be supported: + + - ``'bool'``: boolean data types (e.g., ``bool``). + - ``'signed integer'``: signed integer data types (e.g., ``int8``, ``int16``, ``int32``, ``int64``). + - ``'unsigned integer'``: unsigned integer data types (e.g., ``uint8``, ``uint16``, ``uint32``, ``uint64``). + - ``'integral'``: integer data types. Shorthand for ``('signed integer', 'unsigned integer')``. + - ``'real floating'``: real-valued floating-point data types (e.g., ``float32``, ``float64``). + - ``'complex floating'``: complex floating-point data types (e.g., ``complex64``, ``complex128``). + - ``'numeric'``: numeric data types. Shorthand for ``('integral', 'real floating', 'complex floating')``. + + - If ``kind`` is a tuple, the tuple specifies a union of dtypes and/or kinds, and the function must return a boolean indicating whether the input ``dtype`` is either equal to a specified dtype or belongs to at least one specified data type kind. + + .. note:: + A conforming implementation of the array API standard is **not** limited to only including the dtypes described in this specification in the required data type kinds. For example, implementations supporting ``float16`` and ``bfloat16`` can include ``float16`` and ``bfloat16`` in the ``real floating`` data type kind. Similarly, implementations supporting ``int128`` can include ``int128`` in the ``signed integer`` data type kind. + + In short, conforming implementations may extend data type kinds; however, data type kinds must remain consistent (e.g., only integer dtypes may belong to integer data type kinds and only floating-point dtypes may belong to floating-point data type kinds), and extensions must be clearly documented as such in library documentation. + + Returns + ------- + out: bool + boolean indicating whether a provided dtype is of a specified data type kind. + + Notes + ----- + + .. versionadded:: 2022.12 + """ + + +def result_type(*arrays_and_dtypes: Union[array, dtype]) -> dtype: + """ + Returns the dtype that results from applying the type promotion rules (see :ref:`type-promotion`) to the arguments. + + .. note:: + If provided mixed dtypes (e.g., integer and floating-point), the returned dtype will be implementation-specific. + + Parameters + ---------- + arrays_and_dtypes: Union[array, dtype] + an arbitrary number of input arrays and/or dtypes. + + Returns + ------- + out: dtype + the dtype resulting from an operation involving the input arrays and dtypes. + """ diff --git a/src/array_api_stubs/_2023_12/data_types.py b/src/array_api_stubs/_2023_12/data_types.py new file mode 100644 index 000000000..d15f4a9f7 --- /dev/null +++ b/src/array_api_stubs/_2023_12/data_types.py @@ -0,0 +1,22 @@ +__all__ = ["__eq__"] + + +from ._types import dtype + + +def __eq__(self: dtype, other: dtype, /) -> bool: + """ + Computes the truth value of ``self == other`` in order to test for data type object equality. + + Parameters + ---------- + self: dtype + data type instance. May be any supported data type. + other: dtype + other data type instance. May be any supported data type. + + Returns + ------- + out: bool + a boolean indicating whether the data type objects are equal. + """ diff --git a/src/array_api_stubs/_2023_12/elementwise_functions.py b/src/array_api_stubs/_2023_12/elementwise_functions.py new file mode 100644 index 000000000..4462329d6 --- /dev/null +++ b/src/array_api_stubs/_2023_12/elementwise_functions.py @@ -0,0 +1,2774 @@ +__all__ = [ + "abs", + "acos", + "acosh", + "add", + "asin", + "asinh", + "atan", + "atan2", + "atanh", + "bitwise_and", + "bitwise_left_shift", + "bitwise_invert", + "bitwise_or", + "bitwise_right_shift", + "bitwise_xor", + "ceil", + "clip", + "conj", + "copysign", + "cos", + "cosh", + "divide", + "equal", + "exp", + "expm1", + "floor", + "floor_divide", + "greater", + "greater_equal", + "hypot", + "imag", + "isfinite", + "isinf", + "isnan", + "less", + "less_equal", + "log", + "log1p", + "log2", + "log10", + "logaddexp", + "logical_and", + "logical_not", + "logical_or", + "logical_xor", + "maximum", + "minimum", + "multiply", + "negative", + "not_equal", + "positive", + "pow", + "real", + "remainder", + "round", + "sign", + "signbit", + "sin", + "sinh", + "square", + "sqrt", + "subtract", + "tan", + "tanh", + "trunc", +] + + +from ._types import Optional, Union, array + + +def abs(x: array, /) -> array: + r""" + Calculates the absolute value for each element ``x_i`` of the input array ``x``. + + For real-valued input arrays, the element-wise result has the same magnitude as the respective element in ``x`` but has positive sign. + + .. note:: + For signed integer data types, the absolute value of the minimum representable integer is implementation-dependent. + + .. note:: + For complex floating-point operands, the complex absolute value is known as the norm, modulus, or magnitude and, for a complex number :math:`z = a + bj` is computed as + + .. math:: + \operatorname{abs}(z) = \sqrt{a^2 + b^2} + + .. note:: + For complex floating-point operands, conforming implementations should take care to avoid undue overflow or underflow during intermediate stages of computation. + + .. + TODO: once ``hypot`` is added to the specification, remove the special cases for complex floating-point operands and the note concerning guarding against undue overflow/underflow, and state that special cases must be handled as if implemented as ``hypot(real(x), imag(x))``. + + Parameters + ---------- + x: array + input array. Should have a numeric data type. + + Returns + ------- + out: array + an array containing the absolute value of each element in ``x``. If ``x`` has a real-valued data type, the returned array must have the same data type as ``x``. If ``x`` has a complex floating-point data type, the returned array must have a real-valued floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``complex128``, then the returned array must have a ``float64`` data type). + + Notes + ----- + + **Special Cases** + + For real-valued floating-point operands, + + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If ``x_i`` is ``-0``, the result is ``+0``. + - If ``x_i`` is ``-infinity``, the result is ``+infinity``. + + For complex floating-point operands, let ``a = real(x_i)``, ``b = imag(x_i)``, and + + - If ``a`` is either ``+infinity`` or ``-infinity`` and ``b`` is any value (including ``NaN``), the result is ``+infinity``. + - If ``a`` is any value (including ``NaN``) and ``b`` is either ``+infinity`` or ``-infinity``, the result is ``+infinity``. + - If ``a`` is either ``+0`` or ``-0``, the result is equal to ``abs(b)``. + - If ``b`` is either ``+0`` or ``-0``, the result is equal to ``abs(a)``. + - If ``a`` is ``NaN`` and ``b`` is a finite number, the result is ``NaN``. + - If ``a`` is a finite number and ``b`` is ``NaN``, the result is ``NaN``. + - If ``a`` is ``NaN`` and ``b`` is ``NaN``, the result is ``NaN``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def acos(x: array, /) -> array: + r""" + Calculates an implementation-dependent approximation of the principal value of the inverse cosine for each element ``x_i`` of the input array ``x``. + + Each element-wise result is expressed in radians. + + .. note:: + The principal value of the arc cosine of a complex number :math:`z` is + + .. math:: + \operatorname{acos}(z) = \frac{1}{2}\pi + j\ \ln(zj + \sqrt{1-z^2}) + + For any :math:`z`, + + .. math:: + \operatorname{acos}(z) = \pi - \operatorname{acos}(-z) + + .. note:: + For complex floating-point operands, ``acos(conj(x))`` must equal ``conj(acos(x))``. + + .. note:: + The inverse cosine (or arc cosine) is a multi-valued function and requires a branch cut on the complex plane. By convention, a branch cut is placed at the line segments :math:`(-\infty, -1)` and :math:`(1, \infty)` of the real axis. + + Accordingly, for complex arguments, the function returns the inverse cosine in the range of a strip unbounded along the imaginary axis and in the interval :math:`[0, \pi]` along the real axis. + + *Note: branch cuts follow C99 and have provisional status* (see :ref:`branch-cuts`). + + Parameters + ---------- + x: array + input array. Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the inverse cosine of each element in ``x``. The returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If ``x_i`` is greater than ``1``, the result is ``NaN``. + - If ``x_i`` is less than ``-1``, the result is ``NaN``. + - If ``x_i`` is ``1``, the result is ``+0``. + + For complex floating-point operands, let ``a = real(x_i)``, ``b = imag(x_i)``, and + + - If ``a`` is either ``+0`` or ``-0`` and ``b`` is ``+0``, the result is ``π/2 - 0j``. + - If ``a`` is either ``+0`` or ``-0`` and ``b`` is ``NaN``, the result is ``π/2 + NaN j``. + - If ``a`` is a finite number and ``b`` is ``+infinity``, the result is ``π/2 - infinity j``. + - If ``a`` is a nonzero finite number and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + - If ``a`` is ``-infinity`` and ``b`` is a positive (i.e., greater than ``0``) finite number, the result is ``π - infinity j``. + - If ``a`` is ``+infinity`` and ``b`` is a positive (i.e., greater than ``0``) finite number, the result is ``+0 - infinity j``. + - If ``a`` is ``-infinity`` and ``b`` is ``+infinity``, the result is ``3π/4 - infinity j``. + - If ``a`` is ``+infinity`` and ``b`` is ``+infinity``, the result is ``π/4 - infinity j``. + - If ``a`` is either ``+infinity`` or ``-infinity`` and ``b`` is ``NaN``, the result is ``NaN ± infinity j`` (sign of the imaginary component is unspecified). + - If ``a`` is ``NaN`` and ``b`` is a finite number, the result is ``NaN + NaN j``. + - If ``a`` is ``NaN`` and ``b`` is ``+infinity``, the result is ``NaN - infinity j``. + - If ``a`` is ``NaN`` and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def acosh(x: array, /) -> array: + r""" + Calculates an implementation-dependent approximation to the inverse hyperbolic cosine for each element ``x_i`` of the input array ``x``. + + .. note:: + The principal value of the inverse hyperbolic cosine of a complex number :math:`z` is + + .. math:: + \operatorname{acosh}(z) = \ln(z + \sqrt{z+1}\sqrt{z-1}) + + For any :math:`z`, + + .. math:: + \operatorname{acosh}(z) = \frac{\sqrt{z-1}}{\sqrt{1-z}}\operatorname{acos}(z) + + or simply + + .. math:: + \operatorname{acosh}(z) = j\ \operatorname{acos}(z) + + in the upper half of the complex plane. + + .. note:: + For complex floating-point operands, ``acosh(conj(x))`` must equal ``conj(acosh(x))``. + + .. note:: + The inverse hyperbolic cosine is a multi-valued function and requires a branch cut on the complex plane. By convention, a branch cut is placed at the line segment :math:`(-\infty, 1)` of the real axis. + + Accordingly, for complex arguments, the function returns the inverse hyperbolic cosine in the interval :math:`[0, \infty)` along the real axis and in the interval :math:`[-\pi j, +\pi j]` along the imaginary axis. + + *Note: branch cuts follow C99 and have provisional status* (see :ref:`branch-cuts`). + + Parameters + ---------- + x: array + input array whose elements each represent the area of a hyperbolic sector. Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the inverse hyperbolic cosine of each element in ``x``. The returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If ``x_i`` is less than ``1``, the result is ``NaN``. + - If ``x_i`` is ``1``, the result is ``+0``. + - If ``x_i`` is ``+infinity``, the result is ``+infinity``. + + For complex floating-point operands, let ``a = real(x_i)``, ``b = imag(x_i)``, and + + - If ``a`` is either ``+0`` or ``-0`` and ``b`` is ``+0``, the result is ``+0 + πj/2``. + - If ``a`` is a finite number and ``b`` is ``+infinity``, the result is ``+infinity + πj/2``. + - If ``a`` is a nonzero finite number and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + - If ``a`` is ``+0`` and ``b`` is ``NaN``, the result is ``NaN ± πj/2`` (sign of imaginary component is unspecified). + - If ``a`` is ``-infinity`` and ``b`` is a positive (i.e., greater than ``0``) finite number, the result is ``+infinity + πj``. + - If ``a`` is ``+infinity`` and ``b`` is a positive (i.e., greater than ``0``) finite number, the result is ``+infinity + 0j``. + - If ``a`` is ``-infinity`` and ``b`` is ``+infinity``, the result is ``+infinity + 3πj/4``. + - If ``a`` is ``+infinity`` and ``b`` is ``+infinity``, the result is ``+infinity + πj/4``. + - If ``a`` is either ``+infinity`` or ``-infinity`` and ``b`` is ``NaN``, the result is ``+infinity + NaN j``. + - If ``a`` is ``NaN`` and ``b`` is a finite number, the result is ``NaN + NaN j``. + - If ``a`` is ``NaN`` and ``b`` is ``+infinity``, the result is ``+infinity + NaN j``. + - If ``a`` is ``NaN`` and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def add(x1: array, x2: array, /) -> array: + """ + Calculates the sum for each element ``x1_i`` of the input array ``x1`` with the respective element ``x2_i`` of the input array ``x2``. + + Parameters + ---------- + x1: array + first input array. Should have a numeric data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a numeric data type. + + Returns + ------- + out: array + an array containing the element-wise sums. The returned array must have a data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If either ``x1_i`` or ``x2_i`` is ``NaN``, the result is ``NaN``. + - If ``x1_i`` is ``+infinity`` and ``x2_i`` is ``-infinity``, the result is ``NaN``. + - If ``x1_i`` is ``-infinity`` and ``x2_i`` is ``+infinity``, the result is ``NaN``. + - If ``x1_i`` is ``+infinity`` and ``x2_i`` is ``+infinity``, the result is ``+infinity``. + - If ``x1_i`` is ``-infinity`` and ``x2_i`` is ``-infinity``, the result is ``-infinity``. + - If ``x1_i`` is ``+infinity`` and ``x2_i`` is a finite number, the result is ``+infinity``. + - If ``x1_i`` is ``-infinity`` and ``x2_i`` is a finite number, the result is ``-infinity``. + - If ``x1_i`` is a finite number and ``x2_i`` is ``+infinity``, the result is ``+infinity``. + - If ``x1_i`` is a finite number and ``x2_i`` is ``-infinity``, the result is ``-infinity``. + - If ``x1_i`` is ``-0`` and ``x2_i`` is ``-0``, the result is ``-0``. + - If ``x1_i`` is ``-0`` and ``x2_i`` is ``+0``, the result is ``+0``. + - If ``x1_i`` is ``+0`` and ``x2_i`` is ``-0``, the result is ``+0``. + - If ``x1_i`` is ``+0`` and ``x2_i`` is ``+0``, the result is ``+0``. + - If ``x1_i`` is either ``+0`` or ``-0`` and ``x2_i`` is a nonzero finite number, the result is ``x2_i``. + - If ``x1_i`` is a nonzero finite number and ``x2_i`` is either ``+0`` or ``-0``, the result is ``x1_i``. + - If ``x1_i`` is a nonzero finite number and ``x2_i`` is ``-x1_i``, the result is ``+0``. + - In the remaining cases, when neither ``infinity``, ``+0``, ``-0``, nor a ``NaN`` is involved, and the operands have the same mathematical sign or have different magnitudes, the sum must be computed and rounded to the nearest representable value according to IEEE 754-2019 and a supported round mode. If the magnitude is too large to represent, the operation overflows and the result is an `infinity` of appropriate mathematical sign. + + .. note:: + Floating-point addition is a commutative operation, but not always associative. + + For complex floating-point operands, addition is defined according to the following table. For real components ``a`` and ``c`` and imaginary components ``b`` and ``d``, + + +------------+------------+------------+----------------+ + | | c | dj | c + dj | + +============+============+============+================+ + | **a** | a + c | a + dj | (a+c) + dj | + +------------+------------+------------+----------------+ + | **bj** | c + bj | (b+d)j | c + (b+d)j | + +------------+------------+------------+----------------+ + | **a + bj** | (a+c) + bj | a + (b+d)j | (a+c) + (b+d)j | + +------------+------------+------------+----------------+ + + For complex floating-point operands, real-valued floating-point special cases must independently apply to the real and imaginary component operations involving real numbers as described in the above table. For example, let ``a = real(x1_i)``, ``b = imag(x1_i)``, ``c = real(x2_i)``, ``d = imag(x2_i)``, and + + - If ``a`` is ``-0`` and ``c`` is ``-0``, the real component of the result is ``-0``. + - Similarly, if ``b`` is ``+0`` and ``d`` is ``-0``, the imaginary component of the result is ``+0``. + + Hence, if ``z1 = a + bj = -0 + 0j`` and ``z2 = c + dj = -0 - 0j``, the result of ``z1 + z2`` is ``-0 + 0j``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def asin(x: array, /) -> array: + r""" + Calculates an implementation-dependent approximation of the principal value of the inverse sine for each element ``x_i`` of the input array ``x``. + + Each element-wise result is expressed in radians. + + .. note:: + The principal value of the arc sine of a complex number :math:`z` is + + .. math:: + \operatorname{asin}(z) = -j\ \ln(zj + \sqrt{1-z^2}) + + For any :math:`z`, + + .. math:: + \operatorname{asin}(z) = \operatorname{acos}(-z) - \frac{\pi}{2} + + .. note:: + For complex floating-point operands, ``asin(conj(x))`` must equal ``conj(asin(x))``. + + .. note:: + The inverse sine (or arc sine) is a multi-valued function and requires a branch cut on the complex plane. By convention, a branch cut is placed at the line segments :math:`(-\infty, -1)` and :math:`(1, \infty)` of the real axis. + + Accordingly, for complex arguments, the function returns the inverse sine in the range of a strip unbounded along the imaginary axis and in the interval :math:`[-\pi/2, +\pi/2]` along the real axis. + + *Note: branch cuts follow C99 and have provisional status* (see :ref:`branch-cuts`). + + Parameters + ---------- + x: array + input array. Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the inverse sine of each element in ``x``. The returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If ``x_i`` is greater than ``1``, the result is ``NaN``. + - If ``x_i`` is less than ``-1``, the result is ``NaN``. + - If ``x_i`` is ``+0``, the result is ``+0``. + - If ``x_i`` is ``-0``, the result is ``-0``. + + For complex floating-point operands, special cases must be handled as if the operation is implemented as ``-1j * asinh(x*1j)``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def asinh(x: array, /) -> array: + r""" + Calculates an implementation-dependent approximation to the inverse hyperbolic sine for each element ``x_i`` in the input array ``x``. + + .. note:: + The principal value of the inverse hyperbolic sine of a complex number :math:`z` is + + .. math:: + \operatorname{asinh}(z) = \ln(z + \sqrt{1+z^2}) + + For any :math:`z`, + + .. math:: + \operatorname{asinh}(z) = \frac{\operatorname{asin}(zj)}{j} + + .. note:: + For complex floating-point operands, ``asinh(conj(x))`` must equal ``conj(asinh(x))`` and ``asinh(-z)`` must equal ``-asinh(z)``. + + .. note:: + The inverse hyperbolic sine is a multi-valued function and requires a branch cut on the complex plane. By convention, a branch cut is placed at the line segments :math:`(-\infty j, -j)` and :math:`(j, \infty j)` of the imaginary axis. + + Accordingly, for complex arguments, the function returns the inverse hyperbolic sine in the range of a strip unbounded along the real axis and in the interval :math:`[-\pi j/2, +\pi j/2]` along the imaginary axis. + + *Note: branch cuts follow C99 and have provisional status* (see :ref:`branch-cuts`). + + Parameters + ---------- + x: array + input array whose elements each represent the area of a hyperbolic sector. Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the inverse hyperbolic sine of each element in ``x``. The returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If ``x_i`` is ``+0``, the result is ``+0``. + - If ``x_i`` is ``-0``, the result is ``-0``. + - If ``x_i`` is ``+infinity``, the result is ``+infinity``. + - If ``x_i`` is ``-infinity``, the result is ``-infinity``. + + For complex floating-point operands, let ``a = real(x_i)``, ``b = imag(x_i)``, and + + - If ``a`` is ``+0`` and ``b`` is ``+0``, the result is ``+0 + 0j``. + - If ``a`` is a positive (i.e., greater than ``0``) finite number and ``b`` is ``+infinity``, the result is ``+infinity + πj/2``. + - If ``a`` is a finite number and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + - If ``a`` is ``+infinity`` and ``b`` is a positive (i.e., greater than ``0``) finite number, the result is ``+infinity + 0j``. + - If ``a`` is ``+infinity`` and ``b`` is ``+infinity``, the result is ``+infinity + πj/4``. + - If ``a`` is ``NaN`` and ``b`` is ``+0``, the result is ``NaN + 0j``. + - If ``a`` is ``NaN`` and ``b`` is a nonzero finite number, the result is ``NaN + NaN j``. + - If ``a`` is ``NaN`` and ``b`` is ``+infinity``, the result is ``±infinity + NaN j`` (sign of the real component is unspecified). + - If ``a`` is ``NaN`` and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def atan(x: array, /) -> array: + r""" + Calculates an implementation-dependent approximation of the principal value of the inverse tangent for each element ``x_i`` of the input array ``x``. + + Each element-wise result is expressed in radians. + + .. note:: + The principal value of the inverse tangent of a complex number :math:`z` is + + .. math:: + \operatorname{atan}(z) = -\frac{\ln(1 - zj) - \ln(1 + zj)}{2}j + + .. note:: + For complex floating-point operands, ``atan(conj(x))`` must equal ``conj(atan(x))``. + + .. note:: + The inverse tangent (or arc tangent) is a multi-valued function and requires a branch on the complex plane. By convention, a branch cut is placed at the line segments :math:`(-\infty j, -j)` and :math:`(+j, \infty j)` of the imaginary axis. + + Accordingly, for complex arguments, the function returns the inverse tangent in the range of a strip unbounded along the imaginary axis and in the interval :math:`[-\pi/2, +\pi/2]` along the real axis. + + *Note: branch cuts follow C99 and have provisional status* (see :ref:`branch-cuts`). + + Parameters + ---------- + x: array + input array. Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the inverse tangent of each element in ``x``. The returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If ``x_i`` is ``+0``, the result is ``+0``. + - If ``x_i`` is ``-0``, the result is ``-0``. + - If ``x_i`` is ``+infinity``, the result is an implementation-dependent approximation to ``+π/2``. + - If ``x_i`` is ``-infinity``, the result is an implementation-dependent approximation to ``-π/2``. + + For complex floating-point operands, special cases must be handled as if the operation is implemented as ``-1j * atanh(x*1j)``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def atan2(x1: array, x2: array, /) -> array: + """ + Calculates an implementation-dependent approximation of the inverse tangent of the quotient ``x1/x2``, having domain ``[-infinity, +infinity] x [-infinity, +infinity]`` (where the ``x`` notation denotes the set of ordered pairs of elements ``(x1_i, x2_i)``) and codomain ``[-π, +π]``, for each pair of elements ``(x1_i, x2_i)`` of the input arrays ``x1`` and ``x2``, respectively. Each element-wise result is expressed in radians. + + The mathematical signs of ``x1_i`` and ``x2_i`` determine the quadrant of each element-wise result. The quadrant (i.e., branch) is chosen such that each element-wise result is the signed angle in radians between the ray ending at the origin and passing through the point ``(1,0)`` and the ray ending at the origin and passing through the point ``(x2_i, x1_i)``. + + .. note:: + Note the role reversal: the "y-coordinate" is the first function parameter; the "x-coordinate" is the second function parameter. The parameter order is intentional and traditional for the two-argument inverse tangent function where the y-coordinate argument is first and the x-coordinate argument is second. + + By IEEE 754 convention, the inverse tangent of the quotient ``x1/x2`` is defined for ``x2_i`` equal to positive or negative zero and for either or both of ``x1_i`` and ``x2_i`` equal to positive or negative ``infinity``. + + Parameters + ---------- + x1: array + input array corresponding to the y-coordinates. Should have a real-valued floating-point data type. + x2: array + input array corresponding to the x-coordinates. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a real-valued floating-point data type. + + Returns + ------- + out: array + an array containing the inverse tangent of the quotient ``x1/x2``. The returned array must have a real-valued floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For floating-point operands, + + - If either ``x1_i`` or ``x2_i`` is ``NaN``, the result is ``NaN``. + - If ``x1_i`` is greater than ``0`` and ``x2_i`` is ``+0``, the result is an implementation-dependent approximation to ``+π/2``. + - If ``x1_i`` is greater than ``0`` and ``x2_i`` is ``-0``, the result is an implementation-dependent approximation to ``+π/2``. + - If ``x1_i`` is ``+0`` and ``x2_i`` is greater than ``0``, the result is ``+0``. + - If ``x1_i`` is ``+0`` and ``x2_i`` is ``+0``, the result is ``+0``. + - If ``x1_i`` is ``+0`` and ``x2_i`` is ``-0``, the result is an implementation-dependent approximation to ``+π``. + - If ``x1_i`` is ``+0`` and ``x2_i`` is less than ``0``, the result is an implementation-dependent approximation to ``+π``. + - If ``x1_i`` is ``-0`` and ``x2_i`` is greater than ``0``, the result is ``-0``. + - If ``x1_i`` is ``-0`` and ``x2_i`` is ``+0``, the result is ``-0``. + - If ``x1_i`` is ``-0`` and ``x2_i`` is ``-0``, the result is an implementation-dependent approximation to ``-π``. + - If ``x1_i`` is ``-0`` and ``x2_i`` is less than ``0``, the result is an implementation-dependent approximation to ``-π``. + - If ``x1_i`` is less than ``0`` and ``x2_i`` is ``+0``, the result is an implementation-dependent approximation to ``-π/2``. + - If ``x1_i`` is less than ``0`` and ``x2_i`` is ``-0``, the result is an implementation-dependent approximation to ``-π/2``. + - If ``x1_i`` is greater than ``0``, ``x1_i`` is a finite number, and ``x2_i`` is ``+infinity``, the result is ``+0``. + - If ``x1_i`` is greater than ``0``, ``x1_i`` is a finite number, and ``x2_i`` is ``-infinity``, the result is an implementation-dependent approximation to ``+π``. + - If ``x1_i`` is less than ``0``, ``x1_i`` is a finite number, and ``x2_i`` is ``+infinity``, the result is ``-0``. + - If ``x1_i`` is less than ``0``, ``x1_i`` is a finite number, and ``x2_i`` is ``-infinity``, the result is an implementation-dependent approximation to ``-π``. + - If ``x1_i`` is ``+infinity`` and ``x2_i`` is a finite number, the result is an implementation-dependent approximation to ``+π/2``. + - If ``x1_i`` is ``-infinity`` and ``x2_i`` is a finite number, the result is an implementation-dependent approximation to ``-π/2``. + - If ``x1_i`` is ``+infinity`` and ``x2_i`` is ``+infinity``, the result is an implementation-dependent approximation to ``+π/4``. + - If ``x1_i`` is ``+infinity`` and ``x2_i`` is ``-infinity``, the result is an implementation-dependent approximation to ``+3π/4``. + - If ``x1_i`` is ``-infinity`` and ``x2_i`` is ``+infinity``, the result is an implementation-dependent approximation to ``-π/4``. + - If ``x1_i`` is ``-infinity`` and ``x2_i`` is ``-infinity``, the result is an implementation-dependent approximation to ``-3π/4``. + """ + + +def atanh(x: array, /) -> array: + r""" + Calculates an implementation-dependent approximation to the inverse hyperbolic tangent for each element ``x_i`` of the input array ``x``. + + .. note:: + The principal value of the inverse hyperbolic tangent of a complex number :math:`z` is + + .. math:: + \operatorname{atanh}(z) = \frac{\ln(1+z)-\ln(z-1)}{2} + + For any :math:`z`, + + .. math:: + \operatorname{atanh}(z) = \frac{\operatorname{atan}(zj)}{j} + + .. note:: + For complex floating-point operands, ``atanh(conj(x))`` must equal ``conj(atanh(x))`` and ``atanh(-x)`` must equal ``-atanh(x)``. + + .. note:: + The inverse hyperbolic tangent is a multi-valued function and requires a branch cut on the complex plane. By convention, a branch cut is placed at the line segments :math:`(-\infty, 1]` and :math:`[1, \infty)` of the real axis. + + Accordingly, for complex arguments, the function returns the inverse hyperbolic tangent in the range of a half-strip unbounded along the real axis and in the interval :math:`[-\pi j/2, +\pi j/2]` along the imaginary axis. + + *Note: branch cuts follow C99 and have provisional status* (see :ref:`branch-cuts`). + + Parameters + ---------- + x: array + input array whose elements each represent the area of a hyperbolic sector. Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the inverse hyperbolic tangent of each element in ``x``. The returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If ``x_i`` is less than ``-1``, the result is ``NaN``. + - If ``x_i`` is greater than ``1``, the result is ``NaN``. + - If ``x_i`` is ``-1``, the result is ``-infinity``. + - If ``x_i`` is ``+1``, the result is ``+infinity``. + - If ``x_i`` is ``+0``, the result is ``+0``. + - If ``x_i`` is ``-0``, the result is ``-0``. + + For complex floating-point operands, let ``a = real(x_i)``, ``b = imag(x_i)``, and + + - If ``a`` is ``+0`` and ``b`` is ``+0``, the result is ``+0 + 0j``. + - If ``a`` is ``+0`` and ``b`` is ``NaN``, the result is ``+0 + NaN j``. + - If ``a`` is ``1`` and ``b`` is ``+0``, the result is ``+infinity + 0j``. + - If ``a`` is a positive (i.e., greater than ``0``) finite number and ``b`` is ``+infinity``, the result is ``+0 + πj/2``. + - If ``a`` is a nonzero finite number and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + - If ``a`` is ``+infinity`` and ``b`` is a positive (i.e., greater than ``0``) finite number, the result is ``+0 + πj/2``. + - If ``a`` is ``+infinity`` and ``b`` is ``+infinity``, the result is ``+0 + πj/2``. + - If ``a`` is ``+infinity`` and ``b`` is ``NaN``, the result is ``+0 + NaN j``. + - If ``a`` is ``NaN`` and ``b`` is a finite number, the result is ``NaN + NaN j``. + - If ``a`` is ``NaN`` and ``b`` is ``+infinity``, the result is ``±0 + πj/2`` (sign of the real component is unspecified). + - If ``a`` is ``NaN`` and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def bitwise_and(x1: array, x2: array, /) -> array: + """ + Computes the bitwise AND of the underlying binary representation of each element ``x1_i`` of the input array ``x1`` with the respective element ``x2_i`` of the input array ``x2``. + + Parameters + ---------- + x1: array + first input array. Should have an integer or boolean data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have an integer or boolean data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type determined by :ref:`type-promotion`. + """ + + +def bitwise_left_shift(x1: array, x2: array, /) -> array: + """ + Shifts the bits of each element ``x1_i`` of the input array ``x1`` to the left by appending ``x2_i`` (i.e., the respective element in the input array ``x2``) zeros to the right of ``x1_i``. + + Parameters + ---------- + x1: array + first input array. Should have an integer data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have an integer data type. Each element must be greater than or equal to ``0``. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type determined by :ref:`type-promotion`. + """ + + +def bitwise_invert(x: array, /) -> array: + """ + Inverts (flips) each bit for each element ``x_i`` of the input array ``x``. + + Parameters + ---------- + x: array + input array. Should have an integer or boolean data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have the same data type as ``x``. + """ + + +def bitwise_or(x1: array, x2: array, /) -> array: + """ + Computes the bitwise OR of the underlying binary representation of each element ``x1_i`` of the input array ``x1`` with the respective element ``x2_i`` of the input array ``x2``. + + Parameters + ---------- + x1: array + first input array. Should have an integer or boolean data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have an integer or boolean data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type determined by :ref:`type-promotion`. + """ + + +def bitwise_right_shift(x1: array, x2: array, /) -> array: + """ + Shifts the bits of each element ``x1_i`` of the input array ``x1`` to the right according to the respective element ``x2_i`` of the input array ``x2``. + + .. note:: + This operation must be an arithmetic shift (i.e., sign-propagating) and thus equivalent to floor division by a power of two. + + Parameters + ---------- + x1: array + first input array. Should have an integer data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have an integer data type. Each element must be greater than or equal to ``0``. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type determined by :ref:`type-promotion`. + """ + + +def bitwise_xor(x1: array, x2: array, /) -> array: + """ + Computes the bitwise XOR of the underlying binary representation of each element ``x1_i`` of the input array ``x1`` with the respective element ``x2_i`` of the input array ``x2``. + + Parameters + ---------- + x1: array + first input array. Should have an integer or boolean data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have an integer or boolean data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type determined by :ref:`type-promotion`. + """ + + +def ceil(x: array, /) -> array: + """ + Rounds each element ``x_i`` of the input array ``x`` to the smallest (i.e., closest to ``-infinity``) integer-valued number that is not less than ``x_i``. + + Parameters + ---------- + x: array + input array. Should have a real-valued data type. + + Returns + ------- + out: array + an array containing the rounded result for each element in ``x``. The returned array must have the same data type as ``x``. + + Notes + ----- + + **Special cases** + + - If ``x_i`` is already integer-valued, the result is ``x_i``. + + For floating-point operands, + + - If ``x_i`` is ``+infinity``, the result is ``+infinity``. + - If ``x_i`` is ``-infinity``, the result is ``-infinity``. + - If ``x_i`` is ``+0``, the result is ``+0``. + - If ``x_i`` is ``-0``, the result is ``-0``. + - If ``x_i`` is ``NaN``, the result is ``NaN``. + """ + + +def clip( + x: array, + /, + min: Optional[Union[int, float, array]] = None, + max: Optional[Union[int, float, array]] = None, +) -> array: + r""" + Clamps each element ``x_i`` of the input array ``x`` to the range ``[min, max]``. + + Parameters + ---------- + x: array + input array. Should have a real-valued data type. + min: Optional[Union[int, float, array]] + lower-bound of the range to which to clamp. If ``None``, no lower bound must be applied. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a real-valued data type. Default: ``None``. + max: Optional[Union[int, float, array]] + upper-bound of the range to which to clamp. If ``None``, no upper bound must be applied. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a real-valued data type. Default: ``None``. + + Returns + ------- + out: array + an array containing element-wise results. The returned array must have the same data type as ``x``. + + Notes + ----- + + - If both ``min`` and ``max`` are ``None``, the elements of the returned array must equal the respective elements in ``x``. + - If a broadcasted element in ``min`` is greater than a corresponding broadcasted element in ``max``, behavior is unspecified and thus implementation-dependent. + - If ``x`` and either ``min`` or ``max`` have different data type kinds (e.g., integer versus floating-point), behavior is unspecified and thus implementation-dependent. + + .. versionadded:: 2023.12 + """ + + +def conj(x: array, /) -> array: + """ + Returns the complex conjugate for each element ``x_i`` of the input array ``x``. + + For complex numbers of the form + + .. math:: + a + bj + + the complex conjugate is defined as + + .. math:: + a - bj + + Hence, the returned complex conjugates must be computed by negating the imaginary component of each element ``x_i``. + + Parameters + ---------- + x: array + input array. Should have a complex floating-point data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have the same data type as ``x``. + + Notes + ----- + + .. versionadded:: 2022.12 + """ + + +def copysign(x1: array, x2: array, /) -> array: + r""" + Composes a floating-point value with the magnitude of ``x1_i`` and the sign of ``x2_i`` for each element of the input array ``x1``. + + Parameters + ---------- + x1: array + input array containing magnitudes. Should have a real-valued floating-point data type. + x2: array + input array whose sign bits are applied to the magnitudes of ``x1``. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a real-valued floating-point data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, let ``|x|`` be the absolute value, and if ``x1_i`` is not ``NaN``, + + - If ``x2_i`` is less than ``0``, the result is ``-|x1_i|``. + - If ``x2_i`` is ``-0``, the result is ``-|x1_i|``. + - If ``x2_i`` is ``+0``, the result is ``|x1_i|``. + - If ``x2_i`` is greater than ``0``, the result is ``|x1_i|``. + - If ``x2_i`` is ``NaN`` and the sign bit of ``x2_i`` is ``1``, the result is ``-|x1_i|``. + - If ``x2_i`` is ``NaN`` and the sign bit of ``x2_i`` is ``0``, the result is ``|x1_i|``. + + If ``x1_i`` is ``NaN``, + + - If ``x2_i`` is less than ``0``, the result is ``NaN`` with a sign bit of ``1``. + - If ``x2_i`` is ``-0``, the result is ``NaN`` with a sign bit of ``1``. + - If ``x2_i`` is ``+0``, the result is ``NaN`` with a sign bit of ``0``. + - If ``x2_i`` is greater than ``0``, the result is ``NaN`` with a sign bit of ``0``. + - If ``x2_i`` is ``NaN`` and the sign bit of ``x2_i`` is ``1``, the result is ``NaN`` with a sign bit of ``1``. + - If ``x2_i`` is ``NaN`` and the sign bit of ``x2_i`` is ``0``, the result is ``NaN`` with a sign bit of ``0``. + + .. versionadded:: 2023.12 + """ + + +def cos(x: array, /) -> array: + r""" + Calculates an implementation-dependent approximation to the cosine for each element ``x_i`` of the input array ``x``. + + Each element ``x_i`` is assumed to be expressed in radians. + + .. note:: + The cosine is an entire function on the complex plane and has no branch cuts. + + .. note:: + For complex arguments, the mathematical definition of cosine is + + .. math:: + \begin{align} \operatorname{cos}(x) &= \sum_{n=0}^\infty \frac{(-1)^n}{(2n)!} x^{2n} \\ &= \frac{e^{jx} + e^{-jx}}{2} \\ &= \operatorname{cosh}(jx) \end{align} + + where :math:`\operatorname{cosh}` is the hyperbolic cosine. + + Parameters + ---------- + x: array + input array whose elements are each expressed in radians. Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the cosine of each element in ``x``. The returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If ``x_i`` is ``+0``, the result is ``1``. + - If ``x_i`` is ``-0``, the result is ``1``. + - If ``x_i`` is ``+infinity``, the result is ``NaN``. + - If ``x_i`` is ``-infinity``, the result is ``NaN``. + + For complex floating-point operands, special cases must be handled as if the operation is implemented as ``cosh(x*1j)``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def cosh(x: array, /) -> array: + r""" + Calculates an implementation-dependent approximation to the hyperbolic cosine for each element ``x_i`` in the input array ``x``. + + The mathematical definition of the hyperbolic cosine is + + .. math:: + \operatorname{cosh}(x) = \frac{e^x + e^{-x}}{2} + + .. note:: + The hyperbolic cosine is an entire function in the complex plane and has no branch cuts. The function is periodic, with period :math:`2\pi j`, with respect to the imaginary component. + + Parameters + ---------- + x: array + input array whose elements each represent a hyperbolic angle. Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the hyperbolic cosine of each element in ``x``. The returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + .. note:: + For all operands, ``cosh(x)`` must equal ``cosh(-x)``. + + For real-valued floating-point operands, + + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If ``x_i`` is ``+0``, the result is ``1``. + - If ``x_i`` is ``-0``, the result is ``1``. + - If ``x_i`` is ``+infinity``, the result is ``+infinity``. + - If ``x_i`` is ``-infinity``, the result is ``+infinity``. + + For complex floating-point operands, let ``a = real(x_i)``, ``b = imag(x_i)``, and + + .. note:: + For complex floating-point operands, ``cosh(conj(x))`` must equal ``conj(cosh(x))``. + + - If ``a`` is ``+0`` and ``b`` is ``+0``, the result is ``1 + 0j``. + - If ``a`` is ``+0`` and ``b`` is ``+infinity``, the result is ``NaN + 0j`` (sign of the imaginary component is unspecified). + - If ``a`` is ``+0`` and ``b`` is ``NaN``, the result is ``NaN + 0j`` (sign of the imaginary component is unspecified). + - If ``a`` is a nonzero finite number and ``b`` is ``+infinity``, the result is ``NaN + NaN j``. + - If ``a`` is a nonzero finite number and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + - If ``a`` is ``+infinity`` and ``b`` is ``+0``, the result is ``+infinity + 0j``. + - If ``a`` is ``+infinity`` and ``b`` is a nonzero finite number, the result is ``+infinity * cis(b)``. + - If ``a`` is ``+infinity`` and ``b`` is ``+infinity``, the result is ``+infinity + NaN j`` (sign of the real component is unspecified). + - If ``a`` is ``+infinity`` and ``b`` is ``NaN``, the result is ``+infinity + NaN j``. + - If ``a`` is ``NaN`` and ``b`` is either ``+0`` or ``-0``, the result is ``NaN + 0j`` (sign of the imaginary component is unspecified). + - If ``a`` is ``NaN`` and ``b`` is a nonzero finite number, the result is ``NaN + NaN j``. + - If ``a`` is ``NaN`` and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + + where ``cis(v)`` is ``cos(v) + sin(v)*1j``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def divide(x1: array, x2: array, /) -> array: + r""" + Calculates the division of each element ``x1_i`` of the input array ``x1`` with the respective element ``x2_i`` of the input array ``x2``. + + .. note:: + If one or both of the input arrays have integer data types, the result is implementation-dependent, as type promotion between data type "kinds" (e.g., integer versus floating-point) is unspecified. + + Specification-compliant libraries may choose to raise an error or return an array containing the element-wise results. If an array is returned, the array must have a real-valued floating-point data type. + + Parameters + ---------- + x1: array + dividend input array. Should have a numeric data type. + x2: array + divisor input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a numeric data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If either ``x1_i`` or ``x2_i`` is ``NaN``, the result is ``NaN``. + - If ``x1_i`` is either ``+infinity`` or ``-infinity`` and ``x2_i`` is either ``+infinity`` or ``-infinity``, the result is ``NaN``. + - If ``x1_i`` is either ``+0`` or ``-0`` and ``x2_i`` is either ``+0`` or ``-0``, the result is ``NaN``. + - If ``x1_i`` is ``+0`` and ``x2_i`` is greater than ``0``, the result is ``+0``. + - If ``x1_i`` is ``-0`` and ``x2_i`` is greater than ``0``, the result is ``-0``. + - If ``x1_i`` is ``+0`` and ``x2_i`` is less than ``0``, the result is ``-0``. + - If ``x1_i`` is ``-0`` and ``x2_i`` is less than ``0``, the result is ``+0``. + - If ``x1_i`` is greater than ``0`` and ``x2_i`` is ``+0``, the result is ``+infinity``. + - If ``x1_i`` is greater than ``0`` and ``x2_i`` is ``-0``, the result is ``-infinity``. + - If ``x1_i`` is less than ``0`` and ``x2_i`` is ``+0``, the result is ``-infinity``. + - If ``x1_i`` is less than ``0`` and ``x2_i`` is ``-0``, the result is ``+infinity``. + - If ``x1_i`` is ``+infinity`` and ``x2_i`` is a positive (i.e., greater than ``0``) finite number, the result is ``+infinity``. + - If ``x1_i`` is ``+infinity`` and ``x2_i`` is a negative (i.e., less than ``0``) finite number, the result is ``-infinity``. + - If ``x1_i`` is ``-infinity`` and ``x2_i`` is a positive (i.e., greater than ``0``) finite number, the result is ``-infinity``. + - If ``x1_i`` is ``-infinity`` and ``x2_i`` is a negative (i.e., less than ``0``) finite number, the result is ``+infinity``. + - If ``x1_i`` is a positive (i.e., greater than ``0``) finite number and ``x2_i`` is ``+infinity``, the result is ``+0``. + - If ``x1_i`` is a positive (i.e., greater than ``0``) finite number and ``x2_i`` is ``-infinity``, the result is ``-0``. + - If ``x1_i`` is a negative (i.e., less than ``0``) finite number and ``x2_i`` is ``+infinity``, the result is ``-0``. + - If ``x1_i`` is a negative (i.e., less than ``0``) finite number and ``x2_i`` is ``-infinity``, the result is ``+0``. + - If ``x1_i`` and ``x2_i`` have the same mathematical sign and are both nonzero finite numbers, the result has a positive mathematical sign. + - If ``x1_i`` and ``x2_i`` have different mathematical signs and are both nonzero finite numbers, the result has a negative mathematical sign. + - In the remaining cases, where neither ``-infinity``, ``+0``, ``-0``, nor ``NaN`` is involved, the quotient must be computed and rounded to the nearest representable value according to IEEE 754-2019 and a supported rounding mode. If the magnitude is too large to represent, the operation overflows and the result is an ``infinity`` of appropriate mathematical sign. If the magnitude is too small to represent, the operation underflows and the result is a zero of appropriate mathematical sign. + + For complex floating-point operands, division is defined according to the following table. For real components ``a`` and ``c`` and imaginary components ``b`` and ``d``, + + +------------+----------------+-----------------+--------------------------+ + | | c | dj | c + dj | + +============+================+=================+==========================+ + | **a** | a / c | -(a/d)j | special rules | + +------------+----------------+-----------------+--------------------------+ + | **bj** | (b/c)j | b/d | special rules | + +------------+----------------+-----------------+--------------------------+ + | **a + bj** | (a/c) + (b/c)j | b/d - (a/d)j | special rules | + +------------+----------------+-----------------+--------------------------+ + + In general, for complex floating-point operands, real-valued floating-point special cases must independently apply to the real and imaginary component operations involving real numbers as described in the above table. + + When ``a``, ``b``, ``c``, or ``d`` are all finite numbers (i.e., a value other than ``NaN``, ``+infinity``, or ``-infinity``), division of complex floating-point operands should be computed as if calculated according to the textbook formula for complex number division + + .. math:: + \frac{a + bj}{c + dj} = \frac{(ac + bd) + (bc - ad)j}{c^2 + d^2} + + When at least one of ``a``, ``b``, ``c``, or ``d`` is ``NaN``, ``+infinity``, or ``-infinity``, + + - If ``a``, ``b``, ``c``, and ``d`` are all ``NaN``, the result is ``NaN + NaN j``. + - In the remaining cases, the result is implementation dependent. + + .. note:: + For complex floating-point operands, the results of special cases may be implementation dependent depending on how an implementation chooses to model complex numbers and complex infinity (e.g., complex plane versus Riemann sphere). For those implementations following C99 and its one-infinity model, when at least one component is infinite, even if the other component is ``NaN``, the complex value is infinite, and the usual arithmetic rules do not apply to complex-complex division. In the interest of performance, other implementations may want to avoid the complex branching logic necessary to implement the one-infinity model and choose to implement all complex-complex division according to the textbook formula. Accordingly, special case behavior is unlikely to be consistent across implementations. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def equal(x1: array, x2: array, /) -> array: + r""" + Computes the truth value of ``x1_i == x2_i`` for each element ``x1_i`` of the input array ``x1`` with the respective element ``x2_i`` of the input array ``x2``. + + Parameters + ---------- + x1: array + first input array. May have any data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). May have any data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type of ``bool``. + + Notes + ----- + + **Special Cases** + + For real-valued floating-point operands, + + - If ``x1_i`` is ``NaN`` or ``x2_i`` is ``NaN``, the result is ``False``. + - If ``x1_i`` is ``+infinity`` and ``x2_i`` is ``+infinity``, the result is ``True``. + - If ``x1_i`` is ``-infinity`` and ``x2_i`` is ``-infinity``, the result is ``True``. + - If ``x1_i`` is ``-0`` and ``x2_i`` is either ``+0`` or ``-0``, the result is ``True``. + - If ``x1_i`` is ``+0`` and ``x2_i`` is either ``+0`` or ``-0``, the result is ``True``. + - If ``x1_i`` is a finite number, ``x2_i`` is a finite number, and ``x1_i`` equals ``x2_i``, the result is ``True``. + - In the remaining cases, the result is ``False``. + + For complex floating-point operands, let ``a = real(x1_i)``, ``b = imag(x1_i)``, ``c = real(x2_i)``, ``d = imag(x2_i)``, and + + - If ``a``, ``b``, ``c``, or ``d`` is ``NaN``, the result is ``False``. + - In the remaining cases, the result is the logical AND of the equality comparison between the real values ``a`` and ``c`` (real components) and between the real values ``b`` and ``d`` (imaginary components), as described above for real-valued floating-point operands (i.e., ``a == c AND b == d``). + + .. note:: + For discussion of complex number equality, see :ref:`complex-numbers`. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def exp(x: array, /) -> array: + """ + Calculates an implementation-dependent approximation to the exponential function for each element ``x_i`` of the input array ``x`` (``e`` raised to the power of ``x_i``, where ``e`` is the base of the natural logarithm). + + .. note:: + For complex floating-point operands, ``exp(conj(x))`` must equal ``conj(exp(x))``. + + .. note:: + The exponential function is an entire function in the complex plane and has no branch cuts. + + Parameters + ---------- + x: array + input array. Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the evaluated exponential function result for each element in ``x``. The returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If ``x_i`` is ``+0``, the result is ``1``. + - If ``x_i`` is ``-0``, the result is ``1``. + - If ``x_i`` is ``+infinity``, the result is ``+infinity``. + - If ``x_i`` is ``-infinity``, the result is ``+0``. + + For complex floating-point operands, let ``a = real(x_i)``, ``b = imag(x_i)``, and + + - If ``a`` is either ``+0`` or ``-0`` and ``b`` is ``+0``, the result is ``1 + 0j``. + - If ``a`` is a finite number and ``b`` is ``+infinity``, the result is ``NaN + NaN j``. + - If ``a`` is a finite number and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + - If ``a`` is ``+infinity`` and ``b`` is ``+0``, the result is ``infinity + 0j``. + - If ``a`` is ``-infinity`` and ``b`` is a finite number, the result is ``+0 * cis(b)``. + - If ``a`` is ``+infinity`` and ``b`` is a nonzero finite number, the result is ``+infinity * cis(b)``. + - If ``a`` is ``-infinity`` and ``b`` is ``+infinity``, the result is ``0 + 0j`` (signs of real and imaginary components are unspecified). + - If ``a`` is ``+infinity`` and ``b`` is ``+infinity``, the result is ``infinity + NaN j`` (sign of real component is unspecified). + - If ``a`` is ``-infinity`` and ``b`` is ``NaN``, the result is ``0 + 0j`` (signs of real and imaginary components are unspecified). + - If ``a`` is ``+infinity`` and ``b`` is ``NaN``, the result is ``infinity + NaN j`` (sign of real component is unspecified). + - If ``a`` is ``NaN`` and ``b`` is ``+0``, the result is ``NaN + 0j``. + - If ``a`` is ``NaN`` and ``b`` is not equal to ``0``, the result is ``NaN + NaN j``. + - If ``a`` is ``NaN`` and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + + where ``cis(v)`` is ``cos(v) + sin(v)*1j``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def expm1(x: array, /) -> array: + """ + Calculates an implementation-dependent approximation to ``exp(x)-1`` for each element ``x_i`` of the input array ``x``. + + .. note:: + The purpose of this function is to calculate ``exp(x)-1.0`` more accurately when `x` is close to zero. Accordingly, conforming implementations should avoid implementing this function as simply ``exp(x)-1.0``. See FDLIBM, or some other IEEE 754-2019 compliant mathematical library, for a potential reference implementation. + + .. note:: + For complex floating-point operands, ``expm1(conj(x))`` must equal ``conj(expm1(x))``. + + .. note:: + The exponential function is an entire function in the complex plane and has no branch cuts. + + Parameters + ---------- + x: array + input array. Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the evaluated result for each element in ``x``. The returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If ``x_i`` is ``+0``, the result is ``+0``. + - If ``x_i`` is ``-0``, the result is ``-0``. + - If ``x_i`` is ``+infinity``, the result is ``+infinity``. + - If ``x_i`` is ``-infinity``, the result is ``-1``. + + For complex floating-point operands, let ``a = real(x_i)``, ``b = imag(x_i)``, and + + - If ``a`` is either ``+0`` or ``-0`` and ``b`` is ``+0``, the result is ``0 + 0j``. + - If ``a`` is a finite number and ``b`` is ``+infinity``, the result is ``NaN + NaN j``. + - If ``a`` is a finite number and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + - If ``a`` is ``+infinity`` and ``b`` is ``+0``, the result is ``+infinity + 0j``. + - If ``a`` is ``-infinity`` and ``b`` is a finite number, the result is ``+0 * cis(b) - 1.0``. + - If ``a`` is ``+infinity`` and ``b`` is a nonzero finite number, the result is ``+infinity * cis(b) - 1.0``. + - If ``a`` is ``-infinity`` and ``b`` is ``+infinity``, the result is ``-1 + 0j`` (sign of imaginary component is unspecified). + - If ``a`` is ``+infinity`` and ``b`` is ``+infinity``, the result is ``infinity + NaN j`` (sign of real component is unspecified). + - If ``a`` is ``-infinity`` and ``b`` is ``NaN``, the result is ``-1 + 0j`` (sign of imaginary component is unspecified). + - If ``a`` is ``+infinity`` and ``b`` is ``NaN``, the result is ``infinity + NaN j`` (sign of real component is unspecified). + - If ``a`` is ``NaN`` and ``b`` is ``+0``, the result is ``NaN + 0j``. + - If ``a`` is ``NaN`` and ``b`` is not equal to ``0``, the result is ``NaN + NaN j``. + - If ``a`` is ``NaN`` and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + + where ``cis(v)`` is ``cos(v) + sin(v)*1j``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def floor(x: array, /) -> array: + """ + Rounds each element ``x_i`` of the input array ``x`` to the greatest (i.e., closest to ``+infinity``) integer-valued number that is not greater than ``x_i``. + + Parameters + ---------- + x: array + input array. Should have a real-valued data type. + + Returns + ------- + out: array + an array containing the rounded result for each element in ``x``. The returned array must have the same data type as ``x``. + + Notes + ----- + + **Special cases** + + - If ``x_i`` is already integer-valued, the result is ``x_i``. + + For floating-point operands, + + - If ``x_i`` is ``+infinity``, the result is ``+infinity``. + - If ``x_i`` is ``-infinity``, the result is ``-infinity``. + - If ``x_i`` is ``+0``, the result is ``+0``. + - If ``x_i`` is ``-0``, the result is ``-0``. + - If ``x_i`` is ``NaN``, the result is ``NaN``. + """ + + +def floor_divide(x1: array, x2: array, /) -> array: + r""" + Rounds the result of dividing each element ``x1_i`` of the input array ``x1`` by the respective element ``x2_i`` of the input array ``x2`` to the greatest (i.e., closest to `+infinity`) integer-value number that is not greater than the division result. + + .. note:: + For input arrays which promote to an integer data type, the result of division by zero is unspecified and thus implementation-defined. + + Parameters + ---------- + x1: array + dividend input array. Should have a real-valued data type. + x2: array + divisor input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a real-valued data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + .. note:: + Floor division was introduced in Python via `PEP 238 `_ with the goal to disambiguate "true division" (i.e., computing an approximation to the mathematical operation of division) from "floor division" (i.e., rounding the result of division toward negative infinity). The former was computed when one of the operands was a ``float``, while the latter was computed when both operands were ``int``\s. Overloading the ``/`` operator to support both behaviors led to subtle numerical bugs when integers are possible, but not expected. + + To resolve this ambiguity, ``/`` was designated for true division, and ``//`` was designated for floor division. Semantically, floor division was `defined `_ as equivalent to ``a // b == floor(a/b)``; however, special floating-point cases were left ill-defined. + + Accordingly, floor division is not implemented consistently across array libraries for some of the special cases documented below. Namely, when one of the operands is ``infinity``, libraries may diverge with some choosing to strictly follow ``floor(a/b)`` and others choosing to pair ``//`` with ``%`` according to the relation ``b = a % b + b * (a // b)``. The special cases leading to divergent behavior are documented below. + + This specification prefers floor division to match ``floor(divide(x1, x2))`` in order to avoid surprising and unexpected results; however, array libraries may choose to more strictly follow Python behavior. + + For floating-point operands, + + - If either ``x1_i`` or ``x2_i`` is ``NaN``, the result is ``NaN``. + - If ``x1_i`` is either ``+infinity`` or ``-infinity`` and ``x2_i`` is either ``+infinity`` or ``-infinity``, the result is ``NaN``. + - If ``x1_i`` is either ``+0`` or ``-0`` and ``x2_i`` is either ``+0`` or ``-0``, the result is ``NaN``. + - If ``x1_i`` is ``+0`` and ``x2_i`` is greater than ``0``, the result is ``+0``. + - If ``x1_i`` is ``-0`` and ``x2_i`` is greater than ``0``, the result is ``-0``. + - If ``x1_i`` is ``+0`` and ``x2_i`` is less than ``0``, the result is ``-0``. + - If ``x1_i`` is ``-0`` and ``x2_i`` is less than ``0``, the result is ``+0``. + - If ``x1_i`` is greater than ``0`` and ``x2_i`` is ``+0``, the result is ``+infinity``. + - If ``x1_i`` is greater than ``0`` and ``x2_i`` is ``-0``, the result is ``-infinity``. + - If ``x1_i`` is less than ``0`` and ``x2_i`` is ``+0``, the result is ``-infinity``. + - If ``x1_i`` is less than ``0`` and ``x2_i`` is ``-0``, the result is ``+infinity``. + - If ``x1_i`` is ``+infinity`` and ``x2_i`` is a positive (i.e., greater than ``0``) finite number, the result is ``+infinity``. (**note**: libraries may return ``NaN`` to match Python behavior.) + - If ``x1_i`` is ``+infinity`` and ``x2_i`` is a negative (i.e., less than ``0``) finite number, the result is ``-infinity``. (**note**: libraries may return ``NaN`` to match Python behavior.) + - If ``x1_i`` is ``-infinity`` and ``x2_i`` is a positive (i.e., greater than ``0``) finite number, the result is ``-infinity``. (**note**: libraries may return ``NaN`` to match Python behavior.) + - If ``x1_i`` is ``-infinity`` and ``x2_i`` is a negative (i.e., less than ``0``) finite number, the result is ``+infinity``. (**note**: libraries may return ``NaN`` to match Python behavior.) + - If ``x1_i`` is a positive (i.e., greater than ``0``) finite number and ``x2_i`` is ``+infinity``, the result is ``+0``. + - If ``x1_i`` is a positive (i.e., greater than ``0``) finite number and ``x2_i`` is ``-infinity``, the result is ``-0``. (**note**: libraries may return ``-1.0`` to match Python behavior.) + - If ``x1_i`` is a negative (i.e., less than ``0``) finite number and ``x2_i`` is ``+infinity``, the result is ``-0``. (**note**: libraries may return ``-1.0`` to match Python behavior.) + - If ``x1_i`` is a negative (i.e., less than ``0``) finite number and ``x2_i`` is ``-infinity``, the result is ``+0``. + - If ``x1_i`` and ``x2_i`` have the same mathematical sign and are both nonzero finite numbers, the result has a positive mathematical sign. + - If ``x1_i`` and ``x2_i`` have different mathematical signs and are both nonzero finite numbers, the result has a negative mathematical sign. + - In the remaining cases, where neither ``-infinity``, ``+0``, ``-0``, nor ``NaN`` is involved, the quotient must be computed and rounded to the greatest (i.e., closest to `+infinity`) representable integer-value number that is not greater than the division result. If the magnitude is too large to represent, the operation overflows and the result is an ``infinity`` of appropriate mathematical sign. If the magnitude is too small to represent, the operation underflows and the result is a zero of appropriate mathematical sign. + """ + + +def greater(x1: array, x2: array, /) -> array: + """ + Computes the truth value of ``x1_i > x2_i`` for each element ``x1_i`` of the input array ``x1`` with the respective element ``x2_i`` of the input array ``x2``. + + .. note:: + For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-dependent (see :ref:`complex-number-ordering`). + + Parameters + ---------- + x1: array + first input array. Should have a real-valued data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a real-valued data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type of ``bool``. + """ + + +def greater_equal(x1: array, x2: array, /) -> array: + """ + Computes the truth value of ``x1_i >= x2_i`` for each element ``x1_i`` of the input array ``x1`` with the respective element ``x2_i`` of the input array ``x2``. + + .. note:: + For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-dependent (see :ref:`complex-number-ordering`). + + Parameters + ---------- + x1: array + first input array. Should have a real-valued data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a real-valued data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type of ``bool``. + """ + + +def hypot(x1: array, x2: array, /) -> array: + r""" + Computes the square root of the sum of squares for each element ``x1_i`` of the input array ``x1`` with the respective element ``x2_i`` of the input array ``x2``. + + .. note:: + The value computed by this function may be interpreted as the length of the hypotenuse of a right-angled triangle with sides of length ``x1_i`` and ``x2_i``, the distance of a point ``(x1_i, x2_i)`` from the origin ``(0, 0)``, or the magnitude of a complex number ``x1_i + x2_i * 1j``. + + Parameters + ---------- + x1: array + first input array. Should have a real-valued floating-point data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a real-valued floating-point data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a real-valued floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + The purpose of this function is to avoid underflow and overflow during intermediate stages of computation. Accordingly, conforming implementations should not use naive implementations. + + **Special Cases** + + For real-valued floating-point operands, + + - If ``x1_i`` is ``+infinity`` or ``-infinity`` and ``x2_i`` is any value, including ``NaN``, the result is ``+infinity``. + - If ``x2_i`` is ``+infinity`` or ``-infinity`` and ``x1_i`` is any value, including ``NaN``, the result is ``+infinity``. + - If ``x1_i`` is either ``+0`` or ``-0``, the result is equivalent to ``abs(x2_i)``. + - If ``x2_i`` is either ``+0`` or ``-0``, the result is equivalent to ``abs(x1_i)``. + - If ``x1_i`` is a finite number or ``NaN`` and ``x2_i`` is ``NaN``, the result is ``NaN``. + - If ``x2_i`` is a finite number or ``NaN`` and ``x1_i`` is ``NaN``, the result is ``NaN``. + - Underflow may only occur when both arguments are subnormal and the correct result is also subnormal. + + For real-valued floating-point operands, ``hypot(x1, x2)`` must equal ``hypot(x2, x1)``, ``hypot(x1, -x2)``, ``hypot(-x1, x2)``, and ``hypot(-x1, -x2)``. + + .. note:: + IEEE 754-2019 requires support for subnormal (a.k.a., denormal) numbers, which are useful for supporting gradual underflow. However, hardware support for subnormal numbers is not universal, and many platforms (e.g., accelerators) and compilers support toggling denormals-are-zero (DAZ) and/or flush-to-zero (FTZ) behavior to increase performance and to guard against timing attacks. + + Accordingly, conforming implementations may vary in their support for subnormal numbers. + + .. versionadded:: 2023.12 + """ + + +def imag(x: array, /) -> array: + """ + Returns the imaginary component of a complex number for each element ``x_i`` of the input array ``x``. + + Parameters + ---------- + x: array + input array. Should have a complex floating-point data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a floating-point data type with the same floating-point precision as ``x`` (e.g., if ``x`` is ``complex64``, the returned array must have the floating-point data type ``float32``). + + Notes + ----- + + .. versionadded:: 2022.12 + """ + + +def isfinite(x: array, /) -> array: + """ + Tests each element ``x_i`` of the input array ``x`` to determine if finite. + + Parameters + ---------- + x: array + input array. Should have a numeric data type. + + Returns + ------- + out: array + an array containing test results. The returned array must have a data type of ``bool``. + + Notes + ----- + + **Special Cases** + + For real-valued floating-point operands, + + - If ``x_i`` is either ``+infinity`` or ``-infinity``, the result is ``False``. + - If ``x_i`` is ``NaN``, the result is ``False``. + - If ``x_i`` is a finite number, the result is ``True``. + + For complex floating-point operands, let ``a = real(x_i)``, ``b = imag(x_i)``, and + + - If ``a`` is ``NaN`` or ``b`` is ``NaN``, the result is ``False``. + - If ``a`` is either ``+infinity`` or ``-infinity`` and ``b`` is any value, the result is ``False``. + - If ``a`` is any value and ``b`` is either ``+infinity`` or ``-infinity``, the result is ``False``. + - If ``a`` is a finite number and ``b`` is a finite number, the result is ``True``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def isinf(x: array, /) -> array: + """ + Tests each element ``x_i`` of the input array ``x`` to determine if equal to positive or negative infinity. + + Parameters + ---------- + x: array + input array. Should have a numeric data type. + + Returns + ------- + out: array + an array containing test results. The returned array must have a data type of ``bool``. + + Notes + ----- + + **Special Cases** + + For real-valued floating-point operands, + + - If ``x_i`` is either ``+infinity`` or ``-infinity``, the result is ``True``. + - In the remaining cases, the result is ``False``. + + For complex floating-point operands, let ``a = real(x_i)``, ``b = imag(x_i)``, and + + - If ``a`` is either ``+infinity`` or ``-infinity`` and ``b`` is any value (including ``NaN``), the result is ``True``. + - If ``a`` is either a finite number or ``NaN`` and ``b`` is either ``+infinity`` or ``-infinity``, the result is ``True``. + - In the remaining cases, the result is ``False``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def isnan(x: array, /) -> array: + """ + Tests each element ``x_i`` of the input array ``x`` to determine whether the element is ``NaN``. + + Parameters + ---------- + x: array + input array. Should have a numeric data type. + + Returns + ------- + out: array + an array containing test results. The returned array should have a data type of ``bool``. + + Notes + ----- + + **Special Cases** + + For real-valued floating-point operands, + + - If ``x_i`` is ``NaN``, the result is ``True``. + - In the remaining cases, the result is ``False``. + + For complex floating-point operands, let ``a = real(x_i)``, ``b = imag(x_i)``, and + + - If ``a`` or ``b`` is ``NaN``, the result is ``True``. + - In the remaining cases, the result is ``False``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def less(x1: array, x2: array, /) -> array: + """ + Computes the truth value of ``x1_i < x2_i`` for each element ``x1_i`` of the input array ``x1`` with the respective element ``x2_i`` of the input array ``x2``. + + .. note:: + For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-dependent (see :ref:`complex-number-ordering`). + + Parameters + ---------- + x1: array + first input array. Should have a real-valued data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a real-valued data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type of ``bool``. + """ + + +def less_equal(x1: array, x2: array, /) -> array: + """ + Computes the truth value of ``x1_i <= x2_i`` for each element ``x1_i`` of the input array ``x1`` with the respective element ``x2_i`` of the input array ``x2``. + + .. note:: + For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-dependent (see :ref:`complex-number-ordering`). + + Parameters + ---------- + x1: array + first input array. Should have a real-valued data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a real-valued data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type of ``bool``. + """ + + +def log(x: array, /) -> array: + r""" + Calculates an implementation-dependent approximation to the natural (base ``e``) logarithm for each element ``x_i`` of the input array ``x``. + + .. note:: + The natural logarithm of a complex number :math:`z` with polar coordinates :math:`(r,\theta)` equals :math:`\ln r + (\theta + 2n\pi)j` with principal value :math:`\ln r + \theta j`. + + .. note:: + For complex floating-point operands, ``log(conj(x))`` must equal ``conj(log(x))``. + + .. note:: + By convention, the branch cut of the natural logarithm is the negative real axis :math:`(-\infty, 0)`. + + The natural logarithm is a continuous function from above the branch cut, taking into account the sign of the imaginary component. + + Accordingly, for complex arguments, the function returns the natural logarithm in the range of a strip in the interval :math:`[-\pi j, +\pi j]` along the imaginary axis and mathematically unbounded along the real axis. + + *Note: branch cuts follow C99 and have provisional status* (see :ref:`branch-cuts`). + + Parameters + ---------- + x: array + input array. Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the evaluated natural logarithm for each element in ``x``. The returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If ``x_i`` is less than ``0``, the result is ``NaN``. + - If ``x_i`` is either ``+0`` or ``-0``, the result is ``-infinity``. + - If ``x_i`` is ``1``, the result is ``+0``. + - If ``x_i`` is ``+infinity``, the result is ``+infinity``. + + For complex floating-point operands, let ``a = real(x_i)``, ``b = imag(x_i)``, and + + - If ``a`` is ``-0`` and ``b`` is ``+0``, the result is ``-infinity + πj``. + - If ``a`` is ``+0`` and ``b`` is ``+0``, the result is ``-infinity + 0j``. + - If ``a`` is a finite number and ``b`` is ``+infinity``, the result is ``+infinity + πj/2``. + - If ``a`` is a finite number and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + - If ``a`` is ``-infinity`` and ``b`` is a positive (i.e., greater than ``0``) finite number, the result is ``+infinity + πj``. + - If ``a`` is ``+infinity`` and ``b`` is a positive (i.e., greater than ``0``) finite number, the result is ``+infinity + 0j``. + - If ``a`` is ``-infinity`` and ``b`` is ``+infinity``, the result is ``+infinity + 3πj/4``. + - If ``a`` is ``+infinity`` and ``b`` is ``+infinity``, the result is ``+infinity + πj/4``. + - If ``a`` is either ``+infinity`` or ``-infinity`` and ``b`` is ``NaN``, the result is ``+infinity + NaN j``. + - If ``a`` is ``NaN`` and ``b`` is a finite number, the result is ``NaN + NaN j``. + - If ``a`` is ``NaN`` and ``b`` is ``+infinity``, the result is ``+infinity + NaN j``. + - If ``a`` is ``NaN`` and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def log1p(x: array, /) -> array: + r""" + Calculates an implementation-dependent approximation to ``log(1+x)``, where ``log`` refers to the natural (base ``e``) logarithm, for each element ``x_i`` of the input array ``x``. + + .. note:: + The purpose of this function is to calculate ``log(1+x)`` more accurately when `x` is close to zero. Accordingly, conforming implementations should avoid implementing this function as simply ``log(1+x)``. See FDLIBM, or some other IEEE 754-2019 compliant mathematical library, for a potential reference implementation. + + .. note:: + For complex floating-point operands, ``log1p(conj(x))`` must equal ``conj(log1p(x))``. + + .. note:: + By convention, the branch cut of the natural logarithm is the negative real axis :math:`(-\infty, 0)`. + + The natural logarithm is a continuous function from above the branch cut, taking into account the sign of the imaginary component. + + Accordingly, for complex arguments, the function returns the natural logarithm in the range of a strip in the interval :math:`[-\pi j, +\pi j]` along the imaginary axis and mathematically unbounded along the real axis. + + *Note: branch cuts follow C99 and have provisional status* (see :ref:`branch-cuts`). + + Parameters + ---------- + x: array + input array. Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the evaluated result for each element in ``x``. The returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If ``x_i`` is less than ``-1``, the result is ``NaN``. + - If ``x_i`` is ``-1``, the result is ``-infinity``. + - If ``x_i`` is ``-0``, the result is ``-0``. + - If ``x_i`` is ``+0``, the result is ``+0``. + - If ``x_i`` is ``+infinity``, the result is ``+infinity``. + + For complex floating-point operands, let ``a = real(x_i)``, ``b = imag(x_i)``, and + + - If ``a`` is ``-1`` and ``b`` is ``+0``, the result is ``-infinity + 0j``. + - If ``a`` is a finite number and ``b`` is ``+infinity``, the result is ``+infinity + πj/2``. + - If ``a`` is a finite number and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + - If ``a`` is ``-infinity`` and ``b`` is a positive (i.e., greater than ``0``) finite number, the result is ``+infinity + πj``. + - If ``a`` is ``+infinity`` and ``b`` is a positive (i.e., greater than ``0``) finite number, the result is ``+infinity + 0j``. + - If ``a`` is ``-infinity`` and ``b`` is ``+infinity``, the result is ``+infinity + 3πj/4``. + - If ``a`` is ``+infinity`` and ``b`` is ``+infinity``, the result is ``+infinity + πj/4``. + - If ``a`` is either ``+infinity`` or ``-infinity`` and ``b`` is ``NaN``, the result is ``+infinity + NaN j``. + - If ``a`` is ``NaN`` and ``b`` is a finite number, the result is ``NaN + NaN j``. + - If ``a`` is ``NaN`` and ``b`` is ``+infinity``, the result is ``+infinity + NaN j``. + - If ``a`` is ``NaN`` and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def log2(x: array, /) -> array: + r""" + Calculates an implementation-dependent approximation to the base ``2`` logarithm for each element ``x_i`` of the input array ``x``. + + .. note:: + For complex floating-point operands, ``log2(conj(x))`` must equal ``conj(log2(x))``. + + Parameters + ---------- + x: array + input array. Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the evaluated base ``2`` logarithm for each element in ``x``. The returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If ``x_i`` is less than ``0``, the result is ``NaN``. + - If ``x_i`` is either ``+0`` or ``-0``, the result is ``-infinity``. + - If ``x_i`` is ``1``, the result is ``+0``. + - If ``x_i`` is ``+infinity``, the result is ``+infinity``. + + For complex floating-point operands, special cases must be handled as if the operation is implemented using the standard change of base formula + + .. math:: + \log_{2} x = \frac{\log_{e} x}{\log_{e} 2} + + where :math:`\log_{e}` is the natural logarithm, as implemented by :func:`~array_api.log`. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def log10(x: array, /) -> array: + r""" + Calculates an implementation-dependent approximation to the base ``10`` logarithm for each element ``x_i`` of the input array ``x``. + + .. note:: + For complex floating-point operands, ``log10(conj(x))`` must equal ``conj(log10(x))``. + + Parameters + ---------- + x: array + input array. Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the evaluated base ``10`` logarithm for each element in ``x``. The returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If ``x_i`` is less than ``0``, the result is ``NaN``. + - If ``x_i`` is either ``+0`` or ``-0``, the result is ``-infinity``. + - If ``x_i`` is ``1``, the result is ``+0``. + - If ``x_i`` is ``+infinity``, the result is ``+infinity``. + + For complex floating-point operands, special cases must be handled as if the operation is implemented using the standard change of base formula + + .. math:: + \log_{10} x = \frac{\log_{e} x}{\log_{e} 10} + + where :math:`\log_{e}` is the natural logarithm, as implemented by :func:`~array_api.log`. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def logaddexp(x1: array, x2: array, /) -> array: + """ + Calculates the logarithm of the sum of exponentiations ``log(exp(x1) + exp(x2))`` for each element ``x1_i`` of the input array ``x1`` with the respective element ``x2_i`` of the input array ``x2``. + + Parameters + ---------- + x1: array + first input array. Should have a real-valued floating-point data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a real-valued floating-point data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a real-valued floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For floating-point operands, + + - If either ``x1_i`` or ``x2_i`` is ``NaN``, the result is ``NaN``. + - If ``x1_i`` is ``+infinity`` and ``x2_i`` is not ``NaN``, the result is ``+infinity``. + - If ``x1_i`` is not ``NaN`` and ``x2_i`` is ``+infinity``, the result is ``+infinity``. + """ + + +def logical_and(x1: array, x2: array, /) -> array: + """ + Computes the logical AND for each element ``x1_i`` of the input array ``x1`` with the respective element ``x2_i`` of the input array ``x2``. + + .. note:: + While this specification recommends that this function only accept input arrays having a boolean data type, specification-compliant array libraries may choose to accept input arrays having real-valued data types. If non-boolean data types are supported, zeros must be considered the equivalent of ``False``, while non-zeros must be considered the equivalent of ``True``. + + Parameters + ---------- + x1: array + first input array. Should have a boolean data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a boolean data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type of `bool`. + """ + + +def logical_not(x: array, /) -> array: + """ + Computes the logical NOT for each element ``x_i`` of the input array ``x``. + + .. note:: + While this specification recommends that this function only accept input arrays having a boolean data type, specification-compliant array libraries may choose to accept input arrays having real-valued data types. If non-boolean data types are supported, zeros must be considered the equivalent of ``False``, while non-zeros must be considered the equivalent of ``True``. + + Parameters + ---------- + x: array + input array. Should have a boolean data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type of ``bool``. + """ + + +def logical_or(x1: array, x2: array, /) -> array: + """ + Computes the logical OR for each element ``x1_i`` of the input array ``x1`` with the respective element ``x2_i`` of the input array ``x2``. + + .. note:: + While this specification recommends that this function only accept input arrays having a boolean data type, specification-compliant array libraries may choose to accept input arrays having real-valued data types. If non-boolean data types are supported, zeros must be considered the equivalent of ``False``, while non-zeros must be considered the equivalent of ``True``. + + Parameters + ---------- + x1: array + first input array. Should have a boolean data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a boolean data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type of ``bool``. + """ + + +def logical_xor(x1: array, x2: array, /) -> array: + """ + Computes the logical XOR for each element ``x1_i`` of the input array ``x1`` with the respective element ``x2_i`` of the input array ``x2``. + + .. note:: + While this specification recommends that this function only accept input arrays having a boolean data type, specification-compliant array libraries may choose to accept input arrays having real-valued data types. If non-boolean data types are supported, zeros must be considered the equivalent of ``False``, while non-zeros must be considered the equivalent of ``True``. + + Parameters + ---------- + x1: array + first input array. Should have a boolean data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a boolean data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type of ``bool``. + """ + + +def maximum(x1: array, x2: array, /) -> array: + r""" + Computes the maximum value for each element ``x1_i`` of the input array ``x1`` relative to the respective element ``x2_i`` of the input array ``x2``. + + Parameters + ---------- + x1: array + first input array. Should have a real-valued data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a real-valued data type. + + Returns + ------- + out: array + an array containing the element-wise maximum values. The returned array must have a data type determined by :ref:`type-promotion`. + + Notes + ----- + + The order of signed zeros is unspecified and thus implementation-defined. When choosing between ``-0`` or ``+0`` as a maximum value, specification-compliant libraries may choose to return either value. + + For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-defined (see :ref:`complex-number-ordering`). + + **Special Cases** + + For floating-point operands, + + - If either ``x1_i`` or ``x2_i`` is ``NaN``, the result is ``NaN``. + + .. versionadded:: 2023.12 + """ + + +def minimum(x1: array, x2: array, /) -> array: + r""" + Computes the minimum value for each element ``x1_i`` of the input array ``x1`` relative to the respective element ``x2_i`` of the input array ``x2``. + + Parameters + ---------- + x1: array + first input array. Should have a real-valued data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a real-valued data type. + + Returns + ------- + out: array + an array containing the element-wise minimum values. The returned array must have a data type determined by :ref:`type-promotion`. + + Notes + ----- + + The order of signed zeros is unspecified and thus implementation-defined. When choosing between ``-0`` or ``+0`` as a minimum value, specification-compliant libraries may choose to return either value. + + For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-defined (see :ref:`complex-number-ordering`). + + **Special Cases** + + For floating-point operands, + + - If either ``x1_i`` or ``x2_i`` is ``NaN``, the result is ``NaN``. + + .. versionadded:: 2023.12 + """ + + +def multiply(x1: array, x2: array, /) -> array: + r""" + Calculates the product for each element ``x1_i`` of the input array ``x1`` with the respective element ``x2_i`` of the input array ``x2``. + + .. note:: + Floating-point multiplication is not always associative due to finite precision. + + Parameters + ---------- + x1: array + first input array. Should have a numeric data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a numeric data type. + + Returns + ------- + out: array + an array containing the element-wise products. The returned array must have a data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If either ``x1_i`` or ``x2_i`` is ``NaN``, the result is ``NaN``. + - If ``x1_i`` is either ``+infinity`` or ``-infinity`` and ``x2_i`` is either ``+0`` or ``-0``, the result is ``NaN``. + - If ``x1_i`` is either ``+0`` or ``-0`` and ``x2_i`` is either ``+infinity`` or ``-infinity``, the result is ``NaN``. + - If ``x1_i`` and ``x2_i`` have the same mathematical sign, the result has a positive mathematical sign, unless the result is ``NaN``. If the result is ``NaN``, the "sign" of ``NaN`` is implementation-defined. + - If ``x1_i`` and ``x2_i`` have different mathematical signs, the result has a negative mathematical sign, unless the result is ``NaN``. If the result is ``NaN``, the "sign" of ``NaN`` is implementation-defined. + - If ``x1_i`` is either ``+infinity`` or ``-infinity`` and ``x2_i`` is either ``+infinity`` or ``-infinity``, the result is a signed infinity with the mathematical sign determined by the rule already stated above. + - If ``x1_i`` is either ``+infinity`` or ``-infinity`` and ``x2_i`` is a nonzero finite number, the result is a signed infinity with the mathematical sign determined by the rule already stated above. + - If ``x1_i`` is a nonzero finite number and ``x2_i`` is either ``+infinity`` or ``-infinity``, the result is a signed infinity with the mathematical sign determined by the rule already stated above. + - In the remaining cases, where neither ``infinity`` nor ``NaN`` is involved, the product must be computed and rounded to the nearest representable value according to IEEE 754-2019 and a supported rounding mode. If the magnitude is too large to represent, the result is an `infinity` of appropriate mathematical sign. If the magnitude is too small to represent, the result is a zero of appropriate mathematical sign. + + For complex floating-point operands, multiplication is defined according to the following table. For real components ``a`` and ``c`` and imaginary components ``b`` and ``d``, + + +------------+----------------+-----------------+--------------------------+ + | | c | dj | c + dj | + +============+================+=================+==========================+ + | **a** | a * c | (a*d)j | (a*c) + (a*d)j | + +------------+----------------+-----------------+--------------------------+ + | **bj** | (b*c)j | -(b*d) | -(b*d) + (b*c)j | + +------------+----------------+-----------------+--------------------------+ + | **a + bj** | (a*c) + (b*c)j | -(b*d) + (a*d)j | special rules | + +------------+----------------+-----------------+--------------------------+ + + In general, for complex floating-point operands, real-valued floating-point special cases must independently apply to the real and imaginary component operations involving real numbers as described in the above table. + + When ``a``, ``b``, ``c``, or ``d`` are all finite numbers (i.e., a value other than ``NaN``, ``+infinity``, or ``-infinity``), multiplication of complex floating-point operands should be computed as if calculated according to the textbook formula for complex number multiplication + + .. math:: + (a + bj) \cdot (c + dj) = (ac - bd) + (bc + ad)j + + When at least one of ``a``, ``b``, ``c``, or ``d`` is ``NaN``, ``+infinity``, or ``-infinity``, + + - If ``a``, ``b``, ``c``, and ``d`` are all ``NaN``, the result is ``NaN + NaN j``. + - In the remaining cases, the result is implementation dependent. + + .. note:: + For complex floating-point operands, the results of special cases may be implementation dependent depending on how an implementation chooses to model complex numbers and complex infinity (e.g., complex plane versus Riemann sphere). For those implementations following C99 and its one-infinity model, when at least one component is infinite, even if the other component is ``NaN``, the complex value is infinite, and the usual arithmetic rules do not apply to complex-complex multiplication. In the interest of performance, other implementations may want to avoid the complex branching logic necessary to implement the one-infinity model and choose to implement all complex-complex multiplication according to the textbook formula. Accordingly, special case behavior is unlikely to be consistent across implementations. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def negative(x: array, /) -> array: + """ + Computes the numerical negative of each element ``x_i`` (i.e., ``y_i = -x_i``) of the input array ``x``. + + .. note:: + For signed integer data types, the numerical negative of the minimum representable integer is implementation-dependent. + + .. note:: + If ``x`` has a complex floating-point data type, both the real and imaginary components for each ``x_i`` must be negated (a result which follows from the rules of complex number multiplication). + + Parameters + ---------- + x: array + input array. Should have a numeric data type. + + Returns + ------- + out: array + an array containing the evaluated result for each element in ``x``. The returned array must have a data type determined by :ref:`type-promotion`. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def not_equal(x1: array, x2: array, /) -> array: + """ + Computes the truth value of ``x1_i != x2_i`` for each element ``x1_i`` of the input array ``x1`` with the respective element ``x2_i`` of the input array ``x2``. + + Parameters + ---------- + x1: array + first input array. May have any data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type of ``bool``. + + Notes + ----- + + **Special Cases** + + For real-valued floating-point operands, + + - If ``x1_i`` is ``NaN`` or ``x2_i`` is ``NaN``, the result is ``True``. + - If ``x1_i`` is ``+infinity`` and ``x2_i`` is ``-infinity``, the result is ``True``. + - If ``x1_i`` is ``-infinity`` and ``x2_i`` is ``+infinity``, the result is ``True``. + - If ``x1_i`` is a finite number, ``x2_i`` is a finite number, and ``x1_i`` does not equal ``x2_i``, the result is ``True``. + - In the remaining cases, the result is ``False``. + + For complex floating-point operands, let ``a = real(x1_i)``, ``b = imag(x1_i)``, ``c = real(x2_i)``, ``d = imag(x2_i)``, and + + - If ``a``, ``b``, ``c``, or ``d`` is ``NaN``, the result is ``True``. + - In the remaining cases, the result is the logical OR of the equality comparison between the real values ``a`` and ``c`` (real components) and between the real values ``b`` and ``d`` (imaginary components), as described above for real-valued floating-point operands (i.e., ``a != c OR b != d``). + + .. note:: + For discussion of complex number equality, see :ref:`complex-numbers`. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def positive(x: array, /) -> array: + """ + Computes the numerical positive of each element ``x_i`` (i.e., ``y_i = +x_i``) of the input array ``x``. + + Parameters + ---------- + x: array + input array. Should have a numeric data type. + + Returns + ------- + out: array + an array containing the evaluated result for each element in ``x``. The returned array must have the same data type as ``x``. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def pow(x1: array, x2: array, /) -> array: + r""" + Calculates an implementation-dependent approximation of exponentiation by raising each element ``x1_i`` (the base) of the input array ``x1`` to the power of ``x2_i`` (the exponent), where ``x2_i`` is the corresponding element of the input array ``x2``. + + .. note:: + If both ``x1`` and ``x2`` have integer data types, the result of ``pow`` when ``x2_i`` is negative (i.e., less than zero) is unspecified and thus implementation-dependent. + + If ``x1`` has an integer data type and ``x2`` has a floating-point data type, behavior is implementation-dependent (type promotion between data type "kinds" (integer versus floating-point) is unspecified). + + .. note:: + By convention, the branch cut of the natural logarithm is the negative real axis :math:`(-\infty, 0)`. + + The natural logarithm is a continuous function from above the branch cut, taking into account the sign of the imaginary component. As special cases involving complex floating-point operands should be handled according to ``exp(x2*log(x1))``, exponentiation has the same branch cut for ``x1`` as the natural logarithm (see :func:`~array_api.log`). + + *Note: branch cuts follow C99 and have provisional status* (see :ref:`branch-cuts`). + + Parameters + ---------- + x1: array + first input array whose elements correspond to the exponentiation base. Should have a numeric data type. + x2: array + second input array whose elements correspond to the exponentiation exponent. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a numeric data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If ``x1_i`` is not equal to ``1`` and ``x2_i`` is ``NaN``, the result is ``NaN``. + - If ``x2_i`` is ``+0``, the result is ``1``, even if ``x1_i`` is ``NaN``. + - If ``x2_i`` is ``-0``, the result is ``1``, even if ``x1_i`` is ``NaN``. + - If ``x1_i`` is ``NaN`` and ``x2_i`` is not equal to ``0``, the result is ``NaN``. + - If ``abs(x1_i)`` is greater than ``1`` and ``x2_i`` is ``+infinity``, the result is ``+infinity``. + - If ``abs(x1_i)`` is greater than ``1`` and ``x2_i`` is ``-infinity``, the result is ``+0``. + - If ``abs(x1_i)`` is ``1`` and ``x2_i`` is ``+infinity``, the result is ``1``. + - If ``abs(x1_i)`` is ``1`` and ``x2_i`` is ``-infinity``, the result is ``1``. + - If ``x1_i`` is ``1`` and ``x2_i`` is not ``NaN``, the result is ``1``. + - If ``abs(x1_i)`` is less than ``1`` and ``x2_i`` is ``+infinity``, the result is ``+0``. + - If ``abs(x1_i)`` is less than ``1`` and ``x2_i`` is ``-infinity``, the result is ``+infinity``. + - If ``x1_i`` is ``+infinity`` and ``x2_i`` is greater than ``0``, the result is ``+infinity``. + - If ``x1_i`` is ``+infinity`` and ``x2_i`` is less than ``0``, the result is ``+0``. + - If ``x1_i`` is ``-infinity``, ``x2_i`` is greater than ``0``, and ``x2_i`` is an odd integer value, the result is ``-infinity``. + - If ``x1_i`` is ``-infinity``, ``x2_i`` is greater than ``0``, and ``x2_i`` is not an odd integer value, the result is ``+infinity``. + - If ``x1_i`` is ``-infinity``, ``x2_i`` is less than ``0``, and ``x2_i`` is an odd integer value, the result is ``-0``. + - If ``x1_i`` is ``-infinity``, ``x2_i`` is less than ``0``, and ``x2_i`` is not an odd integer value, the result is ``+0``. + - If ``x1_i`` is ``+0`` and ``x2_i`` is greater than ``0``, the result is ``+0``. + - If ``x1_i`` is ``+0`` and ``x2_i`` is less than ``0``, the result is ``+infinity``. + - If ``x1_i`` is ``-0``, ``x2_i`` is greater than ``0``, and ``x2_i`` is an odd integer value, the result is ``-0``. + - If ``x1_i`` is ``-0``, ``x2_i`` is greater than ``0``, and ``x2_i`` is not an odd integer value, the result is ``+0``. + - If ``x1_i`` is ``-0``, ``x2_i`` is less than ``0``, and ``x2_i`` is an odd integer value, the result is ``-infinity``. + - If ``x1_i`` is ``-0``, ``x2_i`` is less than ``0``, and ``x2_i`` is not an odd integer value, the result is ``+infinity``. + - If ``x1_i`` is less than ``0``, ``x1_i`` is a finite number, ``x2_i`` is a finite number, and ``x2_i`` is not an integer value, the result is ``NaN``. + + For complex floating-point operands, special cases should be handled as if the operation is implemented as ``exp(x2*log(x1))``. + + .. note:: + Conforming implementations are allowed to treat special cases involving complex floating-point operands more carefully than as described in this specification. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def real(x: array, /) -> array: + """ + Returns the real component of a complex number for each element ``x_i`` of the input array ``x``. + + Parameters + ---------- + x: array + input array. Should have a complex floating-point data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a floating-point data type with the same floating-point precision as ``x`` (e.g., if ``x`` is ``complex64``, the returned array must have the floating-point data type ``float32``). + + Notes + ----- + + .. versionadded:: 2022.12 + """ + + +def remainder(x1: array, x2: array, /) -> array: + """ + Returns the remainder of division for each element ``x1_i`` of the input array ``x1`` and the respective element ``x2_i`` of the input array ``x2``. + + .. note:: + This function is equivalent to the Python modulus operator ``x1_i % x2_i``. + + .. note:: + For input arrays which promote to an integer data type, the result of division by zero is unspecified and thus implementation-defined. + + Parameters + ---------- + x1: array + dividend input array. Should have a real-valued data type. + x2: array + divisor input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a real-valued data type. + + Returns + ------- + out: array + an array containing the element-wise results. Each element-wise result must have the same sign as the respective element ``x2_i``. The returned array must have a data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + .. note:: + In general, similar to Python's ``%`` operator, this function is **not** recommended for floating-point operands as semantics do not follow IEEE 754. That this function is specified to accept floating-point operands is primarily for reasons of backward compatibility. + + For floating-point operands, + + - If either ``x1_i`` or ``x2_i`` is ``NaN``, the result is ``NaN``. + - If ``x1_i`` is either ``+infinity`` or ``-infinity`` and ``x2_i`` is either ``+infinity`` or ``-infinity``, the result is ``NaN``. + - If ``x1_i`` is either ``+0`` or ``-0`` and ``x2_i`` is either ``+0`` or ``-0``, the result is ``NaN``. + - If ``x1_i`` is ``+0`` and ``x2_i`` is greater than ``0``, the result is ``+0``. + - If ``x1_i`` is ``-0`` and ``x2_i`` is greater than ``0``, the result is ``+0``. + - If ``x1_i`` is ``+0`` and ``x2_i`` is less than ``0``, the result is ``-0``. + - If ``x1_i`` is ``-0`` and ``x2_i`` is less than ``0``, the result is ``-0``. + - If ``x1_i`` is greater than ``0`` and ``x2_i`` is ``+0``, the result is ``NaN``. + - If ``x1_i`` is greater than ``0`` and ``x2_i`` is ``-0``, the result is ``NaN``. + - If ``x1_i`` is less than ``0`` and ``x2_i`` is ``+0``, the result is ``NaN``. + - If ``x1_i`` is less than ``0`` and ``x2_i`` is ``-0``, the result is ``NaN``. + - If ``x1_i`` is ``+infinity`` and ``x2_i`` is a positive (i.e., greater than ``0``) finite number, the result is ``NaN``. + - If ``x1_i`` is ``+infinity`` and ``x2_i`` is a negative (i.e., less than ``0``) finite number, the result is ``NaN``. + - If ``x1_i`` is ``-infinity`` and ``x2_i`` is a positive (i.e., greater than ``0``) finite number, the result is ``NaN``. + - If ``x1_i`` is ``-infinity`` and ``x2_i`` is a negative (i.e., less than ``0``) finite number, the result is ``NaN``. + - If ``x1_i`` is a positive (i.e., greater than ``0``) finite number and ``x2_i`` is ``+infinity``, the result is ``x1_i``. (**note**: this result matches Python behavior.) + - If ``x1_i`` is a positive (i.e., greater than ``0``) finite number and ``x2_i`` is ``-infinity``, the result is ``x2_i``. (**note**: this result matches Python behavior.) + - If ``x1_i`` is a negative (i.e., less than ``0``) finite number and ``x2_i`` is ``+infinity``, the result is ``x2_i``. (**note**: this results matches Python behavior.) + - If ``x1_i`` is a negative (i.e., less than ``0``) finite number and ``x2_i`` is ``-infinity``, the result is ``x1_i``. (**note**: this result matches Python behavior.) + - In the remaining cases, the result must match that of the Python ``%`` operator. + """ + + +def round(x: array, /) -> array: + """ + Rounds each element ``x_i`` of the input array ``x`` to the nearest integer-valued number. + + .. note:: + For complex floating-point operands, real and imaginary components must be independently rounded to the nearest integer-valued number. + + Rounded real and imaginary components must be equal to their equivalent rounded real-valued floating-point counterparts (i.e., for complex-valued ``x``, ``real(round(x))`` must equal ``round(real(x)))`` and ``imag(round(x))`` must equal ``round(imag(x))``). + + Parameters + ---------- + x: array + input array. Should have a numeric data type. + + Returns + ------- + out: array + an array containing the rounded result for each element in ``x``. The returned array must have the same data type as ``x``. + + Notes + ----- + + **Special cases** + + .. note:: + For complex floating-point operands, the following special cases apply to real and imaginary components independently (e.g., if ``real(x_i)`` is ``NaN``, the rounded real component is ``NaN``). + + - If ``x_i`` is already integer-valued, the result is ``x_i``. + + For floating-point operands, + + - If ``x_i`` is ``+infinity``, the result is ``+infinity``. + - If ``x_i`` is ``-infinity``, the result is ``-infinity``. + - If ``x_i`` is ``+0``, the result is ``+0``. + - If ``x_i`` is ``-0``, the result is ``-0``. + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If two integers are equally close to ``x_i``, the result is the even integer closest to ``x_i``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def sign(x: array, /) -> array: + r""" + Returns an indication of the sign of a number for each element ``x_i`` of the input array ``x``. + + The sign function (also known as the **signum function**) of a number :math:`x_i` is defined as + + .. math:: + \operatorname{sign}(x_i) = \begin{cases} + 0 & \textrm{if } x_i = 0 \\ + \frac{x}{|x|} & \textrm{otherwise} + \end{cases} + + where :math:`|x_i|` is the absolute value of :math:`x_i`. + + Parameters + ---------- + x: array + input array. Should have a numeric data type. + + Returns + ------- + out: array + an array containing the evaluated result for each element in ``x``. The returned array must have the same data type as ``x``. + + Notes + ----- + + **Special cases** + + For real-valued operands, + + - If ``x_i`` is less than ``0``, the result is ``-1``. + - If ``x_i`` is either ``-0`` or ``+0``, the result is ``0``. + - If ``x_i`` is greater than ``0``, the result is ``+1``. + - If ``x_i`` is ``NaN``, the result is ``NaN``. + + For complex floating-point operands, let ``a = real(x_i)``, ``b = imag(x_i)``, and + + - If ``a`` is either ``-0`` or ``+0`` and ``b`` is either ``-0`` or ``+0``, the result is ``0 + 0j``. + - If ``a`` is ``NaN`` or ``b`` is ``NaN``, the result is ``NaN + NaN j``. + - In the remaining cases, special cases must be handled according to the rules of complex number division (see :func:`~array_api.divide`). + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def signbit(x: array, /) -> array: + r""" + Determines whether the sign bit is set for each element ``x_i`` of the input array ``x``. + + The sign bit of a real-valued floating-point number ``x_i`` is set whenever ``x_i`` is either ``-0``, less than zero, or a signed ``NaN`` (i.e., a ``NaN`` value whose sign bit is ``1``). + + Parameters + ---------- + x: array + input array. Should have a real-valued floating-point data type. + + Returns + ------- + out: array + an array containing the evaluated result for each element in ``x``. The returned array must have a data type of ``bool``. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If ``x_i`` is ``+0``, the result is ``False``. + - If ``x_i`` is ``-0``, the result is ``True``. + - If ``x_i`` is ``+infinity``, the result is ``False``. + - If ``x_i`` is ``-infinity``, the result is ``True``. + - If ``x_i`` is a positive (i.e., greater than ``0``) finite number, the result is ``False``. + - If ``x_i`` is a negative (i.e., less than ``0``) finite number, the result is ``True``. + - If ``x_i`` is ``NaN`` and the sign bit of ``x_i`` is ``0``, the result is ``False``. + - If ``x_i`` is ``NaN`` and the sign bit of ``x_i`` is ``1``, the result is ``True``. + + .. versionadded:: 2023.12 + """ + + +def sin(x: array, /) -> array: + r""" + Calculates an implementation-dependent approximation to the sine for each element ``x_i`` of the input array ``x``. + + Each element ``x_i`` is assumed to be expressed in radians. + + .. note:: + The sine is an entire function on the complex plane and has no branch cuts. + + .. note:: + For complex arguments, the mathematical definition of sine is + + .. math:: + \begin{align} \operatorname{sin}(x) &= \frac{e^{jx} - e^{-jx}}{2j} \\ &= \frac{\operatorname{sinh}(jx)}{j} \\ &= \frac{\operatorname{sinh}(jx)}{j} \cdot \frac{j}{j} \\ &= -j \cdot \operatorname{sinh}(jx) \end{align} + + where :math:`\operatorname{sinh}` is the hyperbolic sine. + + Parameters + ---------- + x: array + input array whose elements are each expressed in radians. Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the sine of each element in ``x``. The returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If ``x_i`` is ``+0``, the result is ``+0``. + - If ``x_i`` is ``-0``, the result is ``-0``. + - If ``x_i`` is either ``+infinity`` or ``-infinity``, the result is ``NaN``. + + For complex floating-point operands, special cases must be handled as if the operation is implemented as ``-1j * sinh(x*1j)``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def sinh(x: array, /) -> array: + r""" + Calculates an implementation-dependent approximation to the hyperbolic sine for each element ``x_i`` of the input array ``x``. + + The mathematical definition of the hyperbolic sine is + + .. math:: + \operatorname{sinh}(x) = \frac{e^x - e^{-x}}{2} + + .. note:: + The hyperbolic sine is an entire function in the complex plane and has no branch cuts. The function is periodic, with period :math:`2\pi j`, with respect to the imaginary component. + + Parameters + ---------- + x: array + input array whose elements each represent a hyperbolic angle. Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the hyperbolic sine of each element in ``x``. The returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + .. note:: + For all operands, ``sinh(x)`` must equal ``-sinh(-x)``. + + For real-valued floating-point operands, + + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If ``x_i`` is ``+0``, the result is ``+0``. + - If ``x_i`` is ``-0``, the result is ``-0``. + - If ``x_i`` is ``+infinity``, the result is ``+infinity``. + - If ``x_i`` is ``-infinity``, the result is ``-infinity``. + + For complex floating-point operands, let ``a = real(x_i)``, ``b = imag(x_i)``, and + + .. note:: + For complex floating-point operands, ``sinh(conj(x))`` must equal ``conj(sinh(x))``. + + - If ``a`` is ``+0`` and ``b`` is ``+0``, the result is ``+0 + 0j``. + - If ``a`` is ``+0`` and ``b`` is ``+infinity``, the result is ``0 + NaN j`` (sign of the real component is unspecified). + - If ``a`` is ``+0`` and ``b`` is ``NaN``, the result is ``0 + NaN j`` (sign of the real component is unspecified). + - If ``a`` is a positive (i.e., greater than ``0``) finite number and ``b`` is ``+infinity``, the result is ``NaN + NaN j``. + - If ``a`` is a positive (i.e., greater than ``0``) finite number and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + - If ``a`` is ``+infinity`` and ``b`` is ``+0``, the result is ``+infinity + 0j``. + - If ``a`` is ``+infinity`` and ``b`` is a positive finite number, the result is ``+infinity * cis(b)``. + - If ``a`` is ``+infinity`` and ``b`` is ``+infinity``, the result is ``infinity + NaN j`` (sign of the real component is unspecified). + - If ``a`` is ``+infinity`` and ``b`` is ``NaN``, the result is ``infinity + NaN j`` (sign of the real component is unspecified). + - If ``a`` is ``NaN`` and ``b`` is ``+0``, the result is ``NaN + 0j``. + - If ``a`` is ``NaN`` and ``b`` is a nonzero finite number, the result is ``NaN + NaN j``. + - If ``a`` is ``NaN`` and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + + where ``cis(v)`` is ``cos(v) + sin(v)*1j``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def square(x: array, /) -> array: + r""" + Squares each element ``x_i`` of the input array ``x``. + + The square of a number ``x_i`` is defined as + + .. math:: + x_i^2 = x_i \cdot x_i + + Parameters + ---------- + x: array + input array. Should have a numeric data type. + + Returns + ------- + out: array + an array containing the evaluated result for each element in ``x``. The returned array must have a data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For floating-point operands, special cases must be handled as if the operation is implemented as ``x * x`` (see :func:`~array_api.multiply`). + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def sqrt(x: array, /) -> array: + r""" + Calculates the principal square root for each element ``x_i`` of the input array ``x``. + + .. note:: + After rounding, each result must be indistinguishable from the infinitely precise result (as required by IEEE 754). + + .. note:: + For complex floating-point operands, ``sqrt(conj(x))`` must equal ``conj(sqrt(x))``. + + .. note:: + By convention, the branch cut of the square root is the negative real axis :math:`(-\infty, 0)`. + + The square root is a continuous function from above the branch cut, taking into account the sign of the imaginary component. + + Accordingly, for complex arguments, the function returns the square root in the range of the right half-plane, including the imaginary axis (i.e., the plane defined by :math:`[0, +\infty)` along the real axis and :math:`(-\infty, +\infty)` along the imaginary axis). + + *Note: branch cuts follow C99 and have provisional status* (see :ref:`branch-cuts`). + + Parameters + ---------- + x: array + input array. Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the square root of each element in ``x``. The returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If ``x_i`` is less than ``0``, the result is ``NaN``. + - If ``x_i`` is ``+0``, the result is ``+0``. + - If ``x_i`` is ``-0``, the result is ``-0``. + - If ``x_i`` is ``+infinity``, the result is ``+infinity``. + + For complex floating-point operands, let ``a = real(x_i)``, ``b = imag(x_i)``, and + + - If ``a`` is either ``+0`` or ``-0`` and ``b`` is ``+0``, the result is ``+0 + 0j``. + - If ``a`` is any value (including ``NaN``) and ``b`` is ``+infinity``, the result is ``+infinity + infinity j``. + - If ``a`` is a finite number and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + - If ``a`` ``-infinity`` and ``b`` is a positive (i.e., greater than ``0``) finite number, the result is ``NaN + NaN j``. + - If ``a`` is ``+infinity`` and ``b`` is a positive (i.e., greater than ``0``) finite number, the result is ``+0 + infinity j``. + - If ``a`` is ``-infinity`` and ``b`` is ``NaN``, the result is ``NaN + infinity j`` (sign of the imaginary component is unspecified). + - If ``a`` is ``+infinity`` and ``b`` is ``NaN``, the result is ``+infinity + NaN j``. + - If ``a`` is ``NaN`` and ``b`` is any value, the result is ``NaN + NaN j``. + - If ``a`` is ``NaN`` and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def subtract(x1: array, x2: array, /) -> array: + """ + Calculates the difference for each element ``x1_i`` of the input array ``x1`` with the respective element ``x2_i`` of the input array ``x2``. + + The result of ``x1_i - x2_i`` must be the same as ``x1_i + (-x2_i)`` and must be governed by the same floating-point rules as addition (see :meth:`add`). + + Parameters + ---------- + x1: array + first input array. Should have a numeric data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have a numeric data type. + + Returns + ------- + out: array + an array containing the element-wise differences. The returned array must have a data type determined by :ref:`type-promotion`. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def tan(x: array, /) -> array: + r""" + Calculates an implementation-dependent approximation to the tangent for each element ``x_i`` of the input array ``x``. + + Each element ``x_i`` is assumed to be expressed in radians. + + .. note:: + Tangent is an analytical function on the complex plane and has no branch cuts. The function is periodic, with period :math:`\pi j`, with respect to the real component and has first order poles along the real line at coordinates :math:`(\pi (\frac{1}{2} + n), 0)`. However, IEEE 754 binary floating-point representation cannot represent the value :math:`\pi / 2` exactly, and, thus, no argument value is possible for which a pole error occurs. + + .. note:: + For complex arguments, the mathematical definition of tangent is + + .. math:: + \begin{align} \operatorname{tan}(x) &= \frac{j(e^{-jx} - e^{jx})}{e^{-jx} + e^{jx}} \\ &= (-1) \frac{j(e^{jx} - e^{-jx})}{e^{jx} + e^{-jx}} \\ &= -j \cdot \operatorname{tanh}(jx) \end{align} + + where :math:`\operatorname{tanh}` is the hyperbolic tangent. + + Parameters + ---------- + x: array + input array whose elements are expressed in radians. Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the tangent of each element in ``x``. The returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If ``x_i`` is ``+0``, the result is ``+0``. + - If ``x_i`` is ``-0``, the result is ``-0``. + - If ``x_i`` is either ``+infinity`` or ``-infinity``, the result is ``NaN``. + + For complex floating-point operands, special cases must be handled as if the operation is implemented as ``-1j * tanh(x*1j)``. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def tanh(x: array, /) -> array: + r""" + Calculates an implementation-dependent approximation to the hyperbolic tangent for each element ``x_i`` of the input array ``x``. + + The mathematical definition of the hyperbolic tangent is + + .. math:: + \begin{align} \operatorname{tanh}(x) &= \frac{\operatorname{sinh}(x)}{\operatorname{cosh}(x)} \\ &= \frac{e^x - e^{-x}}{e^x + e^{-x}} \end{align} + + where :math:`\operatorname{sinh}(x)` is the hyperbolic sine and :math:`\operatorname{cosh}(x)` is the hyperbolic cosine. + + .. note:: + The hyperbolic tangent is an analytical function on the complex plane and has no branch cuts. The function is periodic, with period :math:`\pi j`, with respect to the imaginary component and has first order poles along the imaginary line at coordinates :math:`(0, \pi (\frac{1}{2} + n))`. However, IEEE 754 binary floating-point representation cannot represent :math:`\pi / 2` exactly, and, thus, no argument value is possible such that a pole error occurs. + + Parameters + ---------- + x: array + input array whose elements each represent a hyperbolic angle. Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the hyperbolic tangent of each element in ``x``. The returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + .. note:: + For all operands, ``tanh(-x)`` must equal ``-tanh(x)``. + + For real-valued floating-point operands, + + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If ``x_i`` is ``+0``, the result is ``+0``. + - If ``x_i`` is ``-0``, the result is ``-0``. + - If ``x_i`` is ``+infinity``, the result is ``+1``. + - If ``x_i`` is ``-infinity``, the result is ``-1``. + + For complex floating-point operands, let ``a = real(x_i)``, ``b = imag(x_i)``, and + + .. note:: + For complex floating-point operands, ``tanh(conj(x))`` must equal ``conj(tanh(x))``. + + - If ``a`` is ``+0`` and ``b`` is ``+0``, the result is ``+0 + 0j``. + - If ``a`` is a nonzero finite number and ``b`` is ``+infinity``, the result is ``NaN + NaN j``. + - If ``a`` is ``+0`` and ``b`` is ``+infinity``, the result is ``+0 + NaN j``. + - If ``a`` is a nonzero finite number and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + - If ``a`` is ``+0`` and ``b`` is ``NaN``, the result is ``+0 + NaN j``. + - If ``a`` is ``+infinity`` and ``b`` is a positive (i.e., greater than ``0``) finite number, the result is ``1 + 0j``. + - If ``a`` is ``+infinity`` and ``b`` is ``+infinity``, the result is ``1 + 0j`` (sign of the imaginary component is unspecified). + - If ``a`` is ``+infinity`` and ``b`` is ``NaN``, the result is ``1 + 0j`` (sign of the imaginary component is unspecified). + - If ``a`` is ``NaN`` and ``b`` is ``+0``, the result is ``NaN + 0j``. + - If ``a`` is ``NaN`` and ``b`` is a nonzero number, the result is ``NaN + NaN j``. + - If ``a`` is ``NaN`` and ``b`` is ``NaN``, the result is ``NaN + NaN j``. + + .. warning:: + For historical reasons stemming from the C standard, array libraries may not return the expected result when ``a`` is ``+0`` and ``b`` is either ``+infinity`` or ``NaN``. The result should be ``+0 + NaN j`` in both cases; however, for libraries compiled against older C versions, the result may be ``NaN + NaN j``. + + Array libraries are not required to patch these older C versions, and, thus, users are advised that results may vary across array library implementations for these special cases. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def trunc(x: array, /) -> array: + """ + Rounds each element ``x_i`` of the input array ``x`` to the nearest integer-valued number that is closer to zero than ``x_i``. + + Parameters + ---------- + x: array + input array. Should have a real-valued data type. + + Returns + ------- + out: array + an array containing the rounded result for each element in ``x``. The returned array must have the same data type as ``x``. + + Notes + ----- + + **Special cases** + + - If ``x_i`` is already integer-valued, the result is ``x_i``. + + For floating-point operands, + + - If ``x_i`` is ``+infinity``, the result is ``+infinity``. + - If ``x_i`` is ``-infinity``, the result is ``-infinity``. + - If ``x_i`` is ``+0``, the result is ``+0``. + - If ``x_i`` is ``-0``, the result is ``-0``. + - If ``x_i`` is ``NaN``, the result is ``NaN``. + """ diff --git a/src/array_api_stubs/_2023_12/fft.py b/src/array_api_stubs/_2023_12/fft.py new file mode 100644 index 000000000..4e8131c8b --- /dev/null +++ b/src/array_api_stubs/_2023_12/fft.py @@ -0,0 +1,683 @@ +__all__ = [ + "fft", + "ifft", + "fftn", + "ifftn", + "rfft", + "irfft", + "rfftn", + "irfftn", + "hfft", + "ihfft", + "fftfreq", + "rfftfreq", + "fftshift", + "ifftshift", +] + +from ._types import Tuple, Union, Sequence, array, Optional, Literal, device + + +def fft( + x: array, + /, + *, + n: Optional[int] = None, + axis: int = -1, + norm: Literal["backward", "ortho", "forward"] = "backward", +) -> array: + """ + Computes the one-dimensional discrete Fourier transform. + + .. note:: + Applying the one-dimensional inverse discrete Fourier transform to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``ifft(fft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (number of elements, axis, and normalization mode). + + Parameters + ---------- + x: array + input array. Should have a complex-valued floating-point data type. + n: Optional[int] + number of elements over which to compute the transform along the axis (dimension) specified by ``axis``. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``M``. + + - If ``n`` is greater than ``M``, the axis specified by ``axis`` must be zero-padded to size ``n``. + - If ``n`` is less than ``M``, the axis specified by ``axis`` must be trimmed to size ``n``. + - If ``n`` equals ``M``, all elements along the axis specified by ``axis`` must be used when computing the transform. + + Default: ``None``. + axis: int + axis (dimension) of the input array over which to compute the transform. A valid ``axis`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an ``axis`` is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). Default: ``-1``. + norm: Literal['backward', 'ortho', 'forward'] + normalization mode. Should be one of the following modes: + + - ``'backward'``: no normalization. + - ``'ortho'``: normalize by ``1/sqrt(n)`` (i.e., make the FFT orthonormal). + - ``'forward'``: normalize by ``1/n``. + + Default: ``'backward'``. + + Returns + ------- + out: array + an array transformed along the axis (dimension) specified by ``axis``. The returned array must have the same data type as ``x`` and must have the same shape as ``x``, except for the axis specified by ``axis`` which must have size ``n``. + + Notes + ----- + + .. versionadded:: 2022.12 + + .. versionchanged:: 2023.12 + Required the input array have a complex-valued floating-point data type and required that the output array have the same data type as the input array. + """ + + +def ifft( + x: array, + /, + *, + n: Optional[int] = None, + axis: int = -1, + norm: Literal["backward", "ortho", "forward"] = "backward", +) -> array: + """ + Computes the one-dimensional inverse discrete Fourier transform. + + .. note:: + Applying the one-dimensional inverse discrete Fourier transform to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``ifft(fft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (number of elements, axis, and normalization mode). + + Parameters + ---------- + x: array + input array. Should have a complex-valued floating-point data type. + n: Optional[int] + number of elements over which to compute the transform along the axis (dimension) specified by ``axis``. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``M``. + + - If ``n`` is greater than ``M``, the axis specified by ``axis`` must be zero-padded to size ``n``. + - If ``n`` is less than ``M``, the axis specified by ``axis`` must be trimmed to size ``n``. + - If ``n`` equals ``M``, all elements along the axis specified by ``axis`` must be used when computing the transform. + + Default: ``None``. + axis: int + axis (dimension) of the input array over which to compute the transform. A valid ``axis`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an ``axis`` is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). Default: ``-1``. + norm: Literal['backward', 'ortho', 'forward'] + normalization mode. Should be one of the following modes: + + - ``'backward'``: normalize by ``1/n``. + - ``'ortho'``: normalize by ``1/sqrt(n)`` (i.e., make the FFT orthonormal). + - ``'forward'``: no normalization. + + Default: ``'backward'``. + + Returns + ------- + out: array + an array transformed along the axis (dimension) specified by ``axis``. The returned array must have the same data type as ``x`` and must have the same shape as ``x``, except for the axis specified by ``axis`` which must have size ``n``. + + Notes + ----- + + .. versionadded:: 2022.12 + + .. versionchanged:: 2023.12 + Required the input array have a complex-valued floating-point data type and required that the output array have the same data type as the input array. + """ + + +def fftn( + x: array, + /, + *, + s: Optional[Sequence[int]] = None, + axes: Optional[Sequence[int]] = None, + norm: Literal["backward", "ortho", "forward"] = "backward", +) -> array: + """ + Computes the n-dimensional discrete Fourier transform. + + .. note:: + Applying the n-dimensional inverse discrete Fourier transform to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``ifftn(fftn(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (sizes, axes, and normalization mode). + + Parameters + ---------- + x: array + input array. Should have a complex-valued floating-point data type. + s: Optional[Sequence[int]] + number of elements over which to compute the transform along the axes (dimensions) specified by ``axes``. Let ``i`` be the index of the ``n``-th axis specified by ``axes`` (i.e., ``i = axes[n]``) and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers such that ``s[i]`` equals ``M[i]`` for all ``i``. + + - If ``s[i]`` is greater than ``M[i]``, axis ``i`` must be zero-padded to size ``s[i]``. + - If ``s[i]`` is less than ``M[i]``, axis ``i`` must be trimmed to size ``s[i]``. + - If ``s[i]`` equals ``M[i]`` or ``-1``, all elements along axis ``i`` must be used when computing the transform. + + If ``s`` is not ``None``, ``axes`` must not be ``None``. Default: ``None``. + axes: Optional[Sequence[int]] + axes (dimensions) over which to compute the transform. A valid axis in ``axes`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). + + If ``s`` is provided, the corresponding ``axes`` to be transformed must also be provided. If ``axes`` is ``None``, the function must compute the transform over all axes. Default: ``None``. + + If ``axes`` contains two or more entries which resolve to the same axis (i.e., resolved axes are not unique), the behavior is unspecified and thus implementation-defined. + norm: Literal['backward', 'ortho', 'forward'] + normalization mode. Should be one of the following modes: + + - ``'backward'``: no normalization. + - ``'ortho'``: normalize by ``1/sqrt(n)`` (i.e., make the FFT orthonormal). + - ``'forward'``: normalize by ``1/n``. + + where ``n = prod(s)`` is the logical FFT size. + + Default: ``'backward'``. + + Returns + ------- + out: array + an array transformed along the axes (dimensions) specified by ``axes``. The returned array must have the same data type as ``x`` and must have the same shape as ``x``, except for the axes specified by ``axes`` which must have size ``s[i]``. + + Notes + ----- + + .. versionadded:: 2022.12 + + .. versionchanged:: 2023.12 + Required the input array have a complex-valued floating-point data type and required that the output array have the same data type as the input array. + """ + + +def ifftn( + x: array, + /, + *, + s: Optional[Sequence[int]] = None, + axes: Optional[Sequence[int]] = None, + norm: Literal["backward", "ortho", "forward"] = "backward", +) -> array: + """ + Computes the n-dimensional inverse discrete Fourier transform. + + .. note:: + Applying the n-dimensional inverse discrete Fourier transform to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``ifftn(fftn(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (sizes, axes, and normalization mode). + + Parameters + ---------- + x: array + input array. Should have a complex-valued floating-point data type. + s: Optional[Sequence[int]] + number of elements over which to compute the transform along the axes (dimensions) specified by ``axes``. Let ``i`` be the index of the ``n``-th axis specified by ``axes`` (i.e., ``i = axes[n]``) and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers such that ``s[i]`` equals ``M[i]`` for all ``i``. + + - If ``s[i]`` is greater than ``M[i]``, axis ``i`` must be zero-padded to size ``s[i]``. + - If ``s[i]`` is less than ``M[i]``, axis ``i`` must be trimmed to size ``s[i]``. + - If ``s[i]`` equals ``M[i]`` or ``-1``, all elements along axis ``i`` must be used when computing the transform. + + If ``s`` is not ``None``, ``axes`` must not be ``None``. Default: ``None``. + axes: Optional[Sequence[int]] + axes (dimensions) over which to compute the transform. A valid axis in ``axes`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). + + If ``s`` is provided, the corresponding ``axes`` to be transformed must also be provided. If ``axes`` is ``None``, the function must compute the transform over all axes. Default: ``None``. + + If ``axes`` contains two or more entries which resolve to the same axis (i.e., resolved axes are not unique), the behavior is unspecified and thus implementation-defined. + norm: Literal['backward', 'ortho', 'forward'] + specify the normalization mode. Should be one of the following modes: + + - ``'backward'``: normalize by ``1/n``. + - ``'ortho'``: normalize by ``1/sqrt(n)`` (i.e., make the FFT orthonormal). + - ``'forward'``: no normalization. + + where ``n = prod(s)`` is the logical FFT size. + + Default: ``'backward'``. + + Returns + ------- + out: array + an array transformed along the axes (dimensions) specified by ``axes``. The returned array must have the same data type as ``x`` and must have the same shape as ``x``, except for the axes specified by ``axes`` which must have size ``s[i]``. + + Notes + ----- + + .. versionadded:: 2022.12 + + .. versionchanged:: 2023.12 + Required the input array have a complex-valued floating-point data type and required that the output array have the same data type as the input array. + """ + + +def rfft( + x: array, + /, + *, + n: Optional[int] = None, + axis: int = -1, + norm: Literal["backward", "ortho", "forward"] = "backward", +) -> array: + """ + Computes the one-dimensional discrete Fourier transform for real-valued input. + + .. note:: + Applying the one-dimensional inverse discrete Fourier transform for real-valued input to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``irfft(rfft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (axis and normalization mode) and consistent values for the number of elements over which to compute the transforms. + + Parameters + ---------- + x: array + input array. Must have a real-valued floating-point data type. + n: Optional[int] + number of elements over which to compute the transform along the axis (dimension) specified by ``axis``. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``M``. + + - If ``n`` is greater than ``M``, the axis specified by ``axis`` must be zero-padded to size ``n``. + - If ``n`` is less than ``M``, the axis specified by ``axis`` must be trimmed to size ``n``. + - If ``n`` equals ``M``, all elements along the axis specified by ``axis`` must be used when computing the transform. + + Default: ``None``. + axis: int + axis (dimension) of the input array over which to compute the transform. A valid ``axis`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an ``axis`` is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). Default: ``-1``. + norm: Literal['backward', 'ortho', 'forward'] + normalization mode. Should be one of the following modes: + + - ``'backward'``: no normalization. + - ``'ortho'``: normalize by ``1/sqrt(n)`` (i.e., make the FFT orthonormal). + - ``'forward'``: normalize by ``1/n``. + + Default: ``'backward'``. + + Returns + ------- + out: array + an array transformed along the axis (dimension) specified by ``axis``. The returned array must have a complex floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``float64``, then the returned array must have a ``complex128`` data type). The returned array must have the same shape as ``x``, except for the axis specified by ``axis`` which must have size ``n//2 + 1``. + + Notes + ----- + + .. versionadded:: 2022.12 + """ + + +def irfft( + x: array, + /, + *, + n: Optional[int] = None, + axis: int = -1, + norm: Literal["backward", "ortho", "forward"] = "backward", +) -> array: + """ + Computes the one-dimensional inverse of ``rfft`` for complex-valued input. + + .. note:: + Applying the one-dimensional inverse discrete Fourier transform for real-valued input to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``irfft(rfft(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (axis and normalization mode) and consistent values for the number of elements over which to compute the transforms. + + Parameters + ---------- + x: array + input array. Should have a complex-valued floating-point data type. + n: Optional[int] + number of elements along the transformed axis (dimension) specified by ``axis`` in the **output array**. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``2*(M-1)``. + + - If ``n//2+1`` is greater than ``M``, the axis of the input array specified by ``axis`` must be zero-padded to size ``n//2+1``. + - If ``n//2+1`` is less than ``M``, the axis of the input array specified by ``axis`` must be trimmed to size ``n//2+1``. + - If ``n//2+1`` equals ``M``, all elements along the axis of the input array specified by ``axis`` must be used when computing the transform. + + Default: ``None``. + axis: int + axis (dimension) of the input array over which to compute the transform. A valid ``axis`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an ``axis`` is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). Default: ``-1``. + norm: Literal['backward', 'ortho', 'forward'] + normalization mode. Should be one of the following modes: + + - ``'backward'``: normalize by ``1/n``. + - ``'ortho'``: normalize by ``1/sqrt(n)`` (i.e., make the FFT orthonormal). + - ``'forward'``: no normalization. + + Default: ``'backward'``. + + Returns + ------- + out: array + an array transformed along the axis (dimension) specified by ``axis``. The returned array must have a real-valued floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``complex128``, then the returned array must have a ``float64`` data type). The returned array must have the same shape as ``x``, except for the axis specified by ``axis`` which must have size ``n``. + + Notes + ----- + + - In order to return an array having an odd number of elements along the transformed axis, the function must be provided an odd integer for ``n``. + + .. versionadded:: 2022.12 + + .. versionchanged:: 2023.12 + Required the output array have a real-valued floating-point data type having the same precision as the input array. + """ + + +def rfftn( + x: array, + /, + *, + s: Optional[Sequence[int]] = None, + axes: Optional[Sequence[int]] = None, + norm: Literal["backward", "ortho", "forward"] = "backward", +) -> array: + """ + Computes the n-dimensional discrete Fourier transform for real-valued input. + + .. note:: + Applying the n-dimensional inverse discrete Fourier transform for real-valued input to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``irfftn(rfftn(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (axes and normalization mode) and consistent sizes. + + Parameters + ---------- + x: array + input array. Must have a real-valued floating-point data type. + s: Optional[Sequence[int]] + number of elements over which to compute the transform along axes (dimensions) specified by ``axes``. Let ``i`` be the index of the ``n``-th axis specified by ``axes`` (i.e., ``i = axes[n]``) and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers such that ``s[i]`` equals ``M[i]`` for all ``i``. + + - If ``s[i]`` is greater than ``M[i]``, axis ``i`` must be zero-padded to size ``s[i]``. + - If ``s[i]`` is less than ``M[i]``, axis ``i`` must be trimmed to size ``s[i]``. + - If ``s[i]`` equals ``M[i]`` or ``-1``, all elements along axis ``i`` must be used when computing the transform. + + If ``s`` is not ``None``, ``axes`` must not be ``None``. Default: ``None``. + axes: Optional[Sequence[int]] + axes (dimensions) over which to compute the transform. A valid axis in ``axes`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). + + If ``s`` is provided, the corresponding ``axes`` to be transformed must also be provided. If ``axes`` is ``None``, the function must compute the transform over all axes. Default: ``None``. + + If ``axes`` contains two or more entries which resolve to the same axis (i.e., resolved axes are not unique), the behavior is unspecified and thus implementation-defined. + norm: Literal['backward', 'ortho', 'forward'] + normalization mode. Should be one of the following modes: + + - ``'backward'``: no normalization. + - ``'ortho'``: normalize by ``1/sqrt(n)`` (i.e., make the FFT orthonormal). + - ``'forward'``: normalize by ``1/n``. + + where ``n = prod(s)``, the logical FFT size. + + Default: ``'backward'``. + + Returns + ------- + out: array + an array transformed along the axes (dimension) specified by ``axes``. The returned array must have a complex floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``float64``, then the returned array must have a ``complex128`` data type). The returned array must have the same shape as ``x``, except for the last transformed axis which must have size ``s[-1]//2 + 1`` and the remaining transformed axes which must have size ``s[i]``. + + Notes + ----- + + .. versionadded:: 2022.12 + """ + + +def irfftn( + x: array, + /, + *, + s: Optional[Sequence[int]] = None, + axes: Optional[Sequence[int]] = None, + norm: Literal["backward", "ortho", "forward"] = "backward", +) -> array: + """ + Computes the n-dimensional inverse of ``rfftn`` for complex-valued input. + + .. note:: + Applying the n-dimensional inverse discrete Fourier transform for real-valued input to the output of this function must return the original (i.e., non-transformed) input array within numerical accuracy (i.e., ``irfftn(rfftn(x)) == x``), provided that the transform and inverse transform are performed with the same arguments (axes and normalization mode) and consistent sizes. + + Parameters + ---------- + x: array + input array. Should have a complex-valued floating-point data type. + s: Optional[Sequence[int]] + number of elements along the transformed axes (dimensions) specified by ``axes`` in the **output array**. Let ``i`` be the index of the ``n``-th axis specified by ``axes`` (i.e., ``i = axes[n]``) and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers such that ``s[i]`` equals ``M[i]`` for all ``i``, except for the last transformed axis in which ``s[i]`` equals ``2*(M[i]-1)``. For each ``i``, let ``n`` equal ``s[i]``, except for the last transformed axis in which ``n`` equals ``s[i]//2+1``. + + - If ``n`` is greater than ``M[i]``, axis ``i`` of the input array must be zero-padded to size ``n``. + - If ``n`` is less than ``M[i]``, axis ``i`` of the input array must be trimmed to size ``n``. + - If ``n`` equals ``M[i]`` or ``-1``, all elements along axis ``i`` of the input array must be used when computing the transform. + + If ``s`` is not ``None``, ``axes`` must not be ``None``. Default: ``None``. + axes: Optional[Sequence[int]] + axes (dimensions) over which to compute the transform. A valid axis in ``axes`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an axis is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). + + If ``s`` is provided, the corresponding ``axes`` to be transformed must also be provided. If ``axes`` is ``None``, the function must compute the transform over all axes. Default: ``None``. + + If ``axes`` contains two or more entries which resolve to the same axis (i.e., resolved axes are not unique), the behavior is unspecified and thus implementation-defined. + norm: Literal['backward', 'ortho', 'forward'] + normalization mode. Should be one of the following modes: + + - ``'backward'``: normalize by ``1/n``. + - ``'ortho'``: normalize by ``1/sqrt(n)`` (i.e., make the FFT orthonormal). + - ``'forward'``: no normalization. + + where ``n = prod(s)`` is the logical FFT size. + + Default: ``'backward'``. + + Returns + ------- + out: array + an array transformed along the axes (dimension) specified by ``axes``. The returned array must have a real-valued floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``complex128``, then the returned array must have a ``float64`` data type). The returned array must have the same shape as ``x``, except for the transformed axes which must have size ``s[i]``. + + Notes + ----- + + - In order to return an array having an odd number of elements along the last transformed axis, the function must be provided an odd integer for ``s[-1]``. + + .. versionadded:: 2022.12 + + .. versionchanged:: 2023.12 + Required the output array have a real-valued floating-point data type having the same precision as the input array. + """ + + +def hfft( + x: array, + /, + *, + n: Optional[int] = None, + axis: int = -1, + norm: Literal["backward", "ortho", "forward"] = "backward", +) -> array: + """ + Computes the one-dimensional discrete Fourier transform of a signal with Hermitian symmetry. + + Parameters + ---------- + x: array + input array. Should have a complex-valued floating-point data type. + n: Optional[int] + number of elements along the transformed axis (dimension) specified by ``axis`` in the **output array**. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``2*(M-1)``. + + - If ``n//2+1`` is greater than ``M``, the axis of the input array specified by ``axis`` must be zero-padded to length ``n//2+1``. + - If ``n//2+1`` is less than ``M``, the axis of the input array specified by ``axis`` must be trimmed to size ``n//2+1``. + - If ``n//2+1`` equals ``M``, all elements along the axis of the input array specified by ``axis`` must be used when computing the transform. + + Default: ``None``. + axis: int + axis (dimension) of the input array over which to compute the transform. A valid ``axis`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an ``axis`` is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). Default: ``-1``. + norm: Literal['backward', 'ortho', 'forward'] + normalization mode. Should be one of the following modes: + + - ``'backward'``: no normalization. + - ``'ortho'``: normalize by ``1/sqrt(n)`` (i.e., make the FFT orthonormal). + - ``'forward'``: normalize by ``1/n``. + + Default: ``'backward'``. + + Returns + ------- + out: array + an array transformed along the axis (dimension) specified by ``axis``. The returned array must have a real-valued floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``complex128``, then the returned array must have a ``float64`` data type). The returned array must have the same shape as ``x``, except for the axis specified by ``axis`` which must have size ``n``. + + Notes + ----- + + .. versionadded:: 2022.12 + + .. versionchanged:: 2023.12 + Required the input array to have a complex-valued floating-point data type and required that the output array have a real-valued data type having the same precision as the input array. + """ + + +def ihfft( + x: array, + /, + *, + n: Optional[int] = None, + axis: int = -1, + norm: Literal["backward", "ortho", "forward"] = "backward", +) -> array: + """ + Computes the one-dimensional inverse discrete Fourier transform of a signal with Hermitian symmetry. + + Parameters + ---------- + x: array + input array. Must have a real-valued floating-point data type. + n: Optional[int] + number of elements over which to compute the transform along the axis (dimension) specified by ``axis``. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``M``. + + - If ``n`` is greater than ``M``, the axis specified by ``axis`` must be zero-padded to size ``n``. + - If ``n`` is less than ``M``, the axis specified by ``axis`` must be trimmed to size ``n``. + - If ``n`` equals ``M``, all elements along the axis specified by ``axis`` must be used when computing the transform. + + Default: ``None``. + axis: int + axis (dimension) of the input array over which to compute the transform. A valid ``axis`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an ``axis`` is specified as a negative integer, the function must determine the axis along which to compute the transform by counting backward from the last dimension (where ``-1`` refers to the last dimension). Default: ``-1``. + norm: Literal['backward', 'ortho', 'forward'] + normalization mode. Should be one of the following modes: + + - ``'backward'``: normalize by ``1/n``. + - ``'ortho'``: normalize by ``1/sqrt(n)`` (i.e., make the FFT orthonormal). + - ``'forward'``: no normalization. + + Default: ``'backward'``. + + Returns + ------- + out: array + an array transformed along the axis (dimension) specified by ``axis``. The returned array must have a complex floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``float64``, then the returned array must have a ``complex128`` data type). The returned array must have the same shape as ``x``, except for the axis specified by ``axis`` which must have size ``n//2 + 1``. + + Notes + ----- + + .. versionadded:: 2022.12 + """ + + +def fftfreq(n: int, /, *, d: float = 1.0, device: Optional[device] = None) -> array: + """ + Computes the discrete Fourier transform sample frequencies. + + For a Fourier transform of length ``n`` and length unit of ``d``, the frequencies are described as: + + .. code-block:: + + f = [0, 1, ..., n/2-1, -n/2, ..., -1] / (d*n) # if n is even + f = [0, 1, ..., (n-1)/2, -(n-1)/2, ..., -1] / (d*n) # if n is odd + + Parameters + ---------- + n: int + window length. + d: float + sample spacing between individual samples of the Fourier transform input. Default: ``1.0``. + device: Optional[device] + device on which to place the created array. Default: ``None``. + + Returns + ------- + out: array + an array of shape ``(n,)`` containing the sample frequencies. The returned array must have the default real-valued floating-point data type. + + Notes + ----- + + .. versionadded:: 2022.12 + + .. versionchanged:: 2023.12 + Required the output array have the default real-valued floating-point data type. + """ + + +def rfftfreq(n: int, /, *, d: float = 1.0, device: Optional[device] = None) -> array: + """ + Computes the discrete Fourier transform sample frequencies (for ``rfft`` and ``irfft``). + + For a Fourier transform of length ``n`` and length unit of ``d``, the frequencies are described as: + + .. code-block:: + + f = [0, 1, ..., n/2-1, n/2] / (d*n) # if n is even + f = [0, 1, ..., (n-1)/2-1, (n-1)/2] / (d*n) # if n is odd + + The Nyquist frequency component is considered to be positive. + + Parameters + ---------- + n: int + window length. + d: float + sample spacing between individual samples of the Fourier transform input. Default: ``1.0``. + device: Optional[device] + device on which to place the created array. Default: ``None``. + + Returns + ------- + out: array + an array of shape ``(n//2+1,)`` containing the sample frequencies. The returned array must have the default real-valued floating-point data type. + + Notes + ----- + + .. versionadded:: 2022.12 + + .. versionchanged:: 2023.12 + Required the output array have the default real-valued floating-point data type. + """ + + +def fftshift(x: array, /, *, axes: Optional[Union[int, Sequence[int]]] = None) -> array: + """ + Shifts the zero-frequency component to the center of the spectrum. + + This function swaps half-spaces for all axes (dimensions) specified by ``axes``. + + .. note:: + ``out[0]`` is the Nyquist component only if the length of the input is even. + + Parameters + ---------- + x: array + input array. Should have a floating-point data type. + axes: Optional[Union[int, Sequence[int]]] + axes over which to shift. If ``None``, the function must shift all axes. Default: ``None``. + + If ``axes`` contains two or more entries which resolve to the same axis (i.e., resolved axes are not unique), the behavior is unspecified and thus implementation-defined. + + Returns + ------- + out: array + the shifted array. The returned array must have the same data type and shape as ``x``. + + Notes + ----- + + .. versionadded:: 2022.12 + """ + + +def ifftshift( + x: array, /, *, axes: Optional[Union[int, Sequence[int]]] = None +) -> array: + """ + Inverse of ``fftshift``. + + .. note:: + Although identical for even-length ``x``, ``fftshift`` and ``ifftshift`` differ by one sample for odd-length ``x``. + + Parameters + ---------- + x: array + input array. Should have a floating-point data type. + axes: Optional[Union[int, Sequence[int]]] + axes over which to perform the inverse shift. If ``None``, the function must shift all axes. Default: ``None``. + + If ``axes`` contains two or more entries which resolve to the same axis (i.e., resolved axes are not unique), the behavior is unspecified and thus implementation-defined. + + Returns + ------- + out: array + the shifted array. The returned array must have the same data type and shape as ``x``. + + Notes + ----- + + .. versionadded:: 2022.12 + """ diff --git a/src/array_api_stubs/_2023_12/indexing_functions.py b/src/array_api_stubs/_2023_12/indexing_functions.py new file mode 100644 index 000000000..35066a4a2 --- /dev/null +++ b/src/array_api_stubs/_2023_12/indexing_functions.py @@ -0,0 +1,40 @@ +__all__ = ["take"] + +from ._types import Union, Optional, array + + +def take(x: array, indices: array, /, *, axis: Optional[int] = None) -> array: + """ + Returns elements of an array along an axis. + + .. note:: + Conceptually, ``take(x, indices, axis=3)`` is equivalent to ``x[:,:,:,indices,...]``; however, explicit indexing via arrays of indices is not currently supported in this specification due to concerns regarding ``__setitem__`` and array mutation semantics. + + Parameters + ---------- + x: array + input array. + indices: array + array indices. The array must be one-dimensional and have an integer data type. + + .. note:: + This specification does not require bounds checking. The behavior for out-of-bounds indices is left unspecified. + + axis: Optional[int] + axis over which to select values. If ``axis`` is negative, the function must determine the axis along which to select values by counting from the last dimension. + + If ``x`` is a one-dimensional array, providing an ``axis`` is optional; however, if ``x`` has more than one dimension, providing an ``axis`` is required. + + Returns + ------- + out: array + an array having the same data type as ``x``. The output array must have the same rank (i.e., number of dimensions) as ``x`` and must have the same shape as ``x``, except for the axis specified by ``axis`` whose size must equal the number of elements in ``indices``. + + Notes + ----- + + .. versionadded:: 2022.12 + + .. versionchanged:: 2023.12 + Out-of-bounds behavior is explicitly left unspecified. + """ diff --git a/src/array_api_stubs/_2023_12/info.py b/src/array_api_stubs/_2023_12/info.py new file mode 100644 index 000000000..b755ca2c0 --- /dev/null +++ b/src/array_api_stubs/_2023_12/info.py @@ -0,0 +1,197 @@ +__all__ = [ + "__array_namespace_info__", + "capabilities", + "default_device", + "default_dtypes", + "devices", + "dtypes", +] + +from ._types import ( + Optional, + Union, + Tuple, + List, + device, + dtype, + DefaultDataTypes, + DataTypes, + Capabilities, + Info, +) + + +def __array_namespace_info__() -> Info: + """ + Returns a namespace with Array API namespace inspection utilities. + + Returns + ------- + out: Info + An object containing Array API namespace inspection utilities. + + Notes + ----- + + The returned object may be either a namespace or a class, so long as an Array API user can access inspection utilities as follows: + + :: + + info = xp.__array_namespace_info__() + info.capabilities() + info.devices() + info.dtypes() + info.default_dtypes() + # ... + + .. versionadded: 2023.12 + """ + + +def capabilities() -> Capabilities: + """ + Returns a dictionary of array library capabilities. + + The dictionary must contain the following keys: + + - `"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``. + - `"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``. + + Returns + ------- + out: Capabilities + a dictionary of array library capabilities. + + Notes + ----- + + .. versionadded: 2023.12 + """ + + +def default_device() -> device: + """ + Returns the default device. + + Returns + ------- + out: device + an object corresponding to the default device. + + Notes + ----- + + .. versionadded: 2023.12 + """ + + +def default_dtypes( + *, + device: Optional[device] = None, +) -> DefaultDataTypes: + """ + Returns a dictionary containing default data types. + + The dictionary must have the following keys: + + - `"real floating"`: default real floating-point data type. + - `"complex floating"`: default complex floating-point data type. + - `"integral"`: default integral data type. + - `"indexing"`: default array index data type. + + Dictionary values must be the corresponding data type object. + + Parameters + ---------- + device: Optional[device] + 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``. + + .. note:: + 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. + + Returns + ------- + out: DefaultDataTypes + a dictionary containing the default data type for respective data type kinds. + + Notes + ----- + + .. versionadded: 2023.12 + """ + + +def dtypes( + *, + device: Optional[device] = None, + kind: Optional[Union[str, Tuple[str, ...]]] = None, +) -> DataTypes: + """ + Returns a dictionary of supported *Array API* data types. + + .. note:: + 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. + + .. note:: + 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. + + Parameters + ---------- + kind: Optional[Union[str, Tuple[str, ...]]] + data type kind. + + - If ``kind`` is ``None``, the function must return a dictionary containing all supported Array API data types. + + - 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: + + - ``'bool'``: boolean data types (e.g., ``bool``). + - ``'signed integer'``: signed integer data types (e.g., ``int8``, ``int16``, ``int32``, ``int64``). + - ``'unsigned integer'``: unsigned integer data types (e.g., ``uint8``, ``uint16``, ``uint32``, ``uint64``). + - ``'integral'``: integer data types. Shorthand for ``('signed integer', 'unsigned integer')``. + - ``'real floating'``: real-valued floating-point data types (e.g., ``float32``, ``float64``). + - ``'complex floating'``: complex floating-point data types (e.g., ``complex64``, ``complex128``). + - ``'numeric'``: numeric data types. Shorthand for ``('integral', 'real floating', 'complex floating')``. + + - 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. + + Default: ``None``. + device: Optional[device] + 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``. + + .. note:: + 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. + + Returns + ------- + out: DataTypes + a dictionary containing supported data types. + + .. note:: + Dictionary keys must only consist of canonical names as defined in :ref:`data-types`. + + Notes + ----- + + .. versionadded: 2023.12 + """ + + +def devices() -> List[device]: + """ + Returns a list of supported devices which are available at runtime. + + Returns + ------- + out: List[device] + a list of supported devices. + + Notes + ----- + + 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. + + Notes + ----- + + .. versionadded: 2023.12 + """ diff --git a/src/array_api_stubs/_2023_12/linalg.py b/src/array_api_stubs/_2023_12/linalg.py new file mode 100644 index 000000000..0950e6937 --- /dev/null +++ b/src/array_api_stubs/_2023_12/linalg.py @@ -0,0 +1,850 @@ +__all__ = [ + "cholesky", + "cross", + "det", + "diagonal", + "eigh", + "eigvalsh", + "inv", + "matmul", + "matrix_norm", + "matrix_power", + "matrix_rank", + "matrix_transpose", + "outer", + "pinv", + "qr", + "slogdet", + "solve", + "svd", + "svdvals", + "tensordot", + "trace", + "vecdot", + "vector_norm", +] + + +from ._types import Literal, Optional, Tuple, Union, Sequence, array, dtype +from .constants import inf + + +def cholesky(x: array, /, *, upper: bool = False) -> array: + r""" + Returns the lower (upper) Cholesky decomposition of a complex Hermitian or real symmetric positive-definite matrix ``x``. + + If ``x`` is real-valued, let :math:`\mathbb{K}` be the set of real numbers $\mathbb{R}$, and, if ``x`` is complex-valued, let $\mathbb{K}$ be the set of complex numbers $\mathbb{C}$. + + The lower **Cholesky decomposition** of a complex Hermitian or real symmetric positive-definite matrix :math:`x \in\ \mathbb{K}^{n \times n}` is defined as + + .. math:: + x = LL^{H} \qquad \text{L $\in\ \mathbb{K}^{n \times n}$} + + where :math:`L` is a lower triangular matrix and :math:`L^{H}` is the conjugate transpose when :math:`L` is complex-valued and the transpose when :math:`L` is real-valued. + + The upper Cholesky decomposition is defined similarly + + .. math:: + x = U^{H}U \qquad \text{U $\in\ \mathbb{K}^{n \times n}$} + + where :math:`U` is an upper triangular matrix. + + When ``x`` is a stack of matrices, the function must compute the Cholesky decomposition for each matrix in the stack. + + .. note:: + Whether an array library explicitly checks whether an input array is Hermitian or a symmetric positive-definite matrix (or a stack of matrices) is implementation-defined. + + Parameters + ---------- + x: array + input array having shape ``(..., M, M)`` and whose innermost two dimensions form square complex Hermitian or real symmetric positive-definite matrices. Should have a floating-point data type. + upper: bool + If ``True``, the result must be the upper-triangular Cholesky factor :math:`U`. If ``False``, the result must be the lower-triangular Cholesky factor :math:`L`. Default: ``False``. + + Returns + ------- + out: array + an array containing the Cholesky factors for each square matrix. If ``upper`` is ``False``, the returned array must contain lower-triangular matrices; otherwise, the returned array must contain upper-triangular matrices. The returned array must have a floating-point data type determined by :ref:`type-promotion` and must have the same shape as ``x``. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def cross(x1: array, x2: array, /, *, axis: int = -1) -> array: + """ + Returns the cross product of 3-element vectors. + + If ``x1`` and/or ``x2`` are multi-dimensional arrays (i.e., the broadcasted result has a rank greater than ``1``), then the cross-product of each pair of corresponding 3-element vectors is independently computed. + + Parameters + ---------- + x1: array + first input array. Must have a numeric data type. The size of the axis over which the cross product is to be computed must be equal to 3. + x2: array + second input array. Must be broadcast compatible with ``x1`` along all axes other than the axis along which the cross-product is computed (see :ref:`broadcasting`). The size of the axis over which the cross product is to be computed must be equal to 3. Must have a numeric data type. + + .. note:: + The compute axis (dimension) must not be broadcasted. + + axis: int + the axis (dimension) of ``x1`` and ``x2`` containing the vectors for which to compute the cross product. Should be an integer on the interval ``[-N, -1]``, where ``N`` is ``min(x1.ndim, x2.ndim)``. The function must determine the axis along which to compute the cross product by counting backward from the last dimension (where ``-1`` refers to the last dimension). By default, the function must compute the cross product over the last axis. Default: ``-1``. + + Returns + ------- + out: array + an array containing the cross products. The returned array must have a data type determined by :ref:`type-promotion`. + + + Notes + ----- + + **Raises** + + - if the size of the axis over which to compute the cross product is not equal to ``3`` (before broadcasting) for both ``x1`` and ``x2``. + + .. versionchanged:: 2022.12 + Added support for broadcasting. + + .. versionchanged:: 2022.12 + Added complex data type support. + + .. versionchanged:: 2023.12 + Restricted broadcasting to only non-compute axes and required that ``axis`` be a negative integer. + """ + + +def det(x: array, /) -> array: + """ + Returns the determinant of a square matrix (or a stack of square matrices) ``x``. + + Parameters + ---------- + x: array + input array having shape ``(..., M, M)`` and whose innermost two dimensions form square matrices. Should have a floating-point data type. + + Returns + ------- + out: array + if ``x`` is a two-dimensional array, a zero-dimensional array containing the determinant; otherwise, a non-zero dimensional array containing the determinant for each square matrix. The returned array must have the same data type as ``x``. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def diagonal(x: array, /, *, offset: int = 0) -> array: + """ + Returns the specified diagonals of a matrix (or a stack of matrices) ``x``. + + Parameters + ---------- + x: array + input array having shape ``(..., M, N)`` and whose innermost two dimensions form ``MxN`` matrices. + offset: int + offset specifying the off-diagonal relative to the main diagonal. + + - ``offset = 0``: the main diagonal. + - ``offset > 0``: off-diagonal above the main diagonal. + - ``offset < 0``: off-diagonal below the main diagonal. + + Default: `0`. + + Returns + ------- + out: array + an array containing the diagonals and whose shape is determined by removing the last two dimensions and appending a dimension equal to the size of the resulting diagonals. The returned array must have the same data type as ``x``. + """ + + +def eigh(x: array, /) -> Tuple[array]: + r""" + Returns an eigenvalue decomposition of a complex Hermitian or real symmetric matrix (or a stack of matrices) ``x``. + + If ``x`` is real-valued, let :math:`\mathbb{K}` be the set of real numbers :math:`\mathbb{R}`, and, if ``x`` is complex-valued, let :math:`\mathbb{K}` be the set of complex numbers :math:`\mathbb{C}`. + + The **eigenvalue decomposition** of a complex Hermitian or real symmetric matrix :math:`x \in\ \mathbb{K}^{n \times n}` is defined as + + .. math:: + x = Q \Lambda Q^H + + with :math:`Q \in \mathbb{K}^{n \times n}` and :math:`\Lambda \in \mathbb{R}^n` and where :math:`Q^H` is the conjugate transpose when :math:`Q` is complex and the transpose when :math:`Q` is real-valued and :math:`\Lambda` is a diagonal matrix whose diagonal elements are the corresponding eigenvalues. When ``x`` is real-valued, :math:`Q` is orthogonal, and, when ``x`` is complex, :math:`Q` is unitary. + + .. note:: + The eigenvalues of a complex Hermitian or real symmetric matrix are always real. + + .. warning:: + The eigenvectors of a symmetric matrix are not unique and are not continuous with respect to ``x``. Because eigenvectors are not unique, different hardware and software may compute different eigenvectors. + + Non-uniqueness stems from the fact that multiplying an eigenvector by :math:`-1` when ``x`` is real-valued and by :math:`e^{\phi j}` (:math:`\phi \in \mathbb{R}`) when ``x`` is complex produces another set of valid eigenvectors. + + .. note:: + Whether an array library explicitly checks whether an input array is Hermitian or a symmetric matrix (or a stack of matrices) is implementation-defined. + + .. note:: + The function ``eig`` will be added in a future version of the specification. + + Parameters + ---------- + x: array + input array having shape ``(..., M, M)`` and whose innermost two dimensions form square matrices. Should have a floating-point data type. + + Returns + ------- + out: Tuple[array] + a namedtuple (``eigenvalues``, ``eigenvectors``) whose + + - first element must have the field name ``eigenvalues`` (corresponding to :math:`\operatorname{diag}\Lambda` above) and must be an array consisting of computed eigenvalues. The array containing the eigenvalues must have shape ``(..., M)`` and must have a real-valued floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``complex128``, then ``eigenvalues`` must be ``float64``). + - second element have have the field name ``eigenvectors`` (corresponding to :math:`Q` above) and must be an array where the columns of the inner most matrices contain the computed eigenvectors. These matrices must be orthogonal. The array containing the eigenvectors must have shape ``(..., M, M)`` and must have the same data type as ``x``. + + Notes + ----- + + .. note:: + Eigenvalue sort order is left unspecified and is thus implementation-dependent. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def eigvalsh(x: array, /) -> array: + r""" + Returns the eigenvalues of a complex Hermitian or real symmetric matrix (or a stack of matrices) ``x``. + + If ``x`` is real-valued, let :math:`\mathbb{K}` be the set of real numbers :math:`\mathbb{R}`, and, if ``x`` is complex-valued, let :math:`\mathbb{K}` be the set of complex numbers :math:`\mathbb{C}`. + + The **eigenvalues** of a complex Hermitian or real symmetric matrix :math:`x \in\ \mathbb{K}^{n \times n}` are defined as the roots (counted with multiplicity) of the polynomial :math:`p` of degree :math:`n` given by + + .. math:: + p(\lambda) = \operatorname{det}(x - \lambda I_n) + + where :math:`\lambda \in \mathbb{R}` and where :math:`I_n` is the *n*-dimensional identity matrix. + + .. note:; + The eigenvalues of a complex Hermitian or real symmetric matrix are always real. + + .. note:: + Whether an array library explicitly checks whether an input array is Hermitian or a symmetric matrix (or a stack of matrices) is implementation-defined. + + .. note:: + The function ``eigvals`` will be added in a future version of the specification. + + Parameters + ---------- + x: array + input array having shape ``(..., M, M)`` and whose innermost two dimensions form square matrices. Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the computed eigenvalues. The returned array must have shape ``(..., M)`` and have a real-valued floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``complex128``, then must have a ``float64`` data type). + + Notes + ----- + + .. note:: + Eigenvalue sort order is left unspecified and is thus implementation-dependent. + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def inv(x: array, /) -> array: + r""" + Returns the multiplicative inverse of a square matrix (or a stack of square matrices) ``x``. + + If ``x`` is real-valued, let :math:`\mathbb{K}` be the set of real numbers :math:`\mathbb{R}`, and, if ``x`` is complex-valued, let :math:`\mathbb{K}` be the set of complex numbers :math:`\mathbb{C}`. + + The **inverse matrix** :math:`x^{-1} \in\ \mathbb{K}^{n \times n}` of a square matrix :math:`x \in\ \mathbb{K}^{n \times n}` is defined as + + .. math:: + x^{-1}x = xx^{-1} = I_n + + where :math:`I_n` is the *n*-dimensional identity matrix. + + The inverse matrix exists if and only if ``x`` is invertible. When ``x`` is invertible, the inverse is unique. + + When ``x`` is a stack of matrices, the function must compute the inverse for each matrix in the stack. + + Parameters + ---------- + x: array + input array having shape ``(..., M, M)`` and whose innermost two dimensions form square matrices. Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the multiplicative inverses. The returned array must have a floating-point data type determined by :ref:`type-promotion` and must have the same shape as ``x``. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def matmul(x1: array, x2: array, /) -> array: + """Alias for :func:`~array_api.matmul`.""" + + +def matrix_norm( + x: array, + /, + *, + keepdims: bool = False, + ord: Optional[Union[int, float, Literal[inf, -inf, "fro", "nuc"]]] = "fro", +) -> array: + """ + Computes the matrix norm of a matrix (or a stack of matrices) ``x``. + + Parameters + ---------- + x: array + input array having shape ``(..., M, N)`` and whose innermost two dimensions form ``MxN`` matrices. Should have a floating-point data type. + keepdims: bool + If ``True``, the last two axes (dimensions) must be included in the result as singleton dimensions, and, accordingly, the result must be compatible with the input array (see :ref:`broadcasting`). Otherwise, if ``False``, the last two axes (dimensions) must not be included in the result. Default: ``False``. + ord: Optional[Union[int, float, Literal[inf, -inf, 'fro', 'nuc']]] + order of the norm. The following mathematical norms must be supported: + + +------------------+---------------------------------+ + | ord | description | + +==================+=================================+ + | 'fro' | Frobenius norm | + +------------------+---------------------------------+ + | 'nuc' | nuclear norm | + +------------------+---------------------------------+ + | 1 | max(sum(abs(x), axis=0)) | + +------------------+---------------------------------+ + | 2 | largest singular value | + +------------------+---------------------------------+ + | inf | max(sum(abs(x), axis=1)) | + +------------------+---------------------------------+ + + The following non-mathematical "norms" must be supported: + + +------------------+---------------------------------+ + | ord | description | + +==================+=================================+ + | -1 | min(sum(abs(x), axis=0)) | + +------------------+---------------------------------+ + | -2 | smallest singular value | + +------------------+---------------------------------+ + | -inf | min(sum(abs(x), axis=1)) | + +------------------+---------------------------------+ + + If ``ord=1``, the norm corresponds to the induced matrix norm where ``p=1`` (i.e., the maximum absolute value column sum). + + If ``ord=2``, the norm corresponds to the induced matrix norm where ``p=inf`` (i.e., the maximum absolute value row sum). + + If ``ord=inf``, the norm corresponds to the induced matrix norm where ``p=2`` (i.e., the largest singular value). + + Default: ``'fro'``. + + Returns + ------- + out: array + an array containing the norms for each ``MxN`` matrix. If ``keepdims`` is ``False``, the returned array must have a rank which is two less than the rank of ``x``. If ``x`` has a real-valued data type, the returned array must have a real-valued floating-point data type determined by :ref:`type-promotion`. If ``x`` has a complex-valued data type, the returned array must have a real-valued floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``complex128``, then the returned array must have a ``float64`` data type). + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def matrix_power(x: array, n: int, /) -> array: + """ + Raises a square matrix (or a stack of square matrices) ``x`` to an integer power ``n``. + + Parameters + ---------- + x: array + input array having shape ``(..., M, M)`` and whose innermost two dimensions form square matrices. Should have a floating-point data type. + n: int + integer exponent. + + Returns + ------- + out: array + if ``n`` is equal to zero, an array containing the identity matrix for each square matrix. If ``n`` is less than zero, an array containing the inverse of each square matrix raised to the absolute value of ``n``, provided that each square matrix is invertible. If ``n`` is greater than zero, an array containing the result of raising each square matrix to the power ``n``. The returned array must have the same shape as ``x`` and a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def matrix_rank(x: array, /, *, rtol: Optional[Union[float, array]] = None) -> array: + """ + Returns the rank (i.e., number of non-zero singular values) of a matrix (or a stack of matrices). + + When ``x`` is a stack of matrices, the function must compute the number of non-zero singular values for each matrix in the stack. + + Parameters + ---------- + x: array + input array having shape ``(..., M, N)`` and whose innermost two dimensions form ``MxN`` matrices. Should have a floating-point data type. + rtol: Optional[Union[float, array]] + relative tolerance for small singular values. Singular values approximately less than or equal to ``rtol * largest_singular_value`` are set to zero. If a ``float``, the value is equivalent to a zero-dimensional array having a real-valued floating-point data type determined by :ref:`type-promotion` (as applied to ``x``) and must be broadcast against each matrix. If an ``array``, must have a real-valued floating-point data type and must be compatible with ``shape(x)[:-2]`` (see :ref:`broadcasting`). If ``None``, the default value is ``max(M, N) * eps``, where ``eps`` must be the machine epsilon associated with the real-valued floating-point data type determined by :ref:`type-promotion` (as applied to ``x``). Default: ``None``. + + Returns + ------- + out: array + an array containing the ranks. The returned array must have the default integer data type and must have shape ``(...)`` (i.e., must have a shape equal to ``shape(x)[:-2]``). + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def matrix_transpose(x: array, /) -> array: + """Alias for :func:`~array_api.matrix_transpose`.""" + + +def outer(x1: array, x2: array, /) -> array: + """ + Returns the outer product of two vectors ``x1`` and ``x2``. + + Parameters + ---------- + x1: array + first one-dimensional input array of size ``N``. Must have a numeric data type. + x2: array + second one-dimensional input array of size ``M``. Must have a numeric data type. + + Returns + ------- + out: array + a two-dimensional array containing the outer product and whose shape is ``(N, M)``. The returned array must have a data type determined by :ref:`type-promotion`. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def pinv(x: array, /, *, rtol: Optional[Union[float, array]] = None) -> array: + r""" + Returns the (Moore-Penrose) pseudo-inverse of a matrix (or a stack of matrices) ``x``. + + The pseudo-inverse of a matrix :math:`A`, denoted :math:`A^{+}`, is defined as the matrix that "solves" the least-squares problem :math:`Ax = b` (i.e., if :math:`\overline{x}` is a solution, then :math:`A^{+}` is the matrix such that :math:`\overline{x} = A^{+}b`). + + While the pseudo-inverse can be defined algebraically, one can understand the pseudo-inverse via singular value decomposition (SVD). Namely, if + + .. math:: + A = U \Sigma V^H + + is a singular decomposition of :math:`A`, then + + .. math:: + A^{+} = U \Sigma^{+} V^H + + where :math:`U` and :math:`V^H` are orthogonal matrices, :math:`\Sigma` is a diagonal matrix consisting of :math:`A`'s singular values, and :math:`\Sigma^{+}` is then a diagonal matrix consisting of the reciprocals of :math:`A`'s singular values, leaving zeros in place. During numerical computation, only elements larger than a small tolerance are considered nonzero, and all others replaced by zeros. + + When ``x`` is a stack of matrices, the function must compute the pseudo-inverse for each matrix in the stack. + + Parameters + ---------- + x: array + input array having shape ``(..., M, N)`` and whose innermost two dimensions form ``MxN`` matrices. Should have a floating-point data type. + rtol: Optional[Union[float, array]] + relative tolerance for small singular values. Singular values approximately less than or equal to ``rtol * largest_singular_value`` are set to zero. If a ``float``, the value is equivalent to a zero-dimensional array having a real-valued floating-point data type determined by :ref:`type-promotion` (as applied to ``x``) and must be broadcast against each matrix. If an ``array``, must have a real-valued floating-point data type and must be compatible with ``shape(x)[:-2]`` (see :ref:`broadcasting`). If ``None``, the default value is ``max(M, N) * eps``, where ``eps`` must be the machine epsilon associated with the real-valued floating-point data type determined by :ref:`type-promotion` (as applied to ``x``). Default: ``None``. + + Returns + ------- + out: array + an array containing the pseudo-inverse(s). The returned array must have a floating-point data type determined by :ref:`type-promotion` and must have shape ``(..., N, M)`` (i.e., must have the same shape as ``x``, except the innermost two dimensions must be transposed). + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def qr( + x: array, /, *, mode: Literal["reduced", "complete"] = "reduced" +) -> Tuple[array, array]: + r""" + Returns the QR decomposition of a full column rank matrix (or a stack of matrices). + + If ``x`` is real-valued, let :math:`\mathbb{K}` be the set of real numbers :math:`\mathbb{R}`, and, if ``x`` is complex-valued, let :math:`\mathbb{K}` be the set of complex numbers :math:`\mathbb{C}`. + + The **complete QR decomposition** of a matrix :math:`x \in\ \mathbb{K}^{n \times n}` is defined as + + .. math:: + x = QR + + where :math:`Q \in\ \mathbb{K}^{m \times m}` is orthogonal when ``x`` is real-valued and unitary when ``x`` is complex-valued and where :math:`R \in\ \mathbb{K}^{m \times n}` is an upper triangular matrix with real diagonal (even when ``x`` is complex-valued). + + When :math:`m \gt n` (tall matrix), as :math:`R` is upper triangular, the last :math:`m - n` rows are zero. In this case, the last :math:`m - n` columns of :math:`Q` can be dropped to form the **reduced QR decomposition**. + + .. math:: + x = QR + + where :math:`Q \in\ \mathbb{K}^{m \times n}` and :math:`R \in\ \mathbb{K}^{n \times n}`. + + The reduced QR decomposition equals with the complete QR decomposition when :math:`n \geq m` (wide matrix). + + When ``x`` is a stack of matrices, the function must compute the QR decomposition for each matrix in the stack. + + .. note:: + Whether an array library explicitly checks whether an input array is a full column rank matrix (or a stack of full column rank matrices) is implementation-defined. + + .. warning:: + The elements in the diagonal of :math:`R` are not necessarily positive. Accordingly, the returned QR decomposition is only unique up to the sign of the diagonal of :math:`R`, and different libraries or inputs on different devices may produce different valid decompositions. + + .. warning:: + The QR decomposition is only well-defined if the first ``k = min(m,n)`` columns of every matrix in ``x`` are linearly independent. + + Parameters + ---------- + x: array + input array having shape ``(..., M, N)`` and whose innermost two dimensions form ``MxN`` matrices of rank ``N``. Should have a floating-point data type. + mode: Literal['reduced', 'complete'] + decomposition mode. Should be one of the following modes: + + - ``'reduced'``: compute only the leading ``K`` columns of ``q``, such that ``q`` and ``r`` have dimensions ``(..., M, K)`` and ``(..., K, N)``, respectively, and where ``K = min(M, N)``. + - ``'complete'``: compute ``q`` and ``r`` with dimensions ``(..., M, M)`` and ``(..., M, N)``, respectively. + + Default: ``'reduced'``. + + Returns + ------- + out: Tuple[array, array] + a namedtuple ``(Q, R)`` whose + + - first element must have the field name ``Q`` and must be an array whose shape depends on the value of ``mode`` and contain matrices with orthonormal columns. If ``mode`` is ``'complete'``, the array must have shape ``(..., M, M)``. If ``mode`` is ``'reduced'``, the array must have shape ``(..., M, K)``, where ``K = min(M, N)``. The first ``x.ndim-2`` dimensions must have the same size as those of the input array ``x``. + - second element must have the field name ``R`` and must be an array whose shape depends on the value of ``mode`` and contain upper-triangular matrices. If ``mode`` is ``'complete'``, the array must have shape ``(..., M, N)``. If ``mode`` is ``'reduced'``, the array must have shape ``(..., K, N)``, where ``K = min(M, N)``. The first ``x.ndim-2`` dimensions must have the same size as those of the input ``x``. + + Each returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def slogdet(x: array, /) -> Tuple[array, array]: + r""" + Returns the sign and the natural logarithm of the absolute value of the determinant of a square matrix (or a stack of square matrices) ``x``. + + .. note:: + The purpose of this function is to calculate the determinant more accurately when the determinant is either very small or very large, as calling ``det`` may overflow or underflow. + + The sign of the determinant is given by + + .. math:: + \operatorname{sign}(\det x) = \begin{cases} + 0 & \textrm{if } \det x = 0 \\ + \frac{\det x}{|\det x|} & \textrm{otherwise} + \end{cases} + + where :math:`|\det x|` is the absolute value of the determinant of ``x``. + + When ``x`` is a stack of matrices, the function must compute the sign and natural logarithm of the absolute value of the determinant for each matrix in the stack. + + **Special Cases** + + For real-valued floating-point operands, + + - If the determinant is zero, the ``sign`` should be ``0`` and ``logabsdet`` should be ``-infinity``. + + For complex floating-point operands, + + - If the determinant is ``0 + 0j``, the ``sign`` should be ``0 + 0j`` and ``logabsdet`` should be ``-infinity + 0j``. + + .. note:: + Depending on the underlying algorithm, when the determinant is zero, the returned result may differ from ``-infinity`` (or ``-infinity + 0j``). In all cases, the determinant should be equal to ``sign * exp(logabsdet)`` (although, again, the result may be subject to numerical precision errors). + + Parameters + ---------- + x: array + input array having shape ``(..., M, M)`` and whose innermost two dimensions form square matrices. Should have a floating-point data type. + + Returns + ------- + out: Tuple[array, array] + a namedtuple (``sign``, ``logabsdet``) whose + + - first element must have the field name ``sign`` and must be an array containing a number representing the sign of the determinant for each square matrix. Must have the same data type as ``x``. + - second element must have the field name ``logabsdet`` and must be an array containing the natural logarithm of the absolute value of the determinant for each square matrix. If ``x`` is real-valued, the returned array must have a real-valued floating-point data type determined by :ref:`type-promotion`. If ``x`` is complex, the returned array must have a real-valued floating-point data type having the same precision as ``x`` (e.g., if ``x`` is ``complex64``, ``logabsdet`` must have a ``float32`` data type). + + Each returned array must have shape ``shape(x)[:-2]``. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def solve(x1: array, x2: array, /) -> array: + r""" + Returns the solution of a square system of linear equations with a unique solution. + + Let ``x1`` equal :math:`A` and ``x2`` equal :math:`B`. If the promoted data type of ``x1`` and ``x2`` is real-valued, let :math:`\mathbb{K}` be the set of real numbers :math:`\mathbb{R}`, and, if the promoted data type of ``x1`` and ``x2`` is complex-valued, let :math:`\mathbb{K}` be the set of complex numbers :math:`\mathbb{C}`. + + This function computes the solution :math:`X \in\ \mathbb{K}^{m \times k}` of the **linear system** associated to :math:`A \in\ \mathbb{K}^{m \times m}` and :math:`B \in\ \mathbb{K}^{m \times k}` and is defined as + + .. math:: + AX = B + + This system of linear equations has a unique solution if and only if :math:`A` is invertible. + + .. note:: + Whether an array library explicitly checks whether ``x1`` is invertible is implementation-defined. + + When ``x1`` and/or ``x2`` is a stack of matrices, the function must compute a solution for each matrix in the stack. + + Parameters + ---------- + x1: array + coefficient array ``A`` having shape ``(..., M, M)`` and whose innermost two dimensions form square matrices. Must be of full rank (i.e., all rows or, equivalently, columns must be linearly independent). Should have a floating-point data type. + x2: array + ordinate (or "dependent variable") array ``B``. If ``x2`` has shape ``(M,)``, ``x2`` is equivalent to an array having shape ``(..., M, 1)``. If ``x2`` has shape ``(..., M, K)``, each column ``k`` defines a set of ordinate values for which to compute a solution, and ``shape(x2)[:-1]`` must be compatible with ``shape(x1)[:-1]`` (see :ref:`broadcasting`). Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the solution to the system ``AX = B`` for each square matrix. The returned array must have the same shape as ``x2`` (i.e., the array corresponding to ``B``) and must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def svd(x: array, /, *, full_matrices: bool = True) -> Tuple[array, array, array]: + r""" + Returns a singular value decomposition (SVD) of a matrix (or a stack of matrices) ``x``. + + If ``x`` is real-valued, let :math:`\mathbb{K}` be the set of real numbers :math:`\mathbb{R}`, and, if ``x`` is complex-valued, let :math:`\mathbb{K}` be the set of complex numbers :math:`\mathbb{C}`. + + The full **singular value decomposition** of an :math:`m \times n` matrix :math:`x \in\ \mathbb{K}^{m \times n}` is a factorization of the form + + .. math:: + x = U \Sigma V^H + + where :math:`U \in\ \mathbb{K}^{m \times m}`, :math:`\Sigma \in\ \mathbb{K}^{m \times\ n}`, :math:`\operatorname{diag}(\Sigma) \in\ \mathbb{R}^{k}` with :math:`k = \operatorname{min}(m, n)`, :math:`V^H \in\ \mathbb{K}^{n \times n}`, and where :math:`V^H` is the conjugate transpose when :math:`V` is complex and the transpose when :math:`V` is real-valued. When ``x`` is real-valued, :math:`U`, :math:`V` (and thus :math:`V^H`) are orthogonal, and, when ``x`` is complex, :math:`U`, :math:`V` (and thus :math:`V^H`) are unitary. + + When :math:`m \gt n` (tall matrix), we can drop the last :math:`m - n` columns of :math:`U` to form the reduced SVD + + .. math:: + x = U \Sigma V^H + + where :math:`U \in\ \mathbb{K}^{m \times k}`, :math:`\Sigma \in\ \mathbb{K}^{k \times\ k}`, :math:`\operatorname{diag}(\Sigma) \in\ \mathbb{R}^{k}`, and :math:`V^H \in\ \mathbb{K}^{k \times n}`. In this case, :math:`U` and :math:`V` have orthonormal columns. + + Similarly, when :math:`n \gt m` (wide matrix), we can drop the last :math:`n - m` columns of :math:`V` to also form a reduced SVD. + + This function returns the decomposition :math:`U`, :math:`S`, and :math:`V^H`, where :math:`S = \operatorname{diag}(\Sigma)`. + + When ``x`` is a stack of matrices, the function must compute the singular value decomposition for each matrix in the stack. + + .. warning:: + The returned arrays :math:`U` and :math:`V` are neither unique nor continuous with respect to ``x``. Because :math:`U` and :math:`V` are not unique, different hardware and software may compute different singular vectors. + + Non-uniqueness stems from the fact that multiplying any pair of singular vectors :math:`u_k`, :math:`v_k` by :math:`-1` when ``x`` is real-valued and by :math:`e^{\phi j}` (:math:`\phi \in \mathbb{R}`) when ``x`` is complex produces another two valid singular vectors of the matrix. + + Parameters + ---------- + x: array + input array having shape ``(..., M, N)`` and whose innermost two dimensions form matrices on which to perform singular value decomposition. Should have a floating-point data type. + full_matrices: bool + If ``True``, compute full-sized ``U`` and ``Vh``, such that ``U`` has shape ``(..., M, M)`` and ``Vh`` has shape ``(..., N, N)``. If ``False``, compute on the leading ``K`` singular vectors, such that ``U`` has shape ``(..., M, K)`` and ``Vh`` has shape ``(..., K, N)`` and where ``K = min(M, N)``. Default: ``True``. + + Returns + ------- + out: Tuple[array, array, array] + a namedtuple ``(U, S, Vh)`` whose + + - first element must have the field name ``U`` and must be an array whose shape depends on the value of ``full_matrices`` and contain matrices with orthonormal columns (i.e., the columns are left singular vectors). If ``full_matrices`` is ``True``, the array must have shape ``(..., M, M)``. If ``full_matrices`` is ``False``, the array must have shape ``(..., M, K)``, where ``K = min(M, N)``. The first ``x.ndim-2`` dimensions must have the same shape as those of the input ``x``. Must have the same data type as ``x``. + - second element must have the field name ``S`` and must be an array with shape ``(..., K)`` that contains the vector(s) of singular values of length ``K``, where ``K = min(M, N)``. For each vector, the singular values must be sorted in descending order by magnitude, such that ``s[..., 0]`` is the largest value, ``s[..., 1]`` is the second largest value, et cetera. The first ``x.ndim-2`` dimensions must have the same shape as those of the input ``x``. Must have a real-valued floating-point data type having the same precision as ``x`` (e.g., if ``x`` is ``complex64``, ``S`` must have a ``float32`` data type). + - third element must have the field name ``Vh`` and must be an array whose shape depends on the value of ``full_matrices`` and contain orthonormal rows (i.e., the rows are the right singular vectors and the array is the adjoint). If ``full_matrices`` is ``True``, the array must have shape ``(..., N, N)``. If ``full_matrices`` is ``False``, the array must have shape ``(..., K, N)`` where ``K = min(M, N)``. The first ``x.ndim-2`` dimensions must have the same shape as those of the input ``x``. Must have the same data type as ``x``. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def svdvals(x: array, /) -> array: + """ + Returns the singular values of a matrix (or a stack of matrices) ``x``. + + When ``x`` is a stack of matrices, the function must compute the singular values for each matrix in the stack. + + Parameters + ---------- + x: array + input array having shape ``(..., M, N)`` and whose innermost two dimensions form matrices on which to perform singular value decomposition. Should have a floating-point data type. + + Returns + ------- + out: array + an array with shape ``(..., K)`` that contains the vector(s) of singular values of length ``K``, where ``K = min(M, N)``. For each vector, the singular values must be sorted in descending order by magnitude, such that ``s[..., 0]`` is the largest value, ``s[..., 1]`` is the second largest value, et cetera. The first ``x.ndim-2`` dimensions must have the same shape as those of the input ``x``. The returned array must have a real-valued floating-point data type having the same precision as ``x`` (e.g., if ``x`` is ``complex64``, the returned array must have a ``float32`` data type). + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def tensordot( + x1: array, + x2: array, + /, + *, + axes: Union[int, Tuple[Sequence[int], Sequence[int]]] = 2, +) -> array: + """Alias for :func:`~array_api.tensordot`.""" + + +def trace(x: array, /, *, offset: int = 0, dtype: Optional[dtype] = None) -> array: + """ + Returns the sum along the specified diagonals of a matrix (or a stack of matrices) ``x``. + + Parameters + ---------- + x: array + input array having shape ``(..., M, N)`` and whose innermost two dimensions form ``MxN`` matrices. Should have a numeric data type. + offset: int + offset specifying the off-diagonal relative to the main diagonal. + + - ``offset = 0``: the main diagonal. + - ``offset > 0``: off-diagonal above the main diagonal. + - ``offset < 0``: off-diagonal below the main diagonal. + + Default: ``0``. + dtype: Optional[dtype] + data type of the returned array. If ``None``, the returned array must have the same data type as ``x``, unless ``x`` has an integer data type supporting a smaller range of values than the default integer data type (e.g., ``x`` has an ``int16`` or ``uint32`` data type and the default integer data type is ``int64``). In those latter cases: + + - if ``x`` has a signed integer data type (e.g., ``int16``), the returned array must have the default integer data type. + - if ``x`` has an unsigned integer data type (e.g., ``uint16``), the returned array must have an unsigned integer data type having the same number of bits as the default integer data type (e.g., if the default integer data type is ``int32``, the returned array must have a ``uint32`` data type). + + If the data type (either specified or resolved) differs from the data type of ``x``, the input array should be cast to the specified data type before computing the sum (rationale: the ``dtype`` keyword argument is intended to help prevent overflows). Default: ``None``. + + Returns + ------- + out: array + an array containing the traces and whose shape is determined by removing the last two dimensions and storing the traces in the last array dimension. For example, if ``x`` has rank ``k`` and shape ``(I, J, K, ..., L, M, N)``, then an output array has rank ``k-2`` and shape ``(I, J, K, ..., L)`` where + + :: + + out[i, j, k, ..., l] = trace(a[i, j, k, ..., l, :, :]) + + The returned array must have a data type as described by the ``dtype`` parameter above. + + Notes + ----- + + **Special Cases** + + Let ``N`` equal the number of elements over which to compute the sum. + + - If ``N`` is ``0``, the sum is ``0`` (i.e., the empty sum). + + For both real-valued and complex floating-point operands, special cases must be handled as if the operation is implemented by successive application of :func:`~array_api.add`. + + .. versionchanged:: 2022.12 + Added complex data type support. + + .. versionchanged:: 2023.12 + Required the function to return a floating-point array having the same data type as the input array when provided a floating-point array. + """ + + +def vecdot(x1: array, x2: array, /, *, axis: int = None) -> array: + """Alias for :func:`~array_api.vecdot`.""" + + +def vector_norm( + x: array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + keepdims: bool = False, + ord: Union[int, float, Literal[inf, -inf]] = 2, +) -> array: + r""" + Computes the vector norm of a vector (or batch of vectors) ``x``. + + Parameters + ---------- + x: array + input array. Should have a floating-point data type. + axis: Optional[Union[int, Tuple[int, ...]]] + If an integer, ``axis`` specifies the axis (dimension) along which to compute vector norms. If an n-tuple, ``axis`` specifies the axes (dimensions) along which to compute batched vector norms. If ``None``, the vector norm must be computed over all array values (i.e., equivalent to computing the vector norm of a flattened array). Negative indices must be supported. Default: ``None``. + keepdims: bool + If ``True``, the axes (dimensions) specified by ``axis`` must be included in the result as singleton dimensions, and, accordingly, the result must be compatible with the input array (see :ref:`broadcasting`). Otherwise, if ``False``, the axes (dimensions) specified by ``axis`` must not be included in the result. Default: ``False``. + ord: Union[int, float, Literal[inf, -inf]] + order of the norm. The following mathematical norms must be supported: + + +------------------+----------------------------+ + | ord | description | + +==================+============================+ + | 1 | L1-norm (Manhattan) | + +------------------+----------------------------+ + | 2 | L2-norm (Euclidean) | + +------------------+----------------------------+ + | inf | infinity norm | + +------------------+----------------------------+ + | (int,float >= 1) | p-norm | + +------------------+----------------------------+ + + The following non-mathematical "norms" must be supported: + + +------------------+--------------------------------+ + | ord | description | + +==================+================================+ + | 0 | sum(a != 0) | + +------------------+--------------------------------+ + | -1 | 1./sum(1./abs(a)) | + +------------------+--------------------------------+ + | -2 | 1./sqrt(sum(1./abs(a)\*\*2)) | + +------------------+--------------------------------+ + | -inf | min(abs(a)) | + +------------------+--------------------------------+ + | (int,float < 1) | sum(abs(a)\*\*ord)\*\*(1./ord) | + +------------------+--------------------------------+ + + Default: ``2``. + + Returns + ------- + out: array + an array containing the vector norms. If ``axis`` is ``None``, the returned array must be a zero-dimensional array containing a vector norm. If ``axis`` is a scalar value (``int`` or ``float``), the returned array must have a rank which is one less than the rank of ``x``. If ``axis`` is a ``n``-tuple, the returned array must have a rank which is ``n`` less than the rank of ``x``. If ``x`` has a real-valued data type, the returned array must have a real-valued floating-point data type determined by :ref:`type-promotion`. If ``x`` has a complex-valued data type, the returned array must have a real-valued floating-point data type whose precision matches the precision of ``x`` (e.g., if ``x`` is ``complex128``, then the returned array must have a ``float64`` data type). + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ diff --git a/src/array_api_stubs/_2023_12/linear_algebra_functions.py b/src/array_api_stubs/_2023_12/linear_algebra_functions.py new file mode 100644 index 000000000..da4c97743 --- /dev/null +++ b/src/array_api_stubs/_2023_12/linear_algebra_functions.py @@ -0,0 +1,166 @@ +__all__ = ["matmul", "matrix_transpose", "tensordot", "vecdot"] + + +from ._types import Tuple, Union, Sequence, array + + +def matmul(x1: array, x2: array, /) -> array: + """ + Computes the matrix product. + + .. note:: + The ``matmul`` function must implement the same semantics as the built-in ``@`` operator (see `PEP 465 `_). + + Parameters + ---------- + x1: array + first input array. Should have a numeric data type. Must have at least one dimension. If ``x1`` is one-dimensional having shape ``(M,)`` and ``x2`` has more than one dimension, ``x1`` must be promoted to a two-dimensional array by prepending ``1`` to its dimensions (i.e., must have shape ``(1, M)``). After matrix multiplication, the prepended dimensions in the returned array must be removed. If ``x1`` has more than one dimension (including after vector-to-matrix promotion), ``shape(x1)[:-2]`` must be compatible with ``shape(x2)[:-2]`` (after vector-to-matrix promotion) (see :ref:`broadcasting`). If ``x1`` has shape ``(..., M, K)``, the innermost two dimensions form matrices on which to perform matrix multiplication. + x2: array + second input array. Should have a numeric data type. Must have at least one dimension. If ``x2`` is one-dimensional having shape ``(N,)`` and ``x1`` has more than one dimension, ``x2`` must be promoted to a two-dimensional array by appending ``1`` to its dimensions (i.e., must have shape ``(N, 1)``). After matrix multiplication, the appended dimensions in the returned array must be removed. If ``x2`` has more than one dimension (including after vector-to-matrix promotion), ``shape(x2)[:-2]`` must be compatible with ``shape(x1)[:-2]`` (after vector-to-matrix promotion) (see :ref:`broadcasting`). If ``x2`` has shape ``(..., K, N)``, the innermost two dimensions form matrices on which to perform matrix multiplication. + + + .. note:: + If either ``x1`` or ``x2`` has a complex floating-point data type, neither argument must be complex-conjugated or transposed. If conjugation and/or transposition is desired, these operations should be explicitly performed prior to computing the matrix product. + + Returns + ------- + out: array + - if both ``x1`` and ``x2`` are one-dimensional arrays having shape ``(N,)``, a zero-dimensional array containing the inner product as its only element. + - if ``x1`` is a two-dimensional array having shape ``(M, K)`` and ``x2`` is a two-dimensional array having shape ``(K, N)``, a two-dimensional array containing the `conventional matrix product `_ and having shape ``(M, N)``. + - if ``x1`` is a one-dimensional array having shape ``(K,)`` and ``x2`` is an array having shape ``(..., K, N)``, an array having shape ``(..., N)`` (i.e., prepended dimensions during vector-to-matrix promotion must be removed) and containing the `conventional matrix product `_. + - if ``x1`` is an array having shape ``(..., M, K)`` and ``x2`` is a one-dimensional array having shape ``(K,)``, an array having shape ``(..., M)`` (i.e., appended dimensions during vector-to-matrix promotion must be removed) and containing the `conventional matrix product `_. + - if ``x1`` is a two-dimensional array having shape ``(M, K)`` and ``x2`` is an array having shape ``(..., K, N)``, an array having shape ``(..., M, N)`` and containing the `conventional matrix product `_ for each stacked matrix. + - if ``x1`` is an array having shape ``(..., M, K)`` and ``x2`` is a two-dimensional array having shape ``(K, N)``, an array having shape ``(..., M, N)`` and containing the `conventional matrix product `_ for each stacked matrix. + - if either ``x1`` or ``x2`` has more than two dimensions, an array having a shape determined by :ref:`broadcasting` ``shape(x1)[:-2]`` against ``shape(x2)[:-2]`` and containing the `conventional matrix product `_ for each stacked matrix. + + The returned array must have a data type determined by :ref:`type-promotion`. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + + **Raises** + + - if either ``x1`` or ``x2`` is a zero-dimensional array. + - if ``x1`` is a one-dimensional array having shape ``(K,)``, ``x2`` is a one-dimensional array having shape ``(L,)``, and ``K != L``. + - if ``x1`` is a one-dimensional array having shape ``(K,)``, ``x2`` is an array having shape ``(..., L, N)``, and ``K != L``. + - if ``x1`` is an array having shape ``(..., M, K)``, ``x2`` is a one-dimensional array having shape ``(L,)``, and ``K != L``. + - if ``x1`` is an array having shape ``(..., M, K)``, ``x2`` is an array having shape ``(..., L, N)``, and ``K != L``. + + """ + + +def matrix_transpose(x: array, /) -> array: + """ + Transposes a matrix (or a stack of matrices) ``x``. + + Parameters + ---------- + x: array + input array having shape ``(..., M, N)`` and whose innermost two dimensions form ``MxN`` matrices. + + Returns + ------- + out: array + an array containing the transpose for each matrix and having shape ``(..., N, M)``. The returned array must have the same data type as ``x``. + """ + + +def tensordot( + x1: array, + x2: array, + /, + *, + axes: Union[int, Tuple[Sequence[int], Sequence[int]]] = 2, +) -> array: + """ + Returns a tensor contraction of ``x1`` and ``x2`` over specific axes. + + .. note:: + The ``tensordot`` function corresponds to the generalized matrix product. + + Parameters + ---------- + x1: array + first input array. Should have a numeric data type. + x2: array + second input array. Should have a numeric data type. Corresponding contracted axes of ``x1`` and ``x2`` must be equal. + + .. note:: + Contracted axes (dimensions) must not be broadcasted. + + axes: Union[int, Tuple[Sequence[int], Sequence[int]]] + number of axes (dimensions) to contract or explicit sequences of axis (dimension) indices for ``x1`` and ``x2``, respectively. + + If ``axes`` is an ``int`` equal to ``N``, then contraction must be performed over the last ``N`` axes of ``x1`` and the first ``N`` axes of ``x2`` in order. The size of each corresponding axis (dimension) must match. Must be nonnegative. + + - If ``N`` equals ``0``, the result is the tensor (outer) product. + - If ``N`` equals ``1``, the result is the tensor dot product. + - If ``N`` equals ``2``, the result is the tensor double contraction (default). + + If ``axes`` is a tuple of two sequences ``(x1_axes, x2_axes)``, the first sequence must apply to ``x1`` and the second sequence to ``x2``. Both sequences must have the same length. Each axis (dimension) ``x1_axes[i]`` for ``x1`` must have the same size as the respective axis (dimension) ``x2_axes[i]`` for ``x2``. Each index referred to in a sequence must be unique. If ``x1`` has rank (i.e, number of dimensions) ``N``, a valid ``x1`` axis must reside on the half-open interval ``[-N, N)``. If ``x2`` has rank ``M``, a valid ``x2`` axis must reside on the half-open interval ``[-M, M)``. + + + .. note:: + If either ``x1`` or ``x2`` has a complex floating-point data type, neither argument must be complex-conjugated or transposed. If conjugation and/or transposition is desired, these operations should be explicitly performed prior to computing the generalized matrix product. + + Returns + ------- + out: array + an array containing the tensor contraction whose shape consists of the non-contracted axes (dimensions) of the first array ``x1``, followed by the non-contracted axes (dimensions) of the second array ``x2``. The returned array must have a data type determined by :ref:`type-promotion`. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + + .. versionchanged:: 2023.12 + Allow negative axes. + """ + + +def vecdot(x1: array, x2: array, /, *, axis: int = -1) -> array: + r""" + Computes the (vector) dot product of two arrays. + + Let :math:`\mathbf{a}` be a vector in ``x1`` and :math:`\mathbf{b}` be a corresponding vector in ``x2``. The dot product is defined as + + .. math:: + \mathbf{a} \cdot \mathbf{b} = \sum_{i=0}^{n-1} \overline{a_i}b_i + + over the dimension specified by ``axis`` and where :math:`n` is the dimension size and :math:`\overline{a_i}` denotes the complex conjugate if :math:`a_i` is complex and the identity if :math:`a_i` is real-valued. + + Parameters + ---------- + x1: array + first input array. Should have a floating-point data type. + x2: array + second input array. Must be compatible with ``x1`` for all non-contracted axes (see :ref:`broadcasting`). The size of the axis over which to compute the dot product must be the same size as the respective axis in ``x1``. Should have a floating-point data type. + + .. note:: + The contracted axis (dimension) must not be broadcasted. + + axis: int + the axis (dimension) of ``x1`` and ``x2`` containing the vectors for which to compute the dot product. Should be an integer on the interval ``[-N, -1]``, where ``N`` is ``min(x1.ndim, x2.ndim)``. The function must determine the axis along which to compute the dot product by counting backward from the last dimension (where ``-1`` refers to the last dimension). By default, the function must compute the dot product over the last axis. Default: ``-1``. + + Returns + ------- + out: array + if ``x1`` and ``x2`` are both one-dimensional arrays, a zero-dimensional containing the dot product; otherwise, a non-zero-dimensional array containing the dot products and having rank ``N-1``, where ``N`` is the rank (number of dimensions) of the shape determined according to :ref:`broadcasting` along the non-contracted axes. The returned array must have a data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Raises** + + - if the size of the axis over which to compute the dot product is not the same (before broadcasting) for both ``x1`` and ``x2``. + + .. versionchanged:: 2022.12 + Added complex data type support. + + .. versionchanged:: 2023.12 + Restricted ``axis`` to only negative integers. + """ diff --git a/src/array_api_stubs/_2023_12/manipulation_functions.py b/src/array_api_stubs/_2023_12/manipulation_functions.py new file mode 100644 index 000000000..87f9511b0 --- /dev/null +++ b/src/array_api_stubs/_2023_12/manipulation_functions.py @@ -0,0 +1,368 @@ +__all__ = [ + "broadcast_arrays", + "broadcast_to", + "concat", + "expand_dims", + "flip", + "moveaxis", + "permute_dims", + "repeat", + "reshape", + "roll", + "squeeze", + "stack", + "tile", + "unstack", +] + + +from ._types import List, Optional, Tuple, Union, array + + +def broadcast_arrays(*arrays: array) -> List[array]: + """ + Broadcasts one or more arrays against one another. + + Parameters + ---------- + arrays: array + an arbitrary number of to-be broadcasted arrays. + + Returns + ------- + out: List[array] + a list of broadcasted arrays. Each array must have the same shape. Each array must have the same dtype as its corresponding input array. + """ + + +def broadcast_to(x: array, /, shape: Tuple[int, ...]) -> array: + """ + Broadcasts an array to a specified shape. + + Parameters + ---------- + x: array + array to broadcast. + shape: Tuple[int, ...] + array shape. Must be compatible with ``x`` (see :ref:`broadcasting`). If the array is incompatible with the specified shape, the function should raise an exception. + + Returns + ------- + out: array + an array having a specified shape. Must have the same data type as ``x``. + """ + + +def concat( + arrays: Union[Tuple[array, ...], List[array]], /, *, axis: Optional[int] = 0 +) -> array: + """ + Joins a sequence of arrays along an existing axis. + + Parameters + ---------- + arrays: Union[Tuple[array, ...], List[array]] + input arrays to join. The arrays must have the same shape, except in the dimension specified by ``axis``. + axis: Optional[int] + axis along which the arrays will be joined. If ``axis`` is ``None``, arrays must be flattened before concatenation. If ``axis`` is negative, the function must determine the axis along which to join by counting from the last dimension. Default: ``0``. + + Returns + ------- + out: array + an output array containing the concatenated values. If the input arrays have different data types, normal :ref:`type-promotion` must apply. If the input arrays have the same data type, the output array must have the same data type as the input arrays. + + .. note:: + This specification leaves type promotion between data type families (i.e., ``intxx`` and ``floatxx``) unspecified. + """ + + +def expand_dims(x: array, /, *, axis: int = 0) -> array: + """ + Expands the shape of an array by inserting a new axis (dimension) of size one at the position specified by ``axis``. + + Parameters + ---------- + x: array + input array. + axis: int + axis position (zero-based). If ``x`` has rank (i.e, number of dimensions) ``N``, a valid ``axis`` must reside on the closed-interval ``[-N-1, N]``. If provided a negative ``axis``, the axis position at which to insert a singleton dimension must be computed as ``N + axis + 1``. Hence, if provided ``-1``, the resolved axis position must be ``N`` (i.e., a singleton dimension must be appended to the input array ``x``). If provided ``-N-1``, the resolved axis position must be ``0`` (i.e., a singleton dimension must be prepended to the input array ``x``). + + Returns + ------- + out: array + an expanded output array having the same data type as ``x``. + + Raises + ------ + IndexError + If provided an invalid ``axis`` position, an ``IndexError`` should be raised. + """ + + +def flip(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> array: + """ + Reverses the order of elements in an array along the given axis. The shape of the array must be preserved. + + Parameters + ---------- + x: array + input array. + axis: Optional[Union[int, Tuple[int, ...]]] + axis (or axes) along which to flip. If ``axis`` is ``None``, the function must flip all input array axes. If ``axis`` is negative, the function must count from the last dimension. If provided more than one axis, the function must flip only the specified axes. Default: ``None``. + + Returns + ------- + out: array + an output array having the same data type and shape as ``x`` and whose elements, relative to ``x``, are reordered. + """ + + +def moveaxis( + x: array, + source: Union[int, Tuple[int, ...]], + destination: Union[int, Tuple[int, ...]], + /, +) -> array: + """ + Moves array axes (dimensions) to new positions, while leaving other axes in their original positions. + + Parameters + ---------- + x: array + input array. + source: Union[int, Tuple[int, ...]] + Axes to move. Provided axes must be unique. If ``x`` has rank (i.e, number of dimensions) ``N``, a valid axis must reside on the half-open interval ``[-N, N)``. + destination: Union[int, Tuple[int, ...]] + indices defining the desired positions for each respective ``source`` axis index. Provided indices must be unique. If ``x`` has rank (i.e, number of dimensions) ``N``, a valid axis must reside on the half-open interval ``[-N, N)``. + + Returns + ------- + out: array + an array containing reordered axes. The returned array must have the same data type as ``x``. + + Notes + ----- + + .. versionadded:: 2023.12 + """ + + +def permute_dims(x: array, /, axes: Tuple[int, ...]) -> array: + """ + Permutes the axes (dimensions) of an array ``x``. + + Parameters + ---------- + x: array + input array. + axes: Tuple[int, ...] + tuple containing a permutation of ``(0, 1, ..., N-1)`` where ``N`` is the number of axes (dimensions) of ``x``. + + Returns + ------- + out: array + an array containing the axes permutation. The returned array must have the same data type as ``x``. + """ + + +def repeat( + x: array, + repeats: Union[int, array], + /, + *, + axis: Optional[int] = None, +) -> array: + """ + Repeats each element of an array a specified number of times on a per-element basis. + + .. admonition:: Data-dependent output shape + :class: important + + When ``repeats`` is an array, the shape of the output array for this function depends on the data values in the ``repeats`` array; hence, array libraries which build computation graphs (e.g., JAX, Dask, etc.) may find this function difficult to implement without knowing the values in ``repeats``. Accordingly, such libraries may choose to omit support for ``repeats`` arrays; however, conforming implementations must support providing a literal ``int``. See :ref:`data-dependent-output-shapes` section for more details. + + Parameters + ---------- + x: array + input array containing elements to repeat. + repeats: Union[int, array] + the number of repetitions for each element. + + If ``axis`` is ``None``, let ``N = prod(x.shape)`` and + + - if ``repeats`` is an array, ``repeats`` must be broadcast compatible with the shape ``(N,)`` (i.e., be a one-dimensional array having shape ``(1,)`` or ``(N,)``). + - if ``repeats`` is an integer, ``repeats`` must be broadcasted to the shape `(N,)`. + + If ``axis`` is not ``None``, let ``M = x.shape[axis]`` and + + - if ``repeats`` is an array, ``repeats`` must be broadcast compatible with the shape ``(M,)`` (i.e., be a one-dimensional array having shape ``(1,)`` or ``(M,)``). + - if ``repeats`` is an integer, ``repeats`` must be broadcasted to the shape ``(M,)``. + + If ``repeats`` is an array, the array must have an integer data type. + + .. note:: + For specification-conforming array libraries supporting hardware acceleration, providing an array for ``repeats`` may cause device synchronization due to an unknown output shape. For those array libraries where synchronization concerns are applicable, conforming array libraries are advised to include a warning in their documentation regarding potential performance degradation when ``repeats`` is an array. + + axis: Optional[int] + the axis (dimension) along which to repeat elements. If ``axis`` is `None`, the function must flatten the input array ``x`` and then repeat elements of the flattened input array and return the result as a one-dimensional output array. A flattened input array must be flattened in row-major, C-style order. Default: ``None``. + + Returns + ------- + out: array + an output array containing repeated elements. The returned array must have the same data type as ``x``. If ``axis`` is ``None``, the returned array must be a one-dimensional array; otherwise, the returned array must have the same shape as ``x``, except for the axis (dimension) along which elements were repeated. + + Notes + ----- + + .. versionadded:: 2023.12 + """ + + +def reshape( + x: array, /, shape: Tuple[int, ...], *, copy: Optional[bool] = None +) -> array: + """ + Reshapes an array without changing its data. + + Parameters + ---------- + x: array + input array to reshape. + shape: Tuple[int, ...] + a new shape compatible with the original shape. One shape dimension is allowed to be ``-1``. When a shape dimension is ``-1``, the corresponding output array shape dimension must be inferred from the length of the array and the remaining dimensions. + copy: Optional[bool] + whether or not to copy the input array. If ``True``, the function must always copy. If ``False``, the function must never copy. If ``None``, the function must avoid copying, if possible, and may copy otherwise. Default: ``None``. + + Returns + ------- + out: array + an output array having the same data type and elements as ``x``. + + Raises + ------ + ValueError + If ``copy=False`` and a copy would be necessary, a ``ValueError`` + should be raised. + """ + + +def roll( + x: array, + /, + shift: Union[int, Tuple[int, ...]], + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, +) -> array: + """ + Rolls array elements along a specified axis. Array elements that roll beyond the last position are re-introduced at the first position. Array elements that roll beyond the first position are re-introduced at the last position. + + Parameters + ---------- + x: array + input array. + shift: Union[int, Tuple[int, ...]] + number of places by which the elements are shifted. If ``shift`` is a tuple, then ``axis`` must be a tuple of the same size, and each of the given axes must be shifted by the corresponding element in ``shift``. If ``shift`` is an ``int`` and ``axis`` a tuple, then the same ``shift`` must be used for all specified axes. If a shift is positive, then array elements must be shifted positively (toward larger indices) along the dimension of ``axis``. If a shift is negative, then array elements must be shifted negatively (toward smaller indices) along the dimension of ``axis``. + axis: Optional[Union[int, Tuple[int, ...]]] + axis (or axes) along which elements to shift. If ``axis`` is ``None``, the array must be flattened, shifted, and then restored to its original shape. Default: ``None``. + + Returns + ------- + out: array + an output array having the same data type as ``x`` and whose elements, relative to ``x``, are shifted. + """ + + +def squeeze(x: array, /, axis: Union[int, Tuple[int, ...]]) -> array: + """ + Removes singleton dimensions (axes) from ``x``. + + Parameters + ---------- + x: array + input array. + axis: Union[int, Tuple[int, ...]] + axis (or axes) to squeeze. + + Returns + ------- + out: array + an output array having the same data type and elements as ``x``. + + Raises + ------ + ValueError + If a specified axis has a size greater than one (i.e., it is not a + singleton dimension), a ``ValueError`` should be raised. + """ + + +def stack(arrays: Union[Tuple[array, ...], List[array]], /, *, axis: int = 0) -> array: + """ + Joins a sequence of arrays along a new axis. + + Parameters + ---------- + arrays: Union[Tuple[array, ...], List[array]] + input arrays to join. Each array must have the same shape. + axis: int + axis along which the arrays will be joined. Providing an ``axis`` specifies the index of the new axis in the dimensions of the result. For example, if ``axis`` is ``0``, the new axis will be the first dimension and the output array will have shape ``(N, A, B, C)``; if ``axis`` is ``1``, the new axis will be the second dimension and the output array will have shape ``(A, N, B, C)``; and, if ``axis`` is ``-1``, the new axis will be the last dimension and the output array will have shape ``(A, B, C, N)``. A valid ``axis`` must be on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If provided an ``axis`` outside of the required interval, the function must raise an exception. Default: ``0``. + + Returns + ------- + out: array + an output array having rank ``N+1``, where ``N`` is the rank (number of dimensions) of ``x``. If the input arrays have different data types, normal :ref:`type-promotion` must apply. If the input arrays have the same data type, the output array must have the same data type as the input arrays. + + .. note:: + This specification leaves type promotion between data type families (i.e., ``intxx`` and ``floatxx``) unspecified. + """ + + +def tile(x: array, repetitions: Tuple[int, ...], /): + """ + Constructs an array by tiling an input array. + + Parameters + ---------- + x: array + input array. + repetitions: Tuple[int, ...] + number of repetitions along each axis (dimension). + + Let ``N = len(x.shape)`` and ``M = len(repetitions)``. + + If ``N > M``, the function must prepend ones until all axes (dimensions) are specified (e.g., if ``x`` has shape ``(8,6,4,2)`` and ``repetitions`` is the tuple ``(3,3)``, then ``repetitions`` must be treated as ``(1,1,3,3)``). + + If ``N < M``, the function must prepend singleton axes (dimensions) to ``x`` until ``x`` has as many axes (dimensions) as ``repetitions`` specifies (e.g., if ``x`` has shape ``(4,2)`` and ``repetitions`` is the tuple ``(3,3,3,3)``, then ``x`` must be treated as if it has shape ``(1,1,4,2)``). + + Returns + ------- + out: array + a tiled output array. The returned array must have the same data type as ``x`` and must have a rank (i.e., number of dimensions) equal to ``max(N, M)``. If ``S`` is the shape of the tiled array after prepending singleton dimensions (if necessary) and ``r`` is the tuple of repetitions after prepending ones (if necessary), then the number of elements along each axis (dimension) must satisfy ``S[i]*r[i]``, where ``i`` refers to the ``i`` th axis (dimension). + + Notes + ----- + + .. versionadded:: 2023.12 + """ + + +def unstack(x: array, /, *, axis: int = 0) -> Tuple[array, ...]: + """ + Splits an array in a sequence of arrays along the given axis. + + Parameters + ---------- + x: array + input array. + axis: int + axis along which the array will be split. A valid ``axis`` must be on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If provided an ``axis`` outside of the required interval, the function must raise an exception. Default: ``0``. + + Returns + ------- + out: Tuple[array, ...] + tuple of slices along the given dimension. All the arrays have the same shape. + + Notes + ----- + + .. versionadded:: 2023.12 + """ diff --git a/src/array_api_stubs/_2023_12/searching_functions.py b/src/array_api_stubs/_2023_12/searching_functions.py new file mode 100644 index 000000000..029459b9a --- /dev/null +++ b/src/array_api_stubs/_2023_12/searching_functions.py @@ -0,0 +1,159 @@ +__all__ = ["argmax", "argmin", "nonzero", "searchsorted", "where"] + + +from ._types import Optional, Tuple, Literal, array + + +def argmax(x: array, /, *, axis: Optional[int] = None, keepdims: bool = False) -> array: + """ + Returns the indices of the maximum values along a specified axis. + + When the maximum value occurs multiple times, only the indices corresponding to the first occurrence are returned. + + .. note:: + For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-dependent (see :ref:`complex-number-ordering`). + + Parameters + ---------- + x: array + input array. Should have a real-valued data type. + axis: Optional[int] + axis along which to search. If ``None``, the function must return the index of the maximum value of the flattened array. Default: ``None``. + keepdims: bool + if ``True``, the reduced axes (dimensions) must be included in the result as singleton dimensions, and, accordingly, the result must be compatible with the input array (see :ref:`broadcasting`). Otherwise, if ``False``, the reduced axes (dimensions) must not be included in the result. Default: ``False``. + + Returns + ------- + out: array + if ``axis`` is ``None``, a zero-dimensional array containing the index of the first occurrence of the maximum value; otherwise, a non-zero-dimensional array containing the indices of the maximum values. The returned array must have be the default array index data type. + """ + + +def argmin(x: array, /, *, axis: Optional[int] = None, keepdims: bool = False) -> array: + """ + Returns the indices of the minimum values along a specified axis. + + When the minimum value occurs multiple times, only the indices corresponding to the first occurrence are returned. + + .. note:: + For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-dependent (see :ref:`complex-number-ordering`). + + Parameters + ---------- + x: array + input array. Should have a real-valued data type. + axis: Optional[int] + axis along which to search. If ``None``, the function must return the index of the minimum value of the flattened array. Default: ``None``. + keepdims: bool + if ``True``, the reduced axes (dimensions) must be included in the result as singleton dimensions, and, accordingly, the result must be compatible with the input array (see :ref:`broadcasting`). Otherwise, if ``False``, the reduced axes (dimensions) must not be included in the result. Default: ``False``. + + Returns + ------- + out: array + if ``axis`` is ``None``, a zero-dimensional array containing the index of the first occurrence of the minimum value; otherwise, a non-zero-dimensional array containing the indices of the minimum values. The returned array must have the default array index data type. + """ + + +def nonzero(x: array, /) -> Tuple[array, ...]: + """ + Returns the indices of the array elements which are non-zero. + + .. note:: + If ``x`` has a complex floating-point data type, non-zero elements are those elements having at least one component (real or imaginary) which is non-zero. + + .. note:: + If ``x`` has a boolean data type, non-zero elements are those elements which are equal to ``True``. + + .. admonition:: Data-dependent output shape + :class: admonition important + + The shape of the output array for this function depends on the data values in the input array; hence, array libraries which build computation graphs (e.g., JAX, Dask, etc.) may find this function difficult to implement without knowing array values. Accordingly, such libraries may choose to omit this function. See :ref:`data-dependent-output-shapes` section for more details. + + Parameters + ---------- + x: array + input array. Must have a positive rank. If ``x`` is zero-dimensional, the function must raise an exception. + + Returns + ------- + out: Typle[array, ...] + a tuple of ``k`` arrays, one for each dimension of ``x`` and each of size ``n`` (where ``n`` is the total number of non-zero elements), containing the indices of the non-zero elements in that dimension. The indices must be returned in row-major, C-style order. The returned array must have the default array index data type. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def searchsorted( + x1: array, + x2: array, + /, + *, + side: Literal["left", "right"] = "left", + sorter: Optional[array] = None, +) -> array: + """ + Finds the indices into ``x1`` such that, if the corresponding elements in ``x2`` were inserted before the indices, the order of ``x1``, when sorted in ascending order, would be preserved. + + Parameters + ---------- + x1: array + input array. Must be a one-dimensional array. Should have a real-valued data type. If ``sorter`` is ``None``, must be sorted in ascending order; otherwise, ``sorter`` must be an array of indices that sort ``x1`` in ascending order. + x2: array + array containing search values. Should have a real-valued data type. + side: Literal['left', 'right'] + argument controlling which index is returned if a value lands exactly on an edge. + + Let ``x`` be an array of rank ``N`` where ``v`` is an individual element given by ``v = x2[n,m,...,j]``. + + If ``side == 'left'``, then + + - each returned index ``i`` must satisfy the index condition ``x1[i-1] < v <= x1[i]``. + - if no index satisfies the index condition, then the returned index for that element must be ``0``. + + Otherwise, if ``side == 'right'``, then + + - each returned index ``i`` must satisfy the index condition ``x1[i-1] <= v < x1[i]``. + - if no index satisfies the index condition, then the returned index for that element must be ``N``, where ``N`` is the number of elements in ``x1``. + + Default: ``'left'``. + sorter: Optional[array] + array of indices that sort ``x1`` in ascending order. The array must have the same shape as ``x1`` and have an integer data type. Default: ``None``. + + Returns + ------- + out: array + an array of indices with the same shape as ``x2``. The returned array must have the default array index data type. + + Notes + ----- + + For real-valued floating-point arrays, the sort order of NaNs and signed zeros is unspecified and thus implementation-dependent. Accordingly, when a real-valued floating-point array contains NaNs and signed zeros, what constitutes ascending order may vary among specification-conforming array libraries. + + While behavior for arrays containing NaNs and signed zeros is implementation-dependent, specification-conforming libraries should, however, ensure consistency with ``sort`` and ``argsort`` (i.e., if a value in ``x2`` is inserted into ``x1`` according to the corresponding index in the output array and ``sort`` is invoked on the resultant array, the sorted result should be an array in the same order). + + .. versionadded:: 2023.12 + """ + + +def where(condition: array, x1: array, x2: array, /) -> array: + """ + Returns elements chosen from ``x1`` or ``x2`` depending on ``condition``. + + Parameters + ---------- + condition: array + when ``True``, yield ``x1_i``; otherwise, yield ``x2_i``. Must be compatible with ``x1`` and ``x2`` (see :ref:`broadcasting`). + x1: array + first input array. Must be compatible with ``condition`` and ``x2`` (see :ref:`broadcasting`). + x2: array + second input array. Must be compatible with ``condition`` and ``x1`` (see :ref:`broadcasting`). + + Returns + ------- + out: array + an array with elements from ``x1`` where ``condition`` is ``True``, and elements from ``x2`` elsewhere. The returned array must have a data type determined by :ref:`type-promotion` rules with the arrays ``x1`` and ``x2``. + """ diff --git a/src/array_api_stubs/_2023_12/set_functions.py b/src/array_api_stubs/_2023_12/set_functions.py new file mode 100644 index 000000000..5b7e9a56c --- /dev/null +++ b/src/array_api_stubs/_2023_12/set_functions.py @@ -0,0 +1,183 @@ +__all__ = ["unique_all", "unique_counts", "unique_inverse", "unique_values"] + + +from ._types import Tuple, array + + +def unique_all(x: array, /) -> Tuple[array, array, array, array]: + """ + Returns the unique elements of an input array ``x``, the first occurring indices for each unique element in ``x``, the indices from the set of unique elements that reconstruct ``x``, and the corresponding counts for each unique element in ``x``. + + .. admonition:: Data-dependent output shape + :class: important + + The shapes of two of the output arrays for this function depend on the data values in the input array; hence, array libraries which build computation graphs (e.g., JAX, Dask, etc.) may find this function difficult to implement without knowing array values. Accordingly, such libraries may choose to omit this function. See :ref:`data-dependent-output-shapes` section for more details. + + .. note:: + Uniqueness should be determined based on value equality (see :func:`~array_api.equal`). For input arrays having floating-point data types, value-based equality implies the following behavior. + + - As ``nan`` values compare as ``False``, ``nan`` values should be considered distinct. + - As complex floating-point values having at least one ``nan`` component compare as ``False``, complex floating-point values having ``nan`` components should be considered distinct. + - As ``-0`` and ``+0`` compare as ``True``, signed zeros should not be considered distinct, and the corresponding unique element will be implementation-dependent (e.g., an implementation could choose to return ``-0`` if ``-0`` occurs before ``+0``). + + As signed zeros are not distinct, using ``inverse_indices`` to reconstruct the input array is not guaranteed to return an array having the exact same values. + + Each ``nan`` value and each complex floating-point value having a ``nan`` component should have a count of one, while the counts for signed zeros should be aggregated as a single count. + + Parameters + ---------- + x: array + input array. If ``x`` has more than one dimension, the function must flatten ``x`` and return the unique elements of the flattened array. + + Returns + ------- + out: Tuple[array, array, array, array] + a namedtuple ``(values, indices, inverse_indices, counts)`` whose + + - first element must have the field name ``values`` and must be a one-dimensional array containing the unique elements of ``x``. The array must have the same data type as ``x``. + - second element must have the field name ``indices`` and must be an array containing the indices (first occurrences) of a flattened ``x`` that result in ``values``. The array must have the same shape as ``values`` and must have the default array index data type. + - third element must have the field name ``inverse_indices`` and must be an array containing the indices of ``values`` that reconstruct ``x``. The array must have the same shape as ``x`` and must have the default array index data type. + - fourth element must have the field name ``counts`` and must be an array containing the number of times each unique element occurs in ``x``. The order of the returned counts must match the order of ``values``, such that a specific element in ``counts`` corresponds to the respective unique element in ``values``. The returned array must have same shape as ``values`` and must have the default array index data type. + + .. note:: + The order of unique elements is not specified and may vary between implementations. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + + .. versionchanged:: 2023.12 + Clarified flattening behavior and required the order of ``counts`` match the order of ``values``. + """ + + +def unique_counts(x: array, /) -> Tuple[array, array]: + """ + Returns the unique elements of an input array ``x`` and the corresponding counts for each unique element in ``x``. + + .. admonition:: Data-dependent output shape + :class: important + + The shapes of two of the output arrays for this function depend on the data values in the input array; hence, array libraries which build computation graphs (e.g., JAX, Dask, etc.) may find this function difficult to implement without knowing array values. Accordingly, such libraries may choose to omit this function. See :ref:`data-dependent-output-shapes` section for more details. + + .. note:: + Uniqueness should be determined based on value equality (see :func:`~array_api.equal`). For input arrays having floating-point data types, value-based equality implies the following behavior. + + - As ``nan`` values compare as ``False``, ``nan`` values should be considered distinct. + - As complex floating-point values having at least one ``nan`` component compare as ``False``, complex floating-point values having ``nan`` components should be considered distinct. + - As ``-0`` and ``+0`` compare as ``True``, signed zeros should not be considered distinct, and the corresponding unique element will be implementation-dependent (e.g., an implementation could choose to return ``-0`` if ``-0`` occurs before ``+0``). + + Each ``nan`` value and each complex floating-point value having a ``nan`` component should have a count of one, while the counts for signed zeros should be aggregated as a single count. + + Parameters + ---------- + x: array + input array. If ``x`` has more than one dimension, the function must flatten ``x`` and return the unique elements of the flattened array. + + Returns + ------- + out: Tuple[array, array] + a namedtuple `(values, counts)` whose + + - first element must have the field name ``values`` and must be a one-dimensional array containing the unique elements of ``x``. The array must have the same data type as ``x``. + - second element must have the field name `counts` and must be an array containing the number of times each unique element occurs in ``x``. The order of the returned counts must match the order of ``values``, such that a specific element in ``counts`` corresponds to the respective unique element in ``values``. The returned array must have same shape as ``values`` and must have the default array index data type. + + .. note:: + The order of unique elements is not specified and may vary between implementations. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + + .. versionchanged:: 2023.12 + Clarified flattening behavior and required the order of ``counts`` match the order of ``values``. + """ + + +def unique_inverse(x: array, /) -> Tuple[array, array]: + """ + Returns the unique elements of an input array ``x`` and the indices from the set of unique elements that reconstruct ``x``. + + .. admonition:: Data-dependent output shape + :class: important + + The shapes of two of the output arrays for this function depend on the data values in the input array; hence, array libraries which build computation graphs (e.g., JAX, Dask, etc.) may find this function difficult to implement without knowing array values. Accordingly, such libraries may choose to omit this function. See :ref:`data-dependent-output-shapes` section for more details. + + .. note:: + Uniqueness should be determined based on value equality (see :func:`~array_api.equal`). For input arrays having floating-point data types, value-based equality implies the following behavior. + + - As ``nan`` values compare as ``False``, ``nan`` values should be considered distinct. + - As complex floating-point values having at least one ``nan`` component compare as ``False``, complex floating-point values having ``nan`` components should be considered distinct. + - As ``-0`` and ``+0`` compare as ``True``, signed zeros should not be considered distinct, and the corresponding unique element will be implementation-dependent (e.g., an implementation could choose to return ``-0`` if ``-0`` occurs before ``+0``). + + As signed zeros are not distinct, using ``inverse_indices`` to reconstruct the input array is not guaranteed to return an array having the exact same values. + + Parameters + ---------- + x: array + input array. If ``x`` has more than one dimension, the function must flatten ``x`` and return the unique elements of the flattened array. + + Returns + ------- + out: Tuple[array, array] + a namedtuple ``(values, inverse_indices)`` whose + + - first element must have the field name ``values`` and must be a one-dimensional array containing the unique elements of ``x``. The array must have the same data type as ``x``. + - second element must have the field name ``inverse_indices`` and must be an array containing the indices of ``values`` that reconstruct ``x``. The array must have the same shape as ``x`` and have the default array index data type. + + .. note:: + The order of unique elements is not specified and may vary between implementations. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + + .. versionchanged:: 2023.12 + Clarified flattening behavior. + """ + + +def unique_values(x: array, /) -> array: + """ + Returns the unique elements of an input array ``x``. + + .. admonition:: Data-dependent output shape + :class: important + + The shapes of two of the output arrays for this function depend on the data values in the input array; hence, array libraries which build computation graphs (e.g., JAX, Dask, etc.) may find this function difficult to implement without knowing array values. Accordingly, such libraries may choose to omit this function. See :ref:`data-dependent-output-shapes` section for more details. + + .. note:: + Uniqueness should be determined based on value equality (see :func:`~array_api.equal`). For input arrays having floating-point data types, value-based equality implies the following behavior. + + - As ``nan`` values compare as ``False``, ``nan`` values should be considered distinct. + - As complex floating-point values having at least one ``nan`` component compare as ``False``, complex floating-point values having ``nan`` components should be considered distinct. + - As ``-0`` and ``+0`` compare as ``True``, signed zeros should not be considered distinct, and the corresponding unique element will be implementation-dependent (e.g., an implementation could choose to return ``-0`` if ``-0`` occurs before ``+0``). + + Parameters + ---------- + x: array + input array. If ``x`` has more than one dimension, the function must flatten ``x`` and return the unique elements of the flattened array. + + Returns + ------- + out: array + a one-dimensional array containing the set of unique elements in ``x``. The returned array must have the same data type as ``x``. + + .. note:: + The order of unique elements is not specified and may vary between implementations. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + + .. versionchanged:: 2023.12 + Required that the output array must be one-dimensional. + """ diff --git a/src/array_api_stubs/_2023_12/sorting_functions.py b/src/array_api_stubs/_2023_12/sorting_functions.py new file mode 100644 index 000000000..2dc4ac410 --- /dev/null +++ b/src/array_api_stubs/_2023_12/sorting_functions.py @@ -0,0 +1,58 @@ +__all__ = ["argsort", "sort"] + + +from ._types import array + + +def argsort( + x: array, /, *, axis: int = -1, descending: bool = False, stable: bool = True +) -> array: + """ + Returns the indices that sort an array ``x`` along a specified axis. + + .. note:: + For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-dependent (see :ref:`complex-number-ordering`). + + Parameters + ---------- + x : array + input array. Should have a real-valued data type. + axis: int + axis along which to sort. If set to ``-1``, the function must sort along the last axis. Default: ``-1``. + descending: bool + sort order. If ``True``, the returned indices sort ``x`` in descending order (by value). If ``False``, the returned indices sort ``x`` in ascending order (by value). Default: ``False``. + stable: bool + sort stability. If ``True``, the returned indices must maintain the relative order of ``x`` values which compare as equal. If ``False``, the returned indices may or may not maintain the relative order of ``x`` values which compare as equal (i.e., the relative order of ``x`` values which compare as equal is implementation-dependent). Default: ``True``. + + Returns + ------- + out : array + an array of indices. The returned array must have the same shape as ``x``. The returned array must have the default array index data type. + """ + + +def sort( + x: array, /, *, axis: int = -1, descending: bool = False, stable: bool = True +) -> array: + """ + Returns a sorted copy of an input array ``x``. + + .. note:: + For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-dependent (see :ref:`complex-number-ordering`). + + Parameters + ---------- + x: array + input array. Should have a real-valued data type. + axis: int + axis along which to sort. If set to ``-1``, the function must sort along the last axis. Default: ``-1``. + descending: bool + sort order. If ``True``, the array must be sorted in descending order (by value). If ``False``, the array must be sorted in ascending order (by value). Default: ``False``. + stable: bool + sort stability. If ``True``, the returned array must maintain the relative order of ``x`` values which compare as equal. If ``False``, the returned array may or may not maintain the relative order of ``x`` values which compare as equal (i.e., the relative order of ``x`` values which compare as equal is implementation-dependent). Default: ``True``. + + Returns + ------- + out : array + a sorted array. The returned array must have the same data type and shape as ``x``. + """ diff --git a/src/array_api_stubs/_2023_12/statistical_functions.py b/src/array_api_stubs/_2023_12/statistical_functions.py new file mode 100644 index 000000000..9d3563e26 --- /dev/null +++ b/src/array_api_stubs/_2023_12/statistical_functions.py @@ -0,0 +1,374 @@ +__all__ = ["cumulative_sum", "max", "mean", "min", "prod", "std", "sum", "var"] + + +from ._types import Optional, Tuple, Union, array, dtype + + +def cumulative_sum( + x: array, + /, + *, + axis: Optional[int] = None, + dtype: Optional[dtype] = None, + include_initial: bool = False, +) -> array: + """ + Calculates the cumulative sum of elements in the input array ``x``. + + Parameters + ---------- + x: array + input array. Should have a numeric data type. + axis: Optional[int] + axis along which a cumulative sum must be computed. If ``axis`` is negative, the function must determine the axis along which to compute a cumulative sum by counting from the last dimension. + + If ``x`` is a one-dimensional array, providing an ``axis`` is optional; however, if ``x`` has more than one dimension, providing an ``axis`` is required. + + dtype: Optional[dtype] + data type of the returned array. If ``None``, the returned array must have the same data type as ``x``, unless ``x`` has an integer data type supporting a smaller range of values than the default integer data type (e.g., ``x`` has an ``int16`` or ``uint32`` data type and the default integer data type is ``int64``). In those latter cases: + + - if ``x`` has a signed integer data type (e.g., ``int16``), the returned array must have the default integer data type. + - if ``x`` has an unsigned integer data type (e.g., ``uint16``), the returned array must have an unsigned integer data type having the same number of bits as the default integer data type (e.g., if the default integer data type is ``int32``, the returned array must have a ``uint32`` data type). + + If the data type (either specified or resolved) differs from the data type of ``x``, the input array should be cast to the specified data type before computing the sum (rationale: the ``dtype`` keyword argument is intended to help prevent overflows). Default: ``None``. + + include_initial: bool + boolean indicating whether to include the initial value as the first value in the output. By convention, the initial value must be the additive identity (i.e., zero). Default: ``False``. + + Returns + ------- + out: array + an array containing the cumulative sums. The returned array must have a data type as described by the ``dtype`` parameter above. + + Let ``N`` be the size of the axis along which to compute the cumulative sum. The returned array must have a shape determined according to the following rules: + + - if ``include_initial`` is ``True``, the returned array must have the same shape as ``x``, except the size of the axis along which to compute the cumulative sum must be ``N+1``. + - if ``include_initial`` is ``False``, the returned array must have the same shape as ``x``. + + Notes + ----- + + **Special Cases** + + For both real-valued and complex floating-point operands, special cases must be handled as if the operation is implemented by successive application of :func:`~array_api.add`. + + .. versionadded:: 2023.12 + """ + + +def max( + x: array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + keepdims: bool = False, +) -> array: + """ + Calculates the maximum value of the input array ``x``. + + Parameters + ---------- + x: array + input array. Should have a real-valued data type. + axis: Optional[Union[int, Tuple[int, ...]]] + axis or axes along which maximum values must be computed. By default, the maximum value must be computed over the entire array. If a tuple of integers, maximum values must be computed over multiple axes. Default: ``None``. + keepdims: bool + if ``True``, the reduced axes (dimensions) must be included in the result as singleton dimensions, and, accordingly, the result must be compatible with the input array (see :ref:`broadcasting`). Otherwise, if ``False``, the reduced axes (dimensions) must not be included in the result. Default: ``False``. + + Returns + ------- + out: array + if the maximum value was computed over the entire array, a zero-dimensional array containing the maximum value; otherwise, a non-zero-dimensional array containing the maximum values. The returned array must have the same data type as ``x``. + + Notes + ----- + + When the number of elements over which to compute the maximum value is zero, the maximum value is implementation-defined. Specification-compliant libraries may choose to raise an error, return a sentinel value (e.g., if ``x`` is a floating-point input array, return ``NaN``), or return the minimum possible value for the input array ``x`` data type (e.g., if ``x`` is a floating-point array, return ``-infinity``). + + The order of signed zeros is unspecified and thus implementation-defined. When choosing between ``-0`` or ``+0`` as a maximum value, specification-compliant libraries may choose to return either value. + + For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-defined (see :ref:`complex-number-ordering`). + + **Special Cases** + + For floating-point operands, + + - If ``x_i`` is ``NaN``, the maximum value is ``NaN`` (i.e., ``NaN`` values propagate). + + .. versionchanged:: 2023.12 + Clarified that the order of signed zeros is implementation-defined. + """ + + +def mean( + x: array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + keepdims: bool = False, +) -> array: + """ + Calculates the arithmetic mean of the input array ``x``. + + Parameters + ---------- + x: array + input array. Should have a real-valued floating-point data type. + axis: Optional[Union[int, Tuple[int, ...]]] + axis or axes along which arithmetic means must be computed. By default, the mean must be computed over the entire array. If a tuple of integers, arithmetic means must be computed over multiple axes. Default: ``None``. + keepdims: bool + if ``True``, the reduced axes (dimensions) must be included in the result as singleton dimensions, and, accordingly, the result must be compatible with the input array (see :ref:`broadcasting`). Otherwise, if ``False``, the reduced axes (dimensions) must not be included in the result. Default: ``False``. + + Returns + ------- + out: array + if the arithmetic mean was computed over the entire array, a zero-dimensional array containing the arithmetic mean; otherwise, a non-zero-dimensional array containing the arithmetic means. The returned array must have the same data type as ``x``. + + .. note:: + While this specification recommends that this function only accept input arrays having a real-valued floating-point data type, specification-compliant array libraries may choose to accept input arrays having an integer data type. While mixed data type promotion is implementation-defined, if the input array ``x`` has an integer data type, the returned array must have the default real-valued floating-point data type. + + Notes + ----- + + **Special Cases** + + Let ``N`` equal the number of elements over which to compute the arithmetic mean. + + - If ``N`` is ``0``, the arithmetic mean is ``NaN``. + - If ``x_i`` is ``NaN``, the arithmetic mean is ``NaN`` (i.e., ``NaN`` values propagate). + """ + + +def min( + x: array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + keepdims: bool = False, +) -> array: + """ + Calculates the minimum value of the input array ``x``. + + Parameters + ---------- + x: array + input array. Should have a real-valued data type. + axis: Optional[Union[int, Tuple[int, ...]]] + axis or axes along which minimum values must be computed. By default, the minimum value must be computed over the entire array. If a tuple of integers, minimum values must be computed over multiple axes. Default: ``None``. + keepdims: bool + if ``True``, the reduced axes (dimensions) must be included in the result as singleton dimensions, and, accordingly, the result must be compatible with the input array (see :ref:`broadcasting`). Otherwise, if ``False``, the reduced axes (dimensions) must not be included in the result. Default: ``False``. + + Returns + ------- + out: array + if the minimum value was computed over the entire array, a zero-dimensional array containing the minimum value; otherwise, a non-zero-dimensional array containing the minimum values. The returned array must have the same data type as ``x``. + + Notes + ----- + + When the number of elements over which to compute the minimum value is zero, the minimum value is implementation-defined. Specification-compliant libraries may choose to raise an error, return a sentinel value (e.g., if ``x`` is a floating-point input array, return ``NaN``), or return the maximum possible value for the input array ``x`` data type (e.g., if ``x`` is a floating-point array, return ``+infinity``). + + The order of signed zeros is unspecified and thus implementation-defined. When choosing between ``-0`` or ``+0`` as a minimum value, specification-compliant libraries may choose to return either value. + + For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-defined (see :ref:`complex-number-ordering`). + + **Special Cases** + + For floating-point operands, + + - If ``x_i`` is ``NaN``, the minimum value is ``NaN`` (i.e., ``NaN`` values propagate). + + .. versionchanged:: 2023.12 + Clarified that the order of signed zeros is implementation-defined. + """ + + +def prod( + x: array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + dtype: Optional[dtype] = None, + keepdims: bool = False, +) -> array: + """ + Calculates the product of input array ``x`` elements. + + Parameters + ---------- + x: array + input array. Should have a numeric data type. + axis: Optional[Union[int, Tuple[int, ...]]] + axis or axes along which products must be computed. By default, the product must be computed over the entire array. If a tuple of integers, products must be computed over multiple axes. Default: ``None``. + + dtype: Optional[dtype] + data type of the returned array. If ``None``, the returned array must have the same data type as ``x``, unless ``x`` has an integer data type supporting a smaller range of values than the default integer data type (e.g., ``x`` has an ``int16`` or ``uint32`` data type and the default integer data type is ``int64``). In those latter cases: + + - if ``x`` has a signed integer data type (e.g., ``int16``), the returned array must have the default integer data type. + - if ``x`` has an unsigned integer data type (e.g., ``uint16``), the returned array must have an unsigned integer data type having the same number of bits as the default integer data type (e.g., if the default integer data type is ``int32``, the returned array must have a ``uint32`` data type). + + If the data type (either specified or resolved) differs from the data type of ``x``, the input array should be cast to the specified data type before computing the sum (rationale: the ``dtype`` keyword argument is intended to help prevent overflows). Default: ``None``. + + keepdims: bool + if ``True``, the reduced axes (dimensions) must be included in the result as singleton dimensions, and, accordingly, the result must be compatible with the input array (see :ref:`broadcasting`). Otherwise, if ``False``, the reduced axes (dimensions) must not be included in the result. Default: ``False``. + + Returns + ------- + out: array + if the product was computed over the entire array, a zero-dimensional array containing the product; otherwise, a non-zero-dimensional array containing the products. The returned array must have a data type as described by the ``dtype`` parameter above. + + Notes + ----- + + **Special Cases** + + Let ``N`` equal the number of elements over which to compute the product. + + - If ``N`` is ``0``, the product is `1` (i.e., the empty product). + + For both real-valued and complex floating-point operands, special cases must be handled as if the operation is implemented by successive application of :func:`~array_api.multiply`. + + .. versionchanged:: 2022.12 + Added complex data type support. + + .. versionchanged:: 2023.12 + Required the function to return a floating-point array having the same data type as the input array when provided a floating-point array. + """ + + +def std( + x: array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + correction: Union[int, float] = 0.0, + keepdims: bool = False, +) -> array: + """ + Calculates the standard deviation of the input array ``x``. + + Parameters + ---------- + x: array + input array. Should have a real-valued floating-point data type. + axis: Optional[Union[int, Tuple[int, ...]]] + axis or axes along which standard deviations must be computed. By default, the standard deviation must be computed over the entire array. If a tuple of integers, standard deviations must be computed over multiple axes. Default: ``None``. + correction: Union[int, float] + degrees of freedom adjustment. Setting this parameter to a value other than ``0`` has the effect of adjusting the divisor during the calculation of the standard deviation according to ``N-c`` where ``N`` corresponds to the total number of elements over which the standard deviation is computed and ``c`` corresponds to the provided degrees of freedom adjustment. When computing the standard deviation of a population, setting this parameter to ``0`` is the standard choice (i.e., the provided array contains data constituting an entire population). When computing the corrected sample standard deviation, setting this parameter to ``1`` is the standard choice (i.e., the provided array contains data sampled from a larger population; this is commonly referred to as Bessel's correction). Default: ``0``. + keepdims: bool + if ``True``, the reduced axes (dimensions) must be included in the result as singleton dimensions, and, accordingly, the result must be compatible with the input array (see :ref:`broadcasting`). Otherwise, if ``False``, the reduced axes (dimensions) must not be included in the result. Default: ``False``. + + Returns + ------- + out: array + if the standard deviation was computed over the entire array, a zero-dimensional array containing the standard deviation; otherwise, a non-zero-dimensional array containing the standard deviations. The returned array must have the same data type as ``x``. + + .. note:: + While this specification recommends that this function only accept input arrays having a real-valued floating-point data type, specification-compliant array libraries may choose to accept input arrays having an integer data type. While mixed data type promotion is implementation-defined, if the input array ``x`` has an integer data type, the returned array must have the default real-valued floating-point data type. + + Notes + ----- + + **Special Cases** + + Let ``N`` equal the number of elements over which to compute the standard deviation. + + - If ``N - correction`` is less than or equal to ``0``, the standard deviation is ``NaN``. + - If ``x_i`` is ``NaN``, the standard deviation is ``NaN`` (i.e., ``NaN`` values propagate). + """ + + +def sum( + x: array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + dtype: Optional[dtype] = None, + keepdims: bool = False, +) -> array: + """ + Calculates the sum of the input array ``x``. + + Parameters + ---------- + x: array + input array. Should have a numeric data type. + axis: Optional[Union[int, Tuple[int, ...]]] + axis or axes along which sums must be computed. By default, the sum must be computed over the entire array. If a tuple of integers, sums must be computed over multiple axes. Default: ``None``. + + dtype: Optional[dtype] + data type of the returned array. If ``None``, the returned array must have the same data type as ``x``, unless ``x`` has an integer data type supporting a smaller range of values than the default integer data type (e.g., ``x`` has an ``int16`` or ``uint32`` data type and the default integer data type is ``int64``). In those latter cases: + + - if ``x`` has a signed integer data type (e.g., ``int16``), the returned array must have the default integer data type. + - if ``x`` has an unsigned integer data type (e.g., ``uint16``), the returned array must have an unsigned integer data type having the same number of bits as the default integer data type (e.g., if the default integer data type is ``int32``, the returned array must have a ``uint32`` data type). + + If the data type (either specified or resolved) differs from the data type of ``x``, the input array should be cast to the specified data type before computing the sum (rationale: the ``dtype`` keyword argument is intended to help prevent overflows). Default: ``None``. + + keepdims: bool + if ``True``, the reduced axes (dimensions) must be included in the result as singleton dimensions, and, accordingly, the result must be compatible with the input array (see :ref:`broadcasting`). Otherwise, if ``False``, the reduced axes (dimensions) must not be included in the result. Default: ``False``. + + Returns + ------- + out: array + if the sum was computed over the entire array, a zero-dimensional array containing the sum; otherwise, an array containing the sums. The returned array must have a data type as described by the ``dtype`` parameter above. + + Notes + ----- + + **Special Cases** + + Let ``N`` equal the number of elements over which to compute the sum. + + - If ``N`` is ``0``, the sum is ``0`` (i.e., the empty sum). + + For both real-valued and complex floating-point operands, special cases must be handled as if the operation is implemented by successive application of :func:`~array_api.add`. + + .. versionchanged:: 2022.12 + Added complex data type support. + + .. versionchanged:: 2023.12 + Required the function to return a floating-point array having the same data type as the input array when provided a floating-point array. + """ + + +def var( + x: array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + correction: Union[int, float] = 0.0, + keepdims: bool = False, +) -> array: + """ + Calculates the variance of the input array ``x``. + + Parameters + ---------- + x: array + input array. Should have a real-valued floating-point data type. + axis: Optional[Union[int, Tuple[int, ...]]] + axis or axes along which variances must be computed. By default, the variance must be computed over the entire array. If a tuple of integers, variances must be computed over multiple axes. Default: ``None``. + correction: Union[int, float] + degrees of freedom adjustment. Setting this parameter to a value other than ``0`` has the effect of adjusting the divisor during the calculation of the variance according to ``N-c`` where ``N`` corresponds to the total number of elements over which the variance is computed and ``c`` corresponds to the provided degrees of freedom adjustment. When computing the variance of a population, setting this parameter to ``0`` is the standard choice (i.e., the provided array contains data constituting an entire population). When computing the unbiased sample variance, setting this parameter to ``1`` is the standard choice (i.e., the provided array contains data sampled from a larger population; this is commonly referred to as Bessel's correction). Default: ``0``. + keepdims: bool + if ``True``, the reduced axes (dimensions) must be included in the result as singleton dimensions, and, accordingly, the result must be compatible with the input array (see :ref:`broadcasting`). Otherwise, if ``False``, the reduced axes (dimensions) must not be included in the result. Default: ``False``. + + Returns + ------- + out: array + if the variance was computed over the entire array, a zero-dimensional array containing the variance; otherwise, a non-zero-dimensional array containing the variances. The returned array must have the same data type as ``x``. + + + .. note:: + While this specification recommends that this function only accept input arrays having a real-valued floating-point data type, specification-compliant array libraries may choose to accept input arrays having an integer data type. While mixed data type promotion is implementation-defined, if the input array ``x`` has an integer data type, the returned array must have the default real-valued floating-point data type. + + Notes + ----- + + **Special Cases** + + Let ``N`` equal the number of elements over which to compute the variance. + + - If ``N - correction`` is less than or equal to ``0``, the variance is ``NaN``. + - If ``x_i`` is ``NaN``, the variance is ``NaN`` (i.e., ``NaN`` values propagate). + """ diff --git a/src/array_api_stubs/_2023_12/utility_functions.py b/src/array_api_stubs/_2023_12/utility_functions.py new file mode 100644 index 000000000..81d8dca41 --- /dev/null +++ b/src/array_api_stubs/_2023_12/utility_functions.py @@ -0,0 +1,86 @@ +__all__ = ["all", "any"] + + +from ._types import Optional, Tuple, Union, array + + +def all( + x: array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + keepdims: bool = False, +) -> array: + """ + Tests whether all input array elements evaluate to ``True`` along a specified axis. + + .. note:: + Positive infinity, negative infinity, and NaN must evaluate to ``True``. + + .. note:: + If ``x`` has a complex floating-point data type, elements having a non-zero component (real or imaginary) must evaluate to ``True``. + + .. note:: + If ``x`` is an empty array or the size of the axis (dimension) along which to evaluate elements is zero, the test result must be ``True``. + + Parameters + ---------- + x: array + input array. + axis: Optional[Union[int, Tuple[int, ...]]] + axis or axes along which to perform a logical AND reduction. By default, a logical AND reduction must be performed over the entire array. If a tuple of integers, logical AND reductions must be performed over multiple axes. A valid ``axis`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an ``axis`` is specified as a negative integer, the function must determine the axis along which to perform a reduction by counting backward from the last dimension (where ``-1`` refers to the last dimension). If provided an invalid ``axis``, the function must raise an exception. Default: ``None``. + keepdims: bool + If ``True``, the reduced axes (dimensions) must be included in the result as singleton dimensions, and, accordingly, the result must be compatible with the input array (see :ref:`broadcasting`). Otherwise, if ``False``, the reduced axes (dimensions) must not be included in the result. Default: ``False``. + + Returns + ------- + out: array + if a logical AND reduction was performed over the entire array, the returned array must be a zero-dimensional array containing the test result; otherwise, the returned array must be a non-zero-dimensional array containing the test results. The returned array must have a data type of ``bool``. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ + + +def any( + x: array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + keepdims: bool = False, +) -> array: + """ + Tests whether any input array element evaluates to ``True`` along a specified axis. + + .. note:: + Positive infinity, negative infinity, and NaN must evaluate to ``True``. + + .. note:: + If ``x`` has a complex floating-point data type, elements having a non-zero component (real or imaginary) must evaluate to ``True``. + + .. note:: + If ``x`` is an empty array or the size of the axis (dimension) along which to evaluate elements is zero, the test result must be ``False``. + + Parameters + ---------- + x: array + input array. + axis: Optional[Union[int, Tuple[int, ...]]] + axis or axes along which to perform a logical OR reduction. By default, a logical OR reduction must be performed over the entire array. If a tuple of integers, logical OR reductions must be performed over multiple axes. A valid ``axis`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an ``axis`` is specified as a negative integer, the function must determine the axis along which to perform a reduction by counting backward from the last dimension (where ``-1`` refers to the last dimension). If provided an invalid ``axis``, the function must raise an exception. Default: ``None``. + keepdims: bool + If ``True``, the reduced axes (dimensions) must be included in the result as singleton dimensions, and, accordingly, the result must be compatible with the input array (see :ref:`broadcasting`). Otherwise, if ``False``, the reduced axes (dimensions) must not be included in the result. Default: ``False``. + + Returns + ------- + out: array + if a logical OR reduction was performed over the entire array, the returned array must be a zero-dimensional array containing the test result; otherwise, the returned array must be a non-zero-dimensional array containing the test results. The returned array must have a data type of ``bool``. + + Notes + ----- + + .. versionchanged:: 2022.12 + Added complex data type support. + """ diff --git a/src/array_api_stubs/__init__.py b/src/array_api_stubs/__init__.py index 4ac3783ef..ca9122e7f 100644 --- a/src/array_api_stubs/__init__.py +++ b/src/array_api_stubs/__init__.py @@ -1 +1 @@ -from . import _2021_12, _2022_12, _draft +from . import _2021_12, _2022_12, _2023_12, _draft From ef81ddf066a3505c72baf7812364d18b9a4c728f Mon Sep 17 00:00:00 2001 From: Athan Reines Date: Mon, 26 Feb 2024 00:08:12 -0800 Subject: [PATCH 27/57] Empty commit for draft at 2023.12 From 2f95e0bb8132419fb500f4221d7027c9f66f74bd Mon Sep 17 00:00:00 2001 From: Athan Reines Date: Mon, 26 Feb 2024 00:11:52 -0800 Subject: [PATCH 28/57] build: update list of ignored files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index d4f538406..cc40a3b43 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ tmp/ *.egg-info/ *.egg dist/ +.DS_STORE From f63e9fddec99d37827e7339ea02e01203e774f2d Mon Sep 17 00:00:00 2001 From: Athan Reines Date: Mon, 26 Feb 2024 00:12:31 -0800 Subject: [PATCH 29/57] chore: remove files --- .DS_Store | Bin 6148 -> 0 bytes src/.DS_Store | Bin 6148 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store delete mode 100644 src/.DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index cb2845bfd45fe0b2d4e5d60af22acccb797086ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~F$w}f3`G;&V!>uh%V|7-Hy9Q@ffo=|Y(z!TdXDZ-CJ3(9BJu;tpJXO1`-+{7 zh-iP?&P6&AY2l_avoJ74-pEzXvXjkybvYhR$31FRAH`W)!#f%5$2NroNPq-LfCNb3 zhX~lc4QnS=8A*TyNZ?7pz7Gj*nnO!f|8yYu2mozRcEj3d323qcG>4X|sK7L)2aQ(s zF~sWL4oz_`hnA|fT{MOdjVG&3F)*#|q6rC1vkLuHq@&4g1L!&>UK-q5|WOfMZ}Ffv*yH E03NCmz5oCK diff --git a/src/.DS_Store b/src/.DS_Store deleted file mode 100644 index dd19daea5d1a7b84e37b40dd1eb49bfc451dd50b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKOG-mQ5UkcL0xrzbzd)!;&mE;Ee}t(zyiRW?uaiBQ}gHU6T7I45$QbR6ZhEQ9WTW5%(>U(?D*S^_)rPCZTp zSWi@x0#abCz-4Y1-v96DC+7b#NjoVZ1^$%+He0P%OTJR|*2&9xuWj@Py4QTu-M9`4 nL$qUJv}10(9p6Mz)-_-Ac`qCigU)==iTX3(y2zx!Un}qfHa!=x From fa04c35c87708fa565a4a8f6bb8360ae836ac5d7 Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Thu, 29 Feb 2024 06:07:32 +0100 Subject: [PATCH 30/57] Bump Sphinx and sphinx-material versions to latest releases (#757) --- doc-requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc-requirements.txt b/doc-requirements.txt index 08ced9aa2..5c0effdb8 100644 --- a/doc-requirements.txt +++ b/doc-requirements.txt @@ -1,5 +1,5 @@ -sphinx==6.2.1 -sphinx-material==0.0.30 +sphinx==7.2.6 +sphinx-material==0.0.36 myst-parser sphinx_markdown_tables sphinx_copybutton From c305b82a05fe85e47ea26b7ef61fb299b23f7ed6 Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Tue, 5 Mar 2024 16:53:57 +0100 Subject: [PATCH 31/57] Fix version switcher by using sphinxcontrib-jquery (#758) Note that this doesn't work on local doc builds, it'll now give: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at file:///path/to/array-api/_site/versions.json. (Reason: CORS request not http) --- doc-requirements.txt | 1 + src/_array_api_conf.py | 1 + 2 files changed, 2 insertions(+) diff --git a/doc-requirements.txt b/doc-requirements.txt index 5c0effdb8..3e31808f2 100644 --- a/doc-requirements.txt +++ b/doc-requirements.txt @@ -5,3 +5,4 @@ sphinx_markdown_tables sphinx_copybutton sphinx_favicon sphinx-math-dollar +sphinxcontrib-jquery diff --git a/src/_array_api_conf.py b/src/_array_api_conf.py index ec5d56d58..08929cc43 100644 --- a/src/_array_api_conf.py +++ b/src/_array_api_conf.py @@ -36,6 +36,7 @@ "sphinx_copybutton", "sphinx_favicon", "sphinx_markdown_tables", + "sphinxcontrib.jquery", ] autosummary_generate = True From 630149c51d8ec6dff62678eaec454c519a1d87dd Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 21 Mar 2024 02:21:31 -0700 Subject: [PATCH 32/57] docs: fix equation rendering in `linalg.cholesky` (#762) Closes: https://github.com/data-apis/array-api/issues/761 --- src/array_api_stubs/_2022_12/linalg.py | 2 +- src/array_api_stubs/_2023_12/linalg.py | 2 +- src/array_api_stubs/_draft/linalg.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/array_api_stubs/_2022_12/linalg.py b/src/array_api_stubs/_2022_12/linalg.py index a2207bb4c..7996d7472 100644 --- a/src/array_api_stubs/_2022_12/linalg.py +++ b/src/array_api_stubs/_2022_12/linalg.py @@ -6,7 +6,7 @@ def cholesky(x: array, /, *, upper: bool = False) -> array: r""" Returns the lower (upper) Cholesky decomposition of a complex Hermitian or real symmetric positive-definite matrix ``x``. - If ``x`` is real-valued, let :math:`\mathbb{K}` be the set of real numbers $\mathbb{R}$, and, if ``x`` is complex-valued, let $\mathbb{K}$ be the set of complex numbers $\mathbb{C}$. + If ``x`` is real-valued, let :math:`\mathbb{K}` be the set of real numbers :math:`\mathbb{R}`, and, if ``x`` is complex-valued, let :math:`\mathbb{K}` be the set of complex numbers :math:`\mathbb{C}`. The lower **Cholesky decomposition** of a complex Hermitian or real symmetric positive-definite matrix :math:`x \in\ \mathbb{K}^{n \times n}` is defined as diff --git a/src/array_api_stubs/_2023_12/linalg.py b/src/array_api_stubs/_2023_12/linalg.py index 0950e6937..49cce7160 100644 --- a/src/array_api_stubs/_2023_12/linalg.py +++ b/src/array_api_stubs/_2023_12/linalg.py @@ -33,7 +33,7 @@ def cholesky(x: array, /, *, upper: bool = False) -> array: r""" Returns the lower (upper) Cholesky decomposition of a complex Hermitian or real symmetric positive-definite matrix ``x``. - If ``x`` is real-valued, let :math:`\mathbb{K}` be the set of real numbers $\mathbb{R}$, and, if ``x`` is complex-valued, let $\mathbb{K}$ be the set of complex numbers $\mathbb{C}$. + If ``x`` is real-valued, let :math:`\mathbb{K}` be the set of real numbers :math:`\mathbb{R}`, and, if ``x`` is complex-valued, let :math:`\mathbb{K}` be the set of complex numbers :math:`\mathbb{C}`. The lower **Cholesky decomposition** of a complex Hermitian or real symmetric positive-definite matrix :math:`x \in\ \mathbb{K}^{n \times n}` is defined as diff --git a/src/array_api_stubs/_draft/linalg.py b/src/array_api_stubs/_draft/linalg.py index 0950e6937..49cce7160 100644 --- a/src/array_api_stubs/_draft/linalg.py +++ b/src/array_api_stubs/_draft/linalg.py @@ -33,7 +33,7 @@ def cholesky(x: array, /, *, upper: bool = False) -> array: r""" Returns the lower (upper) Cholesky decomposition of a complex Hermitian or real symmetric positive-definite matrix ``x``. - If ``x`` is real-valued, let :math:`\mathbb{K}` be the set of real numbers $\mathbb{R}$, and, if ``x`` is complex-valued, let $\mathbb{K}$ be the set of complex numbers $\mathbb{C}$. + If ``x`` is real-valued, let :math:`\mathbb{K}` be the set of real numbers :math:`\mathbb{R}`, and, if ``x`` is complex-valued, let :math:`\mathbb{K}` be the set of complex numbers :math:`\mathbb{C}`. The lower **Cholesky decomposition** of a complex Hermitian or real symmetric positive-definite matrix :math:`x \in\ \mathbb{K}^{n \times n}` is defined as From 91ff864decaef09a7fcca28a4b65de3c5f765d5f Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 4 Apr 2024 03:57:11 -0600 Subject: [PATCH 33/57] docs: fix typo in the changelog PR-URL: https://github.com/data-apis/array-api/pull/767 Reviewed-by: Athan Reines --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eace3f0a1..5d69bbc23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -89,7 +89,7 @@ The following is a list of breaking changes relative to the previous version of - `prod`: when provided a floating-point array, the function must return a floating-point array having the same data type ([gh-744](https://github.com/data-apis/array-api/pull/744)) - `sum`: when provided a floating-point array, the function must return a floating-point array having the same data type ([gh-744](https://github.com/data-apis/array-api/pull/744)) -- `vecdot`: only allow a negative integer for the `axis` keyword argument ([gh-740](https://github.com/data-apis/array-api/pull/740)) +- `vecdot`: only require a negative integer for the `axis` keyword argument ([gh-740](https://github.com/data-apis/array-api/pull/740)) #### Extensions @@ -104,7 +104,7 @@ The following is a list of breaking changes in specification extensions relative - `fft.irfftn`: require the output array have a real-valued floating-point data type having the same precision as the input array ([gh-720](https://github.com/data-apis/array-api/pull/720); backported to v2022.12 revision of Array API specification) - `fft.fftfreq`: require the output array have the default real-valued floating-point data type ([gh-720](https://github.com/data-apis/array-api/pull/720); backported to v2022.12 revision of Array API specification) - `fft.rfftfreq`: require the output array have the default real-valued floating-point data type ([gh-720](https://github.com/data-apis/array-api/pull/720); backported to v2022.12 revision of Array API specification) -- `linalg.cross`: broadcast only along non-compute axes and only allow a negative integer for the `axis` keyword argument ([gh-740](https://github.com/data-apis/array-api/pull/740)) +- `linalg.cross`: broadcast only along non-compute axes and only require a negative integer for the `axis` keyword argument ([gh-740](https://github.com/data-apis/array-api/pull/740)) - `linalg.trace`: when provided a floating-point array, the function must return a floating-point array having the same data type ([gh-744](https://github.com/data-apis/array-api/pull/744)) * * * From 65d3102c5210310c74c73147e751c4c567e56bb1 Mon Sep 17 00:00:00 2001 From: Meekail Zain <34613774+Micky774@users.noreply.github.com> Date: Tue, 9 Apr 2024 21:55:39 -0400 Subject: [PATCH 34/57] Corrected typo in `__bool__` with backport (#785) --- src/array_api_stubs/_2022_12/array_object.py | 2 +- src/array_api_stubs/_2023_12/array_object.py | 2 +- src/array_api_stubs/_draft/array_object.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/array_api_stubs/_2022_12/array_object.py b/src/array_api_stubs/_2022_12/array_object.py index b8f703996..f00df850b 100644 --- a/src/array_api_stubs/_2022_12/array_object.py +++ b/src/array_api_stubs/_2022_12/array_object.py @@ -237,7 +237,7 @@ def __bool__(self: array, /) -> bool: - If ``self`` is either ``+infinity`` or ``-infinity``, the result is ``True``. - If ``self`` is either ``+0`` or ``-0``, the result is ``False``. - For complex floating-point operands, special cases must be handled as if the operation is implemented as the logical AND of ``bool(real(self))`` and ``bool(imag(self))``. + For complex floating-point operands, special cases must be handled as if the operation is implemented as the logical OR of ``bool(real(self))`` and ``bool(imag(self))``. .. versionchanged:: 2022.12 Added boolean and complex data type support. diff --git a/src/array_api_stubs/_2023_12/array_object.py b/src/array_api_stubs/_2023_12/array_object.py index 6dd70c278..d71a26293 100644 --- a/src/array_api_stubs/_2023_12/array_object.py +++ b/src/array_api_stubs/_2023_12/array_object.py @@ -239,7 +239,7 @@ def __bool__(self: array, /) -> bool: - If ``self`` is either ``+infinity`` or ``-infinity``, the result is ``True``. - If ``self`` is either ``+0`` or ``-0``, the result is ``False``. - For complex floating-point operands, special cases must be handled as if the operation is implemented as the logical AND of ``bool(real(self))`` and ``bool(imag(self))``. + For complex floating-point operands, special cases must be handled as if the operation is implemented as the logical OR of ``bool(real(self))`` and ``bool(imag(self))``. **Lazy implementations** diff --git a/src/array_api_stubs/_draft/array_object.py b/src/array_api_stubs/_draft/array_object.py index 6dd70c278..d71a26293 100644 --- a/src/array_api_stubs/_draft/array_object.py +++ b/src/array_api_stubs/_draft/array_object.py @@ -239,7 +239,7 @@ def __bool__(self: array, /) -> bool: - If ``self`` is either ``+infinity`` or ``-infinity``, the result is ``True``. - If ``self`` is either ``+0`` or ``-0``, the result is ``False``. - For complex floating-point operands, special cases must be handled as if the operation is implemented as the logical AND of ``bool(real(self))`` and ``bool(imag(self))``. + For complex floating-point operands, special cases must be handled as if the operation is implemented as the logical OR of ``bool(real(self))`` and ``bool(imag(self))``. **Lazy implementations** From 63633e7f0484c7afafa66b051a7d2bc75a1b0bab Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 25 Apr 2024 15:42:45 -0600 Subject: [PATCH 35/57] Add missing return type hint to tile() (#798) * Add missing return type hint to tile() * Add tile type signature fix to the draft version --- src/array_api_stubs/_2023_12/manipulation_functions.py | 2 +- src/array_api_stubs/_draft/manipulation_functions.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/array_api_stubs/_2023_12/manipulation_functions.py b/src/array_api_stubs/_2023_12/manipulation_functions.py index 87f9511b0..7d5111135 100644 --- a/src/array_api_stubs/_2023_12/manipulation_functions.py +++ b/src/array_api_stubs/_2023_12/manipulation_functions.py @@ -316,7 +316,7 @@ def stack(arrays: Union[Tuple[array, ...], List[array]], /, *, axis: int = 0) -> """ -def tile(x: array, repetitions: Tuple[int, ...], /): +def tile(x: array, repetitions: Tuple[int, ...], /) -> array: """ Constructs an array by tiling an input array. diff --git a/src/array_api_stubs/_draft/manipulation_functions.py b/src/array_api_stubs/_draft/manipulation_functions.py index 87f9511b0..7d5111135 100644 --- a/src/array_api_stubs/_draft/manipulation_functions.py +++ b/src/array_api_stubs/_draft/manipulation_functions.py @@ -316,7 +316,7 @@ def stack(arrays: Union[Tuple[array, ...], List[array]], /, *, axis: int = 0) -> """ -def tile(x: array, repetitions: Tuple[int, ...], /): +def tile(x: array, repetitions: Tuple[int, ...], /) -> array: """ Constructs an array by tiling an input array. From 69e2733e003803eb992dc7524c3805fd06d5a0c1 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 30 Apr 2024 16:12:28 -0600 Subject: [PATCH 36/57] build: fix requirements to work with conda pip treats dashes and underscores the same but conda does not. The package names here are actually dashes (pip just replaces an underscore with a dash) so this is more correct anyway. PR-URL: https://github.com/data-apis/array-api/pull/800 Reviewed-by: Athan Reines --- doc-requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc-requirements.txt b/doc-requirements.txt index 3e31808f2..15ef0b245 100644 --- a/doc-requirements.txt +++ b/doc-requirements.txt @@ -1,8 +1,8 @@ sphinx==7.2.6 sphinx-material==0.0.36 myst-parser -sphinx_markdown_tables -sphinx_copybutton -sphinx_favicon +sphinx-markdown-tables +sphinx-copybutton +sphinx-favicon sphinx-math-dollar sphinxcontrib-jquery From 6a5767aebf512385a783b7631039398738253332 Mon Sep 17 00:00:00 2001 From: Athan Date: Wed, 1 May 2024 21:01:57 -0700 Subject: [PATCH 37/57] feat: add `max rank` to inspection API capabilities PR-URL: https://github.com/data-apis/array-api/pull/763 Closes: https://github.com/data-apis/array-api/issues/694 Reviewed-by: Ralf Gommers Reviewed-by: Leo Fang --- src/array_api_stubs/_draft/_types.py | 7 ++++++- src/array_api_stubs/_draft/info.py | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/array_api_stubs/_draft/_types.py b/src/array_api_stubs/_draft/_types.py index 7c3d903d7..f2fa356f2 100644 --- a/src/array_api_stubs/_draft/_types.py +++ b/src/array_api_stubs/_draft/_types.py @@ -140,5 +140,10 @@ def dtypes( total=False, ) Capabilities = TypedDict( - "Capabilities", {"boolean indexing": bool, "data-dependent shapes": bool} + "Capabilities", + { + "boolean indexing": bool, + "data-dependent shapes": bool, + "max rank": Optional[int], + }, ) diff --git a/src/array_api_stubs/_draft/info.py b/src/array_api_stubs/_draft/info.py index b755ca2c0..e9eb66cf1 100644 --- a/src/array_api_stubs/_draft/info.py +++ b/src/array_api_stubs/_draft/info.py @@ -56,6 +56,7 @@ def capabilities() -> Capabilities: - `"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``. - `"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``. + - `"max rank"`: maximum number of supported dimensions. If a conforming implementation supports arrays having an arbitrary number of dimensions (potentially infinite), the corresponding dictionary value must be ``None``; otherwise, the value must be a finite integer. Returns ------- From cee41670d93a73c0132dd3e2a943107bea28ecff Mon Sep 17 00:00:00 2001 From: Athan Date: Wed, 1 May 2024 21:50:36 -0700 Subject: [PATCH 38/57] feat: add `nextafter` to specification PR-URL: https://github.com/data-apis/array-api/pull/792 Closes: https://github.com/data-apis/array-api/issues/664 Reviewed-by: Ralf Gommers --- .../elementwise_functions.rst | 1 + .../_draft/elementwise_functions.py | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/spec/draft/API_specification/elementwise_functions.rst b/spec/draft/API_specification/elementwise_functions.rst index 4919cff98..a853ca18c 100644 --- a/spec/draft/API_specification/elementwise_functions.rst +++ b/spec/draft/API_specification/elementwise_functions.rst @@ -66,6 +66,7 @@ Objects in API minimum multiply negative + nextafter not_equal positive pow diff --git a/src/array_api_stubs/_draft/elementwise_functions.py b/src/array_api_stubs/_draft/elementwise_functions.py index 4462329d6..ec0b0567c 100644 --- a/src/array_api_stubs/_draft/elementwise_functions.py +++ b/src/array_api_stubs/_draft/elementwise_functions.py @@ -48,6 +48,7 @@ "minimum", "multiply", "negative", + "nextafter", "not_equal", "positive", "pow", @@ -2069,6 +2070,35 @@ def negative(x: array, /) -> array: """ +def nextafter(x1: array, x2: array, /) -> array: + """ + Returns the next representable floating-point value for each element ``x1_i`` of the input array ``x1`` in the direction of the respective element ``x2_i`` of the input array ``x2``. + + Parameters + ---------- + x1: array + first input array. Should have a real-valued floating-point data type. + x2: array + second input array. Must be compatible with ``x1`` (see :ref:`broadcasting`). Should have the same data type as ``x1``. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have the same data type as ``x1``. + + Notes + ----- + + **Special cases** + + For real-valued floating-point operands, + + - If either ``x1_i`` or ``x2_i`` is ``NaN``, the result is ``NaN``. + - If ``x1_i`` is ``-0`` and ``x2_i`` is ``+0``, the result is ``+0``. + - If ``x1_i`` is ``+0`` and ``x2_i`` is ``-0``, the result is ``-0``. + """ + + def not_equal(x1: array, x2: array, /) -> array: """ Computes the truth value of ``x1_i != x2_i`` for each element ``x1_i`` of the input array ``x1`` with the respective element ``x2_i`` of the input array ``x2``. From 25e717735b72c6b55080f48af634015d94833838 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 22 May 2024 05:07:29 -0600 Subject: [PATCH 39/57] Fixes to the array-api stubs (#806) * Replace info with __array_namespace_info__ in the stubs 'info' is not an actual top-level name in the namespace. * Use consistent wording for complex dtypes in the fft stubs * Fix some copysign special-cases for better machine readability and consistency --- src/array_api_stubs/_2022_12/fft.py | 14 +++++------ src/array_api_stubs/_2023_12/__init__.py | 2 +- .../_2023_12/elementwise_functions.py | 14 +++++------ src/array_api_stubs/_2023_12/fft.py | 24 +++++++++---------- src/array_api_stubs/_draft/__init__.py | 2 +- .../_draft/elementwise_functions.py | 14 +++++------ src/array_api_stubs/_draft/fft.py | 24 +++++++++---------- 7 files changed, 45 insertions(+), 49 deletions(-) diff --git a/src/array_api_stubs/_2022_12/fft.py b/src/array_api_stubs/_2022_12/fft.py index bdd7a9c83..f6fb3627a 100644 --- a/src/array_api_stubs/_2022_12/fft.py +++ b/src/array_api_stubs/_2022_12/fft.py @@ -35,7 +35,7 @@ def fft( Parameters ---------- x: array - input array. Should have a complex-valued floating-point data type. + input array. Should have a complex floating-point data type. n: Optional[int] number of elements over which to compute the transform along the axis (dimension) specified by ``axis``. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``M``. @@ -84,7 +84,7 @@ def ifft( Parameters ---------- x: array - input array. Should have a complex-valued floating-point data type. + input array. Should have a complex floating-point data type. n: Optional[int] number of elements over which to compute the transform along the axis (dimension) specified by ``axis``. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``M``. @@ -133,7 +133,7 @@ def fftn( Parameters ---------- x: array - input array. Should have a complex-valued floating-point data type. + input array. Should have a complex floating-point data type. s: Optional[Sequence[int]] number of elements over which to compute the transform along the axes (dimensions) specified by ``axes``. Let ``i`` be the index of the ``n``-th axis specified by ``axes`` (i.e., ``i = axes[n]``) and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers such that ``s[i]`` equals ``M[i]`` for all ``i``. @@ -188,7 +188,7 @@ def ifftn( Parameters ---------- x: array - input array. Should have a complex-valued floating-point data type. + input array. Should have a complex floating-point data type. s: Optional[Sequence[int]] number of elements over which to compute the transform along the axes (dimensions) specified by ``axes``. Let ``i`` be the index of the ``n``-th axis specified by ``axes`` (i.e., ``i = axes[n]``) and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers such that ``s[i]`` equals ``M[i]`` for all ``i``. @@ -292,7 +292,7 @@ def irfft( Parameters ---------- x: array - input array. Should have a complex-valued floating-point data type. + input array. Should have a complex floating-point data type. n: Optional[int] number of elements along the transformed axis (dimension) specified by ``axis`` in the **output array**. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``2*(M-1)``. @@ -398,7 +398,7 @@ def irfftn( Parameters ---------- x: array - input array. Should have a complex-valued floating-point data type. + input array. Should have a complex floating-point data type. s: Optional[Sequence[int]] number of elements along the transformed axes (dimensions) specified by ``axes`` in the **output array**. Let ``i`` be the index of the ``n``-th axis specified by ``axes`` (i.e., ``i = axes[n]``) and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers such that ``s[i]`` equals ``M[i]`` for all ``i``, except for the last transformed axis in which ``s[i]`` equals ``2*(M[i]-1)``. For each ``i``, let ``n`` equal ``s[i]``, except for the last transformed axis in which ``n`` equals ``s[i]//2+1``. @@ -452,7 +452,7 @@ def hfft( Parameters ---------- x: array - input array. Should have a complex-valued floating-point data type. + input array. Should have a complex floating-point data type. n: Optional[int] number of elements along the transformed axis (dimension) specified by ``axis`` in the **output array**. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``2*(M-1)``. diff --git a/src/array_api_stubs/_2023_12/__init__.py b/src/array_api_stubs/_2023_12/__init__.py index 8415f2765..537ea8f85 100644 --- a/src/array_api_stubs/_2023_12/__init__.py +++ b/src/array_api_stubs/_2023_12/__init__.py @@ -16,7 +16,7 @@ from .utility_functions import * from . import linalg from . import fft -from . import info +from .info import __array_namespace_info__ __array_api_version__: str = "YYYY.MM" diff --git a/src/array_api_stubs/_2023_12/elementwise_functions.py b/src/array_api_stubs/_2023_12/elementwise_functions.py index 4462329d6..2d4847195 100644 --- a/src/array_api_stubs/_2023_12/elementwise_functions.py +++ b/src/array_api_stubs/_2023_12/elementwise_functions.py @@ -874,14 +874,12 @@ def copysign(x1: array, x2: array, /) -> array: - If ``x2_i`` is ``NaN`` and the sign bit of ``x2_i`` is ``1``, the result is ``-|x1_i|``. - If ``x2_i`` is ``NaN`` and the sign bit of ``x2_i`` is ``0``, the result is ``|x1_i|``. - If ``x1_i`` is ``NaN``, - - - If ``x2_i`` is less than ``0``, the result is ``NaN`` with a sign bit of ``1``. - - If ``x2_i`` is ``-0``, the result is ``NaN`` with a sign bit of ``1``. - - If ``x2_i`` is ``+0``, the result is ``NaN`` with a sign bit of ``0``. - - If ``x2_i`` is greater than ``0``, the result is ``NaN`` with a sign bit of ``0``. - - If ``x2_i`` is ``NaN`` and the sign bit of ``x2_i`` is ``1``, the result is ``NaN`` with a sign bit of ``1``. - - If ``x2_i`` is ``NaN`` and the sign bit of ``x2_i`` is ``0``, the result is ``NaN`` with a sign bit of ``0``. + - If ``x1_i`` is ``NaN`` and ``x2_i`` is less than ``0``, the result is ``NaN`` with a sign bit of ``1``. + - If ``x1_i`` is ``NaN`` and ``x2_i`` is ``-0``, the result is ``NaN`` with a sign bit of ``1``. + - If ``x1_i`` is ``NaN`` and ``x2_i`` is ``+0``, the result is ``NaN`` with a sign bit of ``0``. + - If ``x1_i`` is ``NaN`` and ``x2_i`` is greater than ``0``, the result is ``NaN`` with a sign bit of ``0``. + - If ``x1_i`` is ``NaN`` and ``x2_i`` is ``NaN`` and the sign bit of ``x2_i`` is ``1``, the result is ``NaN`` with a sign bit of ``1``. + - If ``x1_i`` is ``NaN`` and ``x2_i`` is ``NaN`` and the sign bit of ``x2_i`` is ``0``, the result is ``NaN`` with a sign bit of ``0``. .. versionadded:: 2023.12 """ diff --git a/src/array_api_stubs/_2023_12/fft.py b/src/array_api_stubs/_2023_12/fft.py index 4e8131c8b..7a4538ccb 100644 --- a/src/array_api_stubs/_2023_12/fft.py +++ b/src/array_api_stubs/_2023_12/fft.py @@ -35,7 +35,7 @@ def fft( Parameters ---------- x: array - input array. Should have a complex-valued floating-point data type. + input array. Should have a complex floating-point data type. n: Optional[int] number of elements over which to compute the transform along the axis (dimension) specified by ``axis``. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``M``. @@ -66,7 +66,7 @@ def fft( .. versionadded:: 2022.12 .. versionchanged:: 2023.12 - Required the input array have a complex-valued floating-point data type and required that the output array have the same data type as the input array. + Required the input array have a complex floating-point data type and required that the output array have the same data type as the input array. """ @@ -87,7 +87,7 @@ def ifft( Parameters ---------- x: array - input array. Should have a complex-valued floating-point data type. + input array. Should have a complex floating-point data type. n: Optional[int] number of elements over which to compute the transform along the axis (dimension) specified by ``axis``. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``M``. @@ -118,7 +118,7 @@ def ifft( .. versionadded:: 2022.12 .. versionchanged:: 2023.12 - Required the input array have a complex-valued floating-point data type and required that the output array have the same data type as the input array. + Required the input array have a complex floating-point data type and required that the output array have the same data type as the input array. """ @@ -139,7 +139,7 @@ def fftn( Parameters ---------- x: array - input array. Should have a complex-valued floating-point data type. + input array. Should have a complex floating-point data type. s: Optional[Sequence[int]] number of elements over which to compute the transform along the axes (dimensions) specified by ``axes``. Let ``i`` be the index of the ``n``-th axis specified by ``axes`` (i.e., ``i = axes[n]``) and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers such that ``s[i]`` equals ``M[i]`` for all ``i``. @@ -176,7 +176,7 @@ def fftn( .. versionadded:: 2022.12 .. versionchanged:: 2023.12 - Required the input array have a complex-valued floating-point data type and required that the output array have the same data type as the input array. + Required the input array have a complex floating-point data type and required that the output array have the same data type as the input array. """ @@ -197,7 +197,7 @@ def ifftn( Parameters ---------- x: array - input array. Should have a complex-valued floating-point data type. + input array. Should have a complex floating-point data type. s: Optional[Sequence[int]] number of elements over which to compute the transform along the axes (dimensions) specified by ``axes``. Let ``i`` be the index of the ``n``-th axis specified by ``axes`` (i.e., ``i = axes[n]``) and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers such that ``s[i]`` equals ``M[i]`` for all ``i``. @@ -234,7 +234,7 @@ def ifftn( .. versionadded:: 2022.12 .. versionchanged:: 2023.12 - Required the input array have a complex-valued floating-point data type and required that the output array have the same data type as the input array. + Required the input array have a complex floating-point data type and required that the output array have the same data type as the input array. """ @@ -304,7 +304,7 @@ def irfft( Parameters ---------- x: array - input array. Should have a complex-valued floating-point data type. + input array. Should have a complex floating-point data type. n: Optional[int] number of elements along the transformed axis (dimension) specified by ``axis`` in the **output array**. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``2*(M-1)``. @@ -413,7 +413,7 @@ def irfftn( Parameters ---------- x: array - input array. Should have a complex-valued floating-point data type. + input array. Should have a complex floating-point data type. s: Optional[Sequence[int]] number of elements along the transformed axes (dimensions) specified by ``axes`` in the **output array**. Let ``i`` be the index of the ``n``-th axis specified by ``axes`` (i.e., ``i = axes[n]``) and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers such that ``s[i]`` equals ``M[i]`` for all ``i``, except for the last transformed axis in which ``s[i]`` equals ``2*(M[i]-1)``. For each ``i``, let ``n`` equal ``s[i]``, except for the last transformed axis in which ``n`` equals ``s[i]//2+1``. @@ -470,7 +470,7 @@ def hfft( Parameters ---------- x: array - input array. Should have a complex-valued floating-point data type. + input array. Should have a complex floating-point data type. n: Optional[int] number of elements along the transformed axis (dimension) specified by ``axis`` in the **output array**. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``2*(M-1)``. @@ -501,7 +501,7 @@ def hfft( .. versionadded:: 2022.12 .. versionchanged:: 2023.12 - Required the input array to have a complex-valued floating-point data type and required that the output array have a real-valued data type having the same precision as the input array. + Required the input array to have a complex floating-point data type and required that the output array have a real-valued data type having the same precision as the input array. """ diff --git a/src/array_api_stubs/_draft/__init__.py b/src/array_api_stubs/_draft/__init__.py index 8415f2765..537ea8f85 100644 --- a/src/array_api_stubs/_draft/__init__.py +++ b/src/array_api_stubs/_draft/__init__.py @@ -16,7 +16,7 @@ from .utility_functions import * from . import linalg from . import fft -from . import info +from .info import __array_namespace_info__ __array_api_version__: str = "YYYY.MM" diff --git a/src/array_api_stubs/_draft/elementwise_functions.py b/src/array_api_stubs/_draft/elementwise_functions.py index ec0b0567c..bd0fd8083 100644 --- a/src/array_api_stubs/_draft/elementwise_functions.py +++ b/src/array_api_stubs/_draft/elementwise_functions.py @@ -875,14 +875,12 @@ def copysign(x1: array, x2: array, /) -> array: - If ``x2_i`` is ``NaN`` and the sign bit of ``x2_i`` is ``1``, the result is ``-|x1_i|``. - If ``x2_i`` is ``NaN`` and the sign bit of ``x2_i`` is ``0``, the result is ``|x1_i|``. - If ``x1_i`` is ``NaN``, - - - If ``x2_i`` is less than ``0``, the result is ``NaN`` with a sign bit of ``1``. - - If ``x2_i`` is ``-0``, the result is ``NaN`` with a sign bit of ``1``. - - If ``x2_i`` is ``+0``, the result is ``NaN`` with a sign bit of ``0``. - - If ``x2_i`` is greater than ``0``, the result is ``NaN`` with a sign bit of ``0``. - - If ``x2_i`` is ``NaN`` and the sign bit of ``x2_i`` is ``1``, the result is ``NaN`` with a sign bit of ``1``. - - If ``x2_i`` is ``NaN`` and the sign bit of ``x2_i`` is ``0``, the result is ``NaN`` with a sign bit of ``0``. + - If ``x1_i`` is ``NaN`` and ``x2_i`` is less than ``0``, the result is ``NaN`` with a sign bit of ``1``. + - If ``x1_i`` is ``NaN`` and ``x2_i`` is ``-0``, the result is ``NaN`` with a sign bit of ``1``. + - If ``x1_i`` is ``NaN`` and ``x2_i`` is ``+0``, the result is ``NaN`` with a sign bit of ``0``. + - If ``x1_i`` is ``NaN`` and ``x2_i`` is greater than ``0``, the result is ``NaN`` with a sign bit of ``0``. + - If ``x1_i`` is ``NaN`` and ``x2_i`` is ``NaN`` and the sign bit of ``x2_i`` is ``1``, the result is ``NaN`` with a sign bit of ``1``. + - If ``x1_i`` is ``NaN`` and ``x2_i`` is ``NaN`` and the sign bit of ``x2_i`` is ``0``, the result is ``NaN`` with a sign bit of ``0``. .. versionadded:: 2023.12 """ diff --git a/src/array_api_stubs/_draft/fft.py b/src/array_api_stubs/_draft/fft.py index 4e8131c8b..7a4538ccb 100644 --- a/src/array_api_stubs/_draft/fft.py +++ b/src/array_api_stubs/_draft/fft.py @@ -35,7 +35,7 @@ def fft( Parameters ---------- x: array - input array. Should have a complex-valued floating-point data type. + input array. Should have a complex floating-point data type. n: Optional[int] number of elements over which to compute the transform along the axis (dimension) specified by ``axis``. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``M``. @@ -66,7 +66,7 @@ def fft( .. versionadded:: 2022.12 .. versionchanged:: 2023.12 - Required the input array have a complex-valued floating-point data type and required that the output array have the same data type as the input array. + Required the input array have a complex floating-point data type and required that the output array have the same data type as the input array. """ @@ -87,7 +87,7 @@ def ifft( Parameters ---------- x: array - input array. Should have a complex-valued floating-point data type. + input array. Should have a complex floating-point data type. n: Optional[int] number of elements over which to compute the transform along the axis (dimension) specified by ``axis``. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``M``. @@ -118,7 +118,7 @@ def ifft( .. versionadded:: 2022.12 .. versionchanged:: 2023.12 - Required the input array have a complex-valued floating-point data type and required that the output array have the same data type as the input array. + Required the input array have a complex floating-point data type and required that the output array have the same data type as the input array. """ @@ -139,7 +139,7 @@ def fftn( Parameters ---------- x: array - input array. Should have a complex-valued floating-point data type. + input array. Should have a complex floating-point data type. s: Optional[Sequence[int]] number of elements over which to compute the transform along the axes (dimensions) specified by ``axes``. Let ``i`` be the index of the ``n``-th axis specified by ``axes`` (i.e., ``i = axes[n]``) and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers such that ``s[i]`` equals ``M[i]`` for all ``i``. @@ -176,7 +176,7 @@ def fftn( .. versionadded:: 2022.12 .. versionchanged:: 2023.12 - Required the input array have a complex-valued floating-point data type and required that the output array have the same data type as the input array. + Required the input array have a complex floating-point data type and required that the output array have the same data type as the input array. """ @@ -197,7 +197,7 @@ def ifftn( Parameters ---------- x: array - input array. Should have a complex-valued floating-point data type. + input array. Should have a complex floating-point data type. s: Optional[Sequence[int]] number of elements over which to compute the transform along the axes (dimensions) specified by ``axes``. Let ``i`` be the index of the ``n``-th axis specified by ``axes`` (i.e., ``i = axes[n]``) and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers such that ``s[i]`` equals ``M[i]`` for all ``i``. @@ -234,7 +234,7 @@ def ifftn( .. versionadded:: 2022.12 .. versionchanged:: 2023.12 - Required the input array have a complex-valued floating-point data type and required that the output array have the same data type as the input array. + Required the input array have a complex floating-point data type and required that the output array have the same data type as the input array. """ @@ -304,7 +304,7 @@ def irfft( Parameters ---------- x: array - input array. Should have a complex-valued floating-point data type. + input array. Should have a complex floating-point data type. n: Optional[int] number of elements along the transformed axis (dimension) specified by ``axis`` in the **output array**. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``2*(M-1)``. @@ -413,7 +413,7 @@ def irfftn( Parameters ---------- x: array - input array. Should have a complex-valued floating-point data type. + input array. Should have a complex floating-point data type. s: Optional[Sequence[int]] number of elements along the transformed axes (dimensions) specified by ``axes`` in the **output array**. Let ``i`` be the index of the ``n``-th axis specified by ``axes`` (i.e., ``i = axes[n]``) and ``M[i]`` be the size of the input array along axis ``i``. When ``s`` is ``None``, the function must set ``s`` equal to a sequence of integers such that ``s[i]`` equals ``M[i]`` for all ``i``, except for the last transformed axis in which ``s[i]`` equals ``2*(M[i]-1)``. For each ``i``, let ``n`` equal ``s[i]``, except for the last transformed axis in which ``n`` equals ``s[i]//2+1``. @@ -470,7 +470,7 @@ def hfft( Parameters ---------- x: array - input array. Should have a complex-valued floating-point data type. + input array. Should have a complex floating-point data type. n: Optional[int] number of elements along the transformed axis (dimension) specified by ``axis`` in the **output array**. Let ``M`` be the size of the input array along the axis specified by ``axis``. When ``n`` is ``None``, the function must set ``n`` equal to ``2*(M-1)``. @@ -501,7 +501,7 @@ def hfft( .. versionadded:: 2022.12 .. versionchanged:: 2023.12 - Required the input array to have a complex-valued floating-point data type and required that the output array have a real-valued data type having the same precision as the input array. + Required the input array to have a complex floating-point data type and required that the output array have a real-valued data type having the same precision as the input array. """ From b569b039612eed2993471c129d2ab95b3d7ea95e Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 25 Jul 2024 09:41:58 -0700 Subject: [PATCH 40/57] feat: add `reciprocal` to the specification PR-URL: https://github.com/data-apis/array-api/pull/802 Closes: https://github.com/data-apis/array-api/issues/790 --- .../elementwise_functions.rst | 1 + .../_draft/elementwise_functions.py | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/spec/draft/API_specification/elementwise_functions.rst b/spec/draft/API_specification/elementwise_functions.rst index a853ca18c..9758c68db 100644 --- a/spec/draft/API_specification/elementwise_functions.rst +++ b/spec/draft/API_specification/elementwise_functions.rst @@ -71,6 +71,7 @@ Objects in API positive pow real + reciprocal remainder round sign diff --git a/src/array_api_stubs/_draft/elementwise_functions.py b/src/array_api_stubs/_draft/elementwise_functions.py index bd0fd8083..c9d5e2953 100644 --- a/src/array_api_stubs/_draft/elementwise_functions.py +++ b/src/array_api_stubs/_draft/elementwise_functions.py @@ -53,6 +53,7 @@ "positive", "pow", "real", + "reciprocal", "remainder", "round", "sign", @@ -2252,6 +2253,29 @@ def real(x: array, /) -> array: """ +def reciprocal(x: array, /) -> array: + """ + Returns the reciprocal for each element ``x_i`` of the input array ``x``. + + Parameters + ---------- + x: array + input array. Should have a floating-point data type. + + Returns + ------- + out: array + an array containing the element-wise results. The returned array must have a floating-point data type determined by :ref:`type-promotion`. + + Notes + ----- + + **Special cases** + + For floating-point operands, special cases must be handled as if the operation is implemented as ``1.0 / x`` (see :func:`~array_api.divide`). + """ + + def remainder(x1: array, x2: array, /) -> array: """ Returns the remainder of division for each element ``x1_i`` of the input array ``x1`` and the respective element ``x2_i`` of the input array ``x2``. From b93391545a8d3e4099a10bac5215bf04680c8d9a Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 25 Jul 2024 10:55:58 -0600 Subject: [PATCH 41/57] docs: clarify broadcasting semantics and output shape in `linalg.solve` (#810) PR-URL: https://github.com/data-apis/array-api/pull/810 --- src/array_api_stubs/_2022_12/linalg.py | 4 ++-- src/array_api_stubs/_2023_12/linalg.py | 4 ++-- src/array_api_stubs/_2023_12/manipulation_functions.py | 2 +- src/array_api_stubs/_draft/linalg.py | 4 ++-- src/array_api_stubs/_draft/manipulation_functions.py | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/array_api_stubs/_2022_12/linalg.py b/src/array_api_stubs/_2022_12/linalg.py index 7996d7472..b13a5bf01 100644 --- a/src/array_api_stubs/_2022_12/linalg.py +++ b/src/array_api_stubs/_2022_12/linalg.py @@ -594,12 +594,12 @@ def solve(x1: array, x2: array, /) -> array: x1: array coefficient array ``A`` having shape ``(..., M, M)`` and whose innermost two dimensions form square matrices. Must be of full rank (i.e., all rows or, equivalently, columns must be linearly independent). Should have a floating-point data type. x2: array - ordinate (or "dependent variable") array ``B``. If ``x2`` has shape ``(M,)``, ``x2`` is equivalent to an array having shape ``(..., M, 1)``. If ``x2`` has shape ``(..., M, K)``, each column ``k`` defines a set of ordinate values for which to compute a solution, and ``shape(x2)[:-1]`` must be compatible with ``shape(x1)[:-1]`` (see :ref:`broadcasting`). Should have a floating-point data type. + ordinate (or "dependent variable") array ``B``. If ``x2`` has shape ``(M,)``, ``x2`` is equivalent to an array having shape ``(..., M, 1)``. If ``x2`` has shape ``(..., M, K)``, each column ``k`` defines a set of ordinate values for which to compute a solution, and ``shape(x2)[:-2]`` must be compatible with ``shape(x1)[:-2]`` (see :ref:`broadcasting`). Should have a floating-point data type. Returns ------- out: array - an array containing the solution to the system ``AX = B`` for each square matrix. The returned array must have the same shape as ``x2`` (i.e., the array corresponding to ``B``) and must have a floating-point data type determined by :ref:`type-promotion`. + an array containing the solution to the system ``AX = B`` for each square matrix. If ``x2`` has shape ``(M,)``, the returned array must have shape equal to ``shape(x1)[:-2] + shape(x2)[-1:]``. Otherwise, if ``x2`` has shape ``(..., M, K)```, the returned array must have shape equal to ``(..., M, K)``, where ``...`` refers to the result of broadcasting ``shape(x1)[:-2]`` and ``shape(x2)[:-2]``. The returned array must have a floating-point data type determined by :ref:`type-promotion`. Notes ----- diff --git a/src/array_api_stubs/_2023_12/linalg.py b/src/array_api_stubs/_2023_12/linalg.py index 49cce7160..a1c9fe028 100644 --- a/src/array_api_stubs/_2023_12/linalg.py +++ b/src/array_api_stubs/_2023_12/linalg.py @@ -623,12 +623,12 @@ def solve(x1: array, x2: array, /) -> array: x1: array coefficient array ``A`` having shape ``(..., M, M)`` and whose innermost two dimensions form square matrices. Must be of full rank (i.e., all rows or, equivalently, columns must be linearly independent). Should have a floating-point data type. x2: array - ordinate (or "dependent variable") array ``B``. If ``x2`` has shape ``(M,)``, ``x2`` is equivalent to an array having shape ``(..., M, 1)``. If ``x2`` has shape ``(..., M, K)``, each column ``k`` defines a set of ordinate values for which to compute a solution, and ``shape(x2)[:-1]`` must be compatible with ``shape(x1)[:-1]`` (see :ref:`broadcasting`). Should have a floating-point data type. + ordinate (or "dependent variable") array ``B``. If ``x2`` has shape ``(M,)``, ``x2`` is equivalent to an array having shape ``(..., M, 1)``. If ``x2`` has shape ``(..., M, K)``, each column ``k`` defines a set of ordinate values for which to compute a solution, and ``shape(x2)[:-2]`` must be compatible with ``shape(x1)[:-2]`` (see :ref:`broadcasting`). Should have a floating-point data type. Returns ------- out: array - an array containing the solution to the system ``AX = B`` for each square matrix. The returned array must have the same shape as ``x2`` (i.e., the array corresponding to ``B``) and must have a floating-point data type determined by :ref:`type-promotion`. + an array containing the solution to the system ``AX = B`` for each square matrix. If ``x2`` has shape ``(M,)``, the returned array must have shape equal to ``shape(x1)[:-2] + shape(x2)[-1:]``. Otherwise, if ``x2`` has shape ``(..., M, K)```, the returned array must have shape equal to ``(..., M, K)``, where ``...`` refers to the result of broadcasting ``shape(x1)[:-2]`` and ``shape(x2)[:-2]``. The returned array must have a floating-point data type determined by :ref:`type-promotion`. Notes ----- diff --git a/src/array_api_stubs/_2023_12/manipulation_functions.py b/src/array_api_stubs/_2023_12/manipulation_functions.py index 7d5111135..131b81eb3 100644 --- a/src/array_api_stubs/_2023_12/manipulation_functions.py +++ b/src/array_api_stubs/_2023_12/manipulation_functions.py @@ -347,7 +347,7 @@ def tile(x: array, repetitions: Tuple[int, ...], /) -> array: def unstack(x: array, /, *, axis: int = 0) -> Tuple[array, ...]: """ - Splits an array in a sequence of arrays along the given axis. + Splits an array into a sequence of arrays along the given axis. Parameters ---------- diff --git a/src/array_api_stubs/_draft/linalg.py b/src/array_api_stubs/_draft/linalg.py index 49cce7160..a1c9fe028 100644 --- a/src/array_api_stubs/_draft/linalg.py +++ b/src/array_api_stubs/_draft/linalg.py @@ -623,12 +623,12 @@ def solve(x1: array, x2: array, /) -> array: x1: array coefficient array ``A`` having shape ``(..., M, M)`` and whose innermost two dimensions form square matrices. Must be of full rank (i.e., all rows or, equivalently, columns must be linearly independent). Should have a floating-point data type. x2: array - ordinate (or "dependent variable") array ``B``. If ``x2`` has shape ``(M,)``, ``x2`` is equivalent to an array having shape ``(..., M, 1)``. If ``x2`` has shape ``(..., M, K)``, each column ``k`` defines a set of ordinate values for which to compute a solution, and ``shape(x2)[:-1]`` must be compatible with ``shape(x1)[:-1]`` (see :ref:`broadcasting`). Should have a floating-point data type. + ordinate (or "dependent variable") array ``B``. If ``x2`` has shape ``(M,)``, ``x2`` is equivalent to an array having shape ``(..., M, 1)``. If ``x2`` has shape ``(..., M, K)``, each column ``k`` defines a set of ordinate values for which to compute a solution, and ``shape(x2)[:-2]`` must be compatible with ``shape(x1)[:-2]`` (see :ref:`broadcasting`). Should have a floating-point data type. Returns ------- out: array - an array containing the solution to the system ``AX = B`` for each square matrix. The returned array must have the same shape as ``x2`` (i.e., the array corresponding to ``B``) and must have a floating-point data type determined by :ref:`type-promotion`. + an array containing the solution to the system ``AX = B`` for each square matrix. If ``x2`` has shape ``(M,)``, the returned array must have shape equal to ``shape(x1)[:-2] + shape(x2)[-1:]``. Otherwise, if ``x2`` has shape ``(..., M, K)```, the returned array must have shape equal to ``(..., M, K)``, where ``...`` refers to the result of broadcasting ``shape(x1)[:-2]`` and ``shape(x2)[:-2]``. The returned array must have a floating-point data type determined by :ref:`type-promotion`. Notes ----- diff --git a/src/array_api_stubs/_draft/manipulation_functions.py b/src/array_api_stubs/_draft/manipulation_functions.py index 7d5111135..131b81eb3 100644 --- a/src/array_api_stubs/_draft/manipulation_functions.py +++ b/src/array_api_stubs/_draft/manipulation_functions.py @@ -347,7 +347,7 @@ def tile(x: array, repetitions: Tuple[int, ...], /) -> array: def unstack(x: array, /, *, axis: int = 0) -> Tuple[array, ...]: """ - Splits an array in a sequence of arrays along the given axis. + Splits an array into a sequence of arrays along the given axis. Parameters ---------- From 6bfea7ab9b54b5862aa8a3ec6d8b1f4496b2e79b Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 25 Jul 2024 11:00:22 -0600 Subject: [PATCH 42/57] feat!: rename "max rank" to "max dimensions" in `capabilities()` PR-URL: https://github.com/data-apis/array-api/pull/809 Ref: https://github.com/data-apis/array-api/pull/763#issuecomment-2138355341 --- src/array_api_stubs/_draft/info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/array_api_stubs/_draft/info.py b/src/array_api_stubs/_draft/info.py index e9eb66cf1..1815a71e9 100644 --- a/src/array_api_stubs/_draft/info.py +++ b/src/array_api_stubs/_draft/info.py @@ -56,7 +56,7 @@ def capabilities() -> Capabilities: - `"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``. - `"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``. - - `"max rank"`: maximum number of supported dimensions. If a conforming implementation supports arrays having an arbitrary number of dimensions (potentially infinite), the corresponding dictionary value must be ``None``; otherwise, the value must be a finite integer. + - `"max dimensions"`: maximum number of supported dimensions. If a conforming implementation supports arrays having an arbitrary number of dimensions (potentially infinite), the corresponding dictionary value must be ``None``; otherwise, the value must be a finite integer. Returns ------- From b2e232b7b687ff201bab1565254c81ec2f428110 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 15 Aug 2024 16:16:30 -0600 Subject: [PATCH 43/57] docs: cross-link to the inspection page in `__array_namespace_info__` PR-URL: https://github.com/data-apis/array-api/pull/836 Reviewed-by: Athan Reines --- spec/2023.12/API_specification/inspection.rst | 2 ++ spec/draft/API_specification/inspection.rst | 2 ++ src/array_api_stubs/_2023_12/info.py | 2 ++ src/array_api_stubs/_draft/info.py | 2 ++ 4 files changed, 8 insertions(+) diff --git a/spec/2023.12/API_specification/inspection.rst b/spec/2023.12/API_specification/inspection.rst index 04691e712..89d9c602a 100644 --- a/spec/2023.12/API_specification/inspection.rst +++ b/spec/2023.12/API_specification/inspection.rst @@ -1,3 +1,5 @@ +.. _inspection: + Inspection ========== diff --git a/spec/draft/API_specification/inspection.rst b/spec/draft/API_specification/inspection.rst index 04691e712..89d9c602a 100644 --- a/spec/draft/API_specification/inspection.rst +++ b/spec/draft/API_specification/inspection.rst @@ -1,3 +1,5 @@ +.. _inspection: + Inspection ========== diff --git a/src/array_api_stubs/_2023_12/info.py b/src/array_api_stubs/_2023_12/info.py index b755ca2c0..507c853c9 100644 --- a/src/array_api_stubs/_2023_12/info.py +++ b/src/array_api_stubs/_2023_12/info.py @@ -25,6 +25,8 @@ def __array_namespace_info__() -> Info: """ Returns a namespace with Array API namespace inspection utilities. + See :ref:`inspection` for a list of inspection APIs. + Returns ------- out: Info diff --git a/src/array_api_stubs/_draft/info.py b/src/array_api_stubs/_draft/info.py index 1815a71e9..6177fb12f 100644 --- a/src/array_api_stubs/_draft/info.py +++ b/src/array_api_stubs/_draft/info.py @@ -25,6 +25,8 @@ def __array_namespace_info__() -> Info: """ Returns a namespace with Array API namespace inspection utilities. + See :ref:`inspection` for a list of inspection APIs. + Returns ------- out: Info From 0cd4bdf6a8f1b3ca62c0d0c480b60a16467bd999 Mon Sep 17 00:00:00 2001 From: Matt Haberland Date: Mon, 26 Aug 2024 19:53:23 -0700 Subject: [PATCH 44/57] docs: fix reference to eigh/eigvalsh PR-URL: https://github.com/data-apis/array-api/pull/838 Reviewed-by: Athan Reines --- spec/2021.12/extensions/linear_algebra_functions.rst | 4 ++-- spec/2022.12/extensions/linear_algebra_functions.rst | 4 ++-- spec/2023.12/extensions/linear_algebra_functions.rst | 4 ++-- spec/draft/extensions/linear_algebra_functions.rst | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/2021.12/extensions/linear_algebra_functions.rst b/spec/2021.12/extensions/linear_algebra_functions.rst index dbe643bed..de24d5a0b 100644 --- a/spec/2021.12/extensions/linear_algebra_functions.rst +++ b/spec/2021.12/extensions/linear_algebra_functions.rst @@ -48,8 +48,8 @@ Accordingly, the standardization process affords the opportunity to reduce inter In general, interfaces should avoid polymorphic return values (e.g., returning an array **or** a namedtuple, dependent on, e.g., an optional keyword argument). Dedicated interfaces for each return value type are preferred, as dedicated interfaces are easier to reason about at both the implementation level and user level. Example interfaces which could be combined into a single overloaded interface, but are not, include: - - ``eig``: computing both eigenvalues and eignvectors. - - ``eigvals``: computing only eigenvalues. + - ``eigh``: computing both eigenvalues and eigenvectors. + - ``eigvalsh``: computing only eigenvalues. 4. **Implementation agnosticism**: a standardized interface should eschew parameterization (including keyword arguments) biased toward particular implementations. diff --git a/spec/2022.12/extensions/linear_algebra_functions.rst b/spec/2022.12/extensions/linear_algebra_functions.rst index 6759b2260..938221c79 100644 --- a/spec/2022.12/extensions/linear_algebra_functions.rst +++ b/spec/2022.12/extensions/linear_algebra_functions.rst @@ -51,8 +51,8 @@ Accordingly, the standardization process affords the opportunity to reduce inter In general, interfaces should avoid polymorphic return values (e.g., returning an array **or** a namedtuple, dependent on, e.g., an optional keyword argument). Dedicated interfaces for each return value type are preferred, as dedicated interfaces are easier to reason about at both the implementation level and user level. Example interfaces which could be combined into a single overloaded interface, but are not, include: - - ``eig``: computing both eigenvalues and eignvectors. - - ``eigvals``: computing only eigenvalues. + - ``eigh``: computing both eigenvalues and eigenvectors. + - ``eigvalsh``: computing only eigenvalues. 4. **Implementation agnosticism**: a standardized interface should eschew parameterization (including keyword arguments) biased toward particular implementations. diff --git a/spec/2023.12/extensions/linear_algebra_functions.rst b/spec/2023.12/extensions/linear_algebra_functions.rst index 6759b2260..938221c79 100644 --- a/spec/2023.12/extensions/linear_algebra_functions.rst +++ b/spec/2023.12/extensions/linear_algebra_functions.rst @@ -51,8 +51,8 @@ Accordingly, the standardization process affords the opportunity to reduce inter In general, interfaces should avoid polymorphic return values (e.g., returning an array **or** a namedtuple, dependent on, e.g., an optional keyword argument). Dedicated interfaces for each return value type are preferred, as dedicated interfaces are easier to reason about at both the implementation level and user level. Example interfaces which could be combined into a single overloaded interface, but are not, include: - - ``eig``: computing both eigenvalues and eignvectors. - - ``eigvals``: computing only eigenvalues. + - ``eigh``: computing both eigenvalues and eigenvectors. + - ``eigvalsh``: computing only eigenvalues. 4. **Implementation agnosticism**: a standardized interface should eschew parameterization (including keyword arguments) biased toward particular implementations. diff --git a/spec/draft/extensions/linear_algebra_functions.rst b/spec/draft/extensions/linear_algebra_functions.rst index 6759b2260..938221c79 100644 --- a/spec/draft/extensions/linear_algebra_functions.rst +++ b/spec/draft/extensions/linear_algebra_functions.rst @@ -51,8 +51,8 @@ Accordingly, the standardization process affords the opportunity to reduce inter In general, interfaces should avoid polymorphic return values (e.g., returning an array **or** a namedtuple, dependent on, e.g., an optional keyword argument). Dedicated interfaces for each return value type are preferred, as dedicated interfaces are easier to reason about at both the implementation level and user level. Example interfaces which could be combined into a single overloaded interface, but are not, include: - - ``eig``: computing both eigenvalues and eignvectors. - - ``eigvals``: computing only eigenvalues. + - ``eigh``: computing both eigenvalues and eigenvectors. + - ``eigvalsh``: computing only eigenvalues. 4. **Implementation agnosticism**: a standardized interface should eschew parameterization (including keyword arguments) biased toward particular implementations. From 45b705de8e20a30487f1d369112a26428b5ab418 Mon Sep 17 00:00:00 2001 From: Athan Date: Wed, 18 Sep 2024 23:01:08 -0700 Subject: [PATCH 45/57] feat: add `diff` to specification PR-URL: https://github.com/data-apis/array-api/pull/791 Closes: https://github.com/data-apis/array-api/issues/784 --- .../API_specification/utility_functions.rst | 1 + .../_draft/utility_functions.py | 44 ++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/spec/draft/API_specification/utility_functions.rst b/spec/draft/API_specification/utility_functions.rst index 5105fa3df..a09c99f79 100644 --- a/spec/draft/API_specification/utility_functions.rst +++ b/spec/draft/API_specification/utility_functions.rst @@ -20,3 +20,4 @@ Objects in API all any + diff diff --git a/src/array_api_stubs/_draft/utility_functions.py b/src/array_api_stubs/_draft/utility_functions.py index 81d8dca41..cdbe4a0f8 100644 --- a/src/array_api_stubs/_draft/utility_functions.py +++ b/src/array_api_stubs/_draft/utility_functions.py @@ -1,4 +1,4 @@ -__all__ = ["all", "any"] +__all__ = ["all", "any", "diff"] from ._types import Optional, Tuple, Union, array @@ -84,3 +84,45 @@ def any( .. versionchanged:: 2022.12 Added complex data type support. """ + + +def diff( + x: array, + /, + *, + axis: int = -1, + n: int = 1, + prepend: Optional[array] = None, + append: Optional[array] = None, +) -> array: + """ + Calculates the n-th discrete forward difference along a specified axis. + + Parameters + ---------- + x: array + input array. Should have a numeric data type. + axis: int + axis along which to compute differences. A valid ``axis`` must be an integer on the interval ``[-N, N)``, where ``N`` is the rank (number of dimensions) of ``x``. If an ``axis`` is specified as a negative integer, the function must determine the axis along which to compute differences by counting backward from the last dimension (where ``-1`` refers to the last dimension). If provided an invalid ``axis``, the function must raise an exception. Default: ``-1``. + n: int + number of times to recursively compute differences. Default: ``1``. + prepend: Optional[array] + values to prepend to a specified axis prior to computing differences. Must have the same shape as ``x``, except for the axis specified by ``axis`` which may have any size. Should have the same data type as ``x``. Default: ``None``. + append: Optional[array] + values to append to a specified axis prior to computing differences. Must have the same shape as ``x``, except for the axis specified by ``axis`` which may have any size. Should have the same data type as ``x``. Default: ``None``. + + Returns + ------- + out: array + an array containing the n-th differences. Should have the same data type as ``x``. Must have the same shape as ``x``, except for the axis specified by ``axis`` which must have a size determined as follows: + + - Let ``M`` be the number of elements along an axis specified by ``axis``. + - Let ``N1`` be the number of prepended values along an axis specified by ``axis``. + - Let ``N2`` be the number of appended values along an axis specified by ``axis``. + - The final size of the axis specified by ``axis`` must be ``M + N1 + N2 - n``. + + Notes + ----- + + - The first-order differences are given by ``out[i] = x[i+1] - x[i]`` along a specified axis. Higher-order differences must be calculated recursively (e.g., by calling ``diff(out, axis=axis, n=n-1)``). + """ From 8b0e405cdbe5d56a9705bc6a1d6b534554e29d64 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 19 Sep 2024 00:06:55 -0600 Subject: [PATCH 46/57] docs: specify the behavior of `clip()` when one of the operands is NaN PR-URL: https://github.com/data-apis/array-api/pull/813 Reviewed-by: Athan Reines Reviewed-by: Ralf Gommers --- src/array_api_stubs/_2023_12/elementwise_functions.py | 6 ++++++ src/array_api_stubs/_draft/elementwise_functions.py | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/array_api_stubs/_2023_12/elementwise_functions.py b/src/array_api_stubs/_2023_12/elementwise_functions.py index 2d4847195..4739ab674 100644 --- a/src/array_api_stubs/_2023_12/elementwise_functions.py +++ b/src/array_api_stubs/_2023_12/elementwise_functions.py @@ -807,6 +807,12 @@ def clip( - If a broadcasted element in ``min`` is greater than a corresponding broadcasted element in ``max``, behavior is unspecified and thus implementation-dependent. - If ``x`` and either ``min`` or ``max`` have different data type kinds (e.g., integer versus floating-point), behavior is unspecified and thus implementation-dependent. + **Special cases** + + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If ``min_i`` is ``NaN``, the result is ``NaN``. + - If ``max_i`` is ``NaN``, the result is ``NaN``. + .. versionadded:: 2023.12 """ diff --git a/src/array_api_stubs/_draft/elementwise_functions.py b/src/array_api_stubs/_draft/elementwise_functions.py index c9d5e2953..78a76b37d 100644 --- a/src/array_api_stubs/_draft/elementwise_functions.py +++ b/src/array_api_stubs/_draft/elementwise_functions.py @@ -809,6 +809,12 @@ def clip( - If a broadcasted element in ``min`` is greater than a corresponding broadcasted element in ``max``, behavior is unspecified and thus implementation-dependent. - If ``x`` and either ``min`` or ``max`` have different data type kinds (e.g., integer versus floating-point), behavior is unspecified and thus implementation-dependent. + **Special cases** + + - If ``x_i`` is ``NaN``, the result is ``NaN``. + - If ``min_i`` is ``NaN``, the result is ``NaN``. + - If ``max_i`` is ``NaN``, the result is ``NaN``. + .. versionadded:: 2023.12 """ From b877795060f9b5ba519507ccf453af516ec11f9f Mon Sep 17 00:00:00 2001 From: "hpkfft.com" Date: Wed, 18 Sep 2024 23:26:22 -0700 Subject: [PATCH 47/57] docs: clarify `roundTiesToEven` behavior for non-ties PR-URL: https://github.com/data-apis/array-api/pull/825 Reviewed-by: Athan Reines --- spec/draft/design_topics/accuracy.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/draft/design_topics/accuracy.rst b/spec/draft/design_topics/accuracy.rst index 8c97db698..61a2c49aa 100644 --- a/spec/draft/design_topics/accuracy.rst +++ b/spec/draft/design_topics/accuracy.rst @@ -23,7 +23,7 @@ including the corresponding element-wise array APIs defined in this standard - multiply - divide -for floating-point operands must return the nearest representable value according to IEEE 754-2019 and a supported rounding mode. By default, the rounding mode should be ``roundTiesToEven`` (i.e., ties rounded toward the nearest value with an even least significant bit). +for floating-point operands must return the nearest representable value according to IEEE 754-2019 and a supported rounding mode. By default, the rounding mode should be ``roundTiesToEven`` (i.e., round to nearest with ties rounded toward the nearest value with an even least significant bit). Mathematical Functions ---------------------- From 390e9ccd27a0ee635c253a2b7acb2e338caf2c95 Mon Sep 17 00:00:00 2001 From: Athan Date: Wed, 18 Sep 2024 23:36:12 -0700 Subject: [PATCH 48/57] feat: add `take_long_axis` to specifiation PR-URL: https://github.com/data-apis/array-api/pull/816 Closes: https://github.com/data-apis/array-api/issues/808 --- .../API_specification/indexing_functions.rst | 1 + .../_draft/indexing_functions.py | 26 ++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/spec/draft/API_specification/indexing_functions.rst b/spec/draft/API_specification/indexing_functions.rst index aef298566..c13e55ecf 100644 --- a/spec/draft/API_specification/indexing_functions.rst +++ b/spec/draft/API_specification/indexing_functions.rst @@ -21,3 +21,4 @@ Objects in API :template: method.rst take + take_along_axis diff --git a/src/array_api_stubs/_draft/indexing_functions.py b/src/array_api_stubs/_draft/indexing_functions.py index 35066a4a2..a9b38b482 100644 --- a/src/array_api_stubs/_draft/indexing_functions.py +++ b/src/array_api_stubs/_draft/indexing_functions.py @@ -1,4 +1,4 @@ -__all__ = ["take"] +__all__ = ["take", "take_along_axis"] from ._types import Union, Optional, array @@ -38,3 +38,27 @@ def take(x: array, indices: array, /, *, axis: Optional[int] = None) -> array: .. versionchanged:: 2023.12 Out-of-bounds behavior is explicitly left unspecified. """ + + +def take_along_axis(x: array, indices: array, /, *, axis: int = -1) -> array: + """ + Returns elements from an array at the one-dimensional indices specified by ``indices`` along a provided ``axis``. + + Parameters + ---------- + x: array + input array. Must be compatible with ``indices``, except for the axis (dimension) specified by ``axis`` (see :ref:`broadcasting`). + indices: array + array indices. Must have the same rank (i.e., number of dimensions) as ``x``. + + .. note:: + This specification does not require bounds checking. The behavior for out-of-bounds indices is left unspecified. + + axis: int + axis along which to select values. If ``axis`` is negative, the function must determine the axis along which to select values by counting from the last dimension. Default: ``-1``. + + Returns + ------- + out: array + an array having the same data type as ``x``. Must have the same rank (i.e., number of dimensions) as ``x`` and must have a shape determined according to :ref:`broadcasting`, except for the axis (dimension) specified by ``axis`` whose size must equal the size of the corresponding axis (dimension) in ``indices``. + """ From 1765933ad23c4dc3be3ff0b29d89e47f38739432 Mon Sep 17 00:00:00 2001 From: Matt Haberland Date: Wed, 30 Oct 2024 20:30:12 -0700 Subject: [PATCH 49/57] docs: add missing subscripts in `sign` definition PR-URL: https://github.com/data-apis/array-api/pull/844 Reviewed-by: Athan Reines Reviewed-by: Oleksandr Pavlyk --- src/array_api_stubs/_2022_12/elementwise_functions.py | 2 +- src/array_api_stubs/_2023_12/elementwise_functions.py | 2 +- src/array_api_stubs/_draft/elementwise_functions.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/array_api_stubs/_2022_12/elementwise_functions.py b/src/array_api_stubs/_2022_12/elementwise_functions.py index 9139612e8..fe0f5f2b1 100644 --- a/src/array_api_stubs/_2022_12/elementwise_functions.py +++ b/src/array_api_stubs/_2022_12/elementwise_functions.py @@ -2065,7 +2065,7 @@ def sign(x: array, /) -> array: .. math:: \operatorname{sign}(x_i) = \begin{cases} 0 & \textrm{if } x_i = 0 \\ - \frac{x}{|x|} & \textrm{otherwise} + \frac{x_i}{|x_i|} & \textrm{otherwise} \end{cases} where :math:`|x_i|` is the absolute value of :math:`x_i`. diff --git a/src/array_api_stubs/_2023_12/elementwise_functions.py b/src/array_api_stubs/_2023_12/elementwise_functions.py index 4739ab674..251a770d6 100644 --- a/src/array_api_stubs/_2023_12/elementwise_functions.py +++ b/src/array_api_stubs/_2023_12/elementwise_functions.py @@ -2335,7 +2335,7 @@ def sign(x: array, /) -> array: .. math:: \operatorname{sign}(x_i) = \begin{cases} 0 & \textrm{if } x_i = 0 \\ - \frac{x}{|x|} & \textrm{otherwise} + \frac{x_i}{|x_i|} & \textrm{otherwise} \end{cases} where :math:`|x_i|` is the absolute value of :math:`x_i`. diff --git a/src/array_api_stubs/_draft/elementwise_functions.py b/src/array_api_stubs/_draft/elementwise_functions.py index 78a76b37d..3f4c08898 100644 --- a/src/array_api_stubs/_draft/elementwise_functions.py +++ b/src/array_api_stubs/_draft/elementwise_functions.py @@ -2389,7 +2389,7 @@ def sign(x: array, /) -> array: .. math:: \operatorname{sign}(x_i) = \begin{cases} 0 & \textrm{if } x_i = 0 \\ - \frac{x}{|x|} & \textrm{otherwise} + \frac{x_i}{|x_i|} & \textrm{otherwise} \end{cases} where :math:`|x_i|` is the absolute value of :math:`x_i`. From c28d3c576a90281a3f713a1e53e0565f430e1647 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 30 Oct 2024 22:18:04 -0600 Subject: [PATCH 50/57] docs: add note that iteration is defined for 1-D arrays PR-URL: https://github.com/data-apis/array-api/pull/821 Co-authored-by: Athan Reines Reviewed-by: Athan Reines --- src/array_api_stubs/_2021_12/array_object.py | 4 ++++ src/array_api_stubs/_2022_12/array_object.py | 4 ++++ src/array_api_stubs/_2023_12/array_object.py | 4 ++++ src/array_api_stubs/_draft/array_object.py | 8 ++++++++ 4 files changed, 20 insertions(+) diff --git a/src/array_api_stubs/_2021_12/array_object.py b/src/array_api_stubs/_2021_12/array_object.py index 528e0a286..07bd2c3e3 100644 --- a/src/array_api_stubs/_2021_12/array_object.py +++ b/src/array_api_stubs/_2021_12/array_object.py @@ -465,6 +465,8 @@ def __getitem__( """ Returns ``self[key]``. + See :ref:`indexing` for details on supported indexing semantics. + Parameters ---------- self: array @@ -914,6 +916,8 @@ def __setitem__( """ Sets ``self[key]`` to ``value``. + See :ref:`indexing` for details on supported indexing semantics. + Parameters ---------- self: array diff --git a/src/array_api_stubs/_2022_12/array_object.py b/src/array_api_stubs/_2022_12/array_object.py index f00df850b..83abc9310 100644 --- a/src/array_api_stubs/_2022_12/array_object.py +++ b/src/array_api_stubs/_2022_12/array_object.py @@ -489,6 +489,8 @@ def __getitem__( """ Returns ``self[key]``. + See :ref:`indexing` for details on supported indexing semantics. + Parameters ---------- self: array @@ -937,6 +939,8 @@ def __setitem__( """ Sets ``self[key]`` to ``value``. + See :ref:`indexing` for details on supported indexing semantics. + Parameters ---------- self: array diff --git a/src/array_api_stubs/_2023_12/array_object.py b/src/array_api_stubs/_2023_12/array_object.py index d71a26293..5c0b10dd9 100644 --- a/src/array_api_stubs/_2023_12/array_object.py +++ b/src/array_api_stubs/_2023_12/array_object.py @@ -616,6 +616,8 @@ def __getitem__( """ Returns ``self[key]``. + See :ref:`indexing` for details on supported indexing semantics. + Parameters ---------- self: array @@ -1085,6 +1087,8 @@ def __setitem__( """ Sets ``self[key]`` to ``value``. + See :ref:`indexing` for details on supported indexing semantics. + Parameters ---------- self: array diff --git a/src/array_api_stubs/_draft/array_object.py b/src/array_api_stubs/_draft/array_object.py index d71a26293..dcd1c53fa 100644 --- a/src/array_api_stubs/_draft/array_object.py +++ b/src/array_api_stubs/_draft/array_object.py @@ -616,6 +616,8 @@ def __getitem__( """ Returns ``self[key]``. + See :ref:`indexing` for details on supported indexing semantics. + Parameters ---------- self: array @@ -627,6 +629,10 @@ def __getitem__( ------- out: array an array containing the accessed value(s). The returned array must have the same data type as ``self``. + + .. note:: + When ``__getitem__`` is defined on an object, Python will automatically define iteration (i.e., the behavior from ``iter(x)``) as ``x[0]``, ``x[1]``, ..., ``x[N-1]``. This can also be implemented directly by defining ``__iter__``. Therefore, for a one-dimensional array ``x``, iteration should produce a sequence of zero-dimensional arrays ``x[0]``, ``x[1]``, ..., ``x[N-1]``, where ``N`` is the number of elements in the array. Iteration behavior for arrays having zero dimensions or more than one dimension is unspecified and thus implementation-defined. + """ def __gt__(self: array, other: Union[int, float, array], /) -> array: @@ -1085,6 +1091,8 @@ def __setitem__( """ Sets ``self[key]`` to ``value``. + See :ref:`indexing` for details on supported indexing semantics. + Parameters ---------- self: array From 6d205d72dde3db8fc8668ad6aef5d003cc8ef80f Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 30 Oct 2024 22:55:16 -0600 Subject: [PATCH 51/57] docs: add note that cross-kind comparisons are undefined PR-URL: https://github.com/data-apis/array-api/pull/822 Closes: https://github.com/data-apis/array-api/issues/819 Co-authored-by: Athan Reines Reviewed-by: Athan Reines --- src/array_api_stubs/_draft/array_object.py | 18 ++++++++++++++++++ .../_draft/elementwise_functions.py | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/array_api_stubs/_draft/array_object.py b/src/array_api_stubs/_draft/array_object.py index dcd1c53fa..3c6fa8763 100644 --- a/src/array_api_stubs/_draft/array_object.py +++ b/src/array_api_stubs/_draft/array_object.py @@ -513,6 +513,9 @@ def __eq__(self: array, other: Union[int, float, bool, array], /) -> array: .. note:: Element-wise results, including special cases, must equal the results returned by the equivalent element-wise function :func:`~array_api.equal`. + + .. note:: + Comparison of arrays without a corresponding promotable data type (see :ref:`type-promotion`) is undefined and thus implementation-dependent. """ def __float__(self: array, /) -> float: @@ -599,6 +602,9 @@ def __ge__(self: array, other: Union[int, float, array], /) -> array: .. note:: Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.greater_equal`. + + .. note:: + Comparison of arrays without a corresponding promotable data type (see :ref:`type-promotion`) is undefined and thus implementation-dependent. """ def __getitem__( @@ -657,6 +663,9 @@ def __gt__(self: array, other: Union[int, float, array], /) -> array: .. note:: Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.greater`. + + .. note:: + Comparison of arrays without a corresponding promotable data type (see :ref:`type-promotion`) is undefined and thus implementation-dependent. """ def __index__(self: array, /) -> int: @@ -778,6 +787,9 @@ def __le__(self: array, other: Union[int, float, array], /) -> array: .. note:: Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.less_equal`. + + .. note:: + Comparison of arrays without a corresponding promotable data type (see :ref:`type-promotion`) is undefined and thus implementation-dependent. """ def __lshift__(self: array, other: Union[int, array], /) -> array: @@ -823,6 +835,9 @@ def __lt__(self: array, other: Union[int, float, array], /) -> array: .. note:: Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.less`. + + .. note:: + Comparison of arrays without a corresponding promotable data type (see :ref:`type-promotion`) is undefined and thus implementation-dependent. """ def __matmul__(self: array, other: array, /) -> array: @@ -949,6 +964,9 @@ def __ne__(self: array, other: Union[int, float, bool, array], /) -> array: .. note:: Element-wise results, including special cases, must equal the results returned by the equivalent element-wise function :func:`~array_api.not_equal`. + .. note:: + Comparison of arrays without a corresponding promotable data type (see :ref:`type-promotion`) is undefined and thus implementation-dependent. + .. versionchanged:: 2022.12 Added complex data type support. """ diff --git a/src/array_api_stubs/_draft/elementwise_functions.py b/src/array_api_stubs/_draft/elementwise_functions.py index 3f4c08898..156715200 100644 --- a/src/array_api_stubs/_draft/elementwise_functions.py +++ b/src/array_api_stubs/_draft/elementwise_functions.py @@ -1125,6 +1125,9 @@ def equal(x1: array, x2: array, /) -> array: .. note:: For discussion of complex number equality, see :ref:`complex-numbers`. + .. note:: + Comparison of arrays without a corresponding promotable data type (see :ref:`type-promotion`) is undefined and thus implementation-dependent. + .. versionchanged:: 2022.12 Added complex data type support. """ @@ -1354,6 +1357,10 @@ def greater(x1: array, x2: array, /) -> array: ------- out: array an array containing the element-wise results. The returned array must have a data type of ``bool``. + + .. note:: + Comparison of arrays without a corresponding promotable data type (see :ref:`type-promotion`) is undefined and thus implementation-dependent. + """ @@ -1375,6 +1382,9 @@ def greater_equal(x1: array, x2: array, /) -> array: ------- out: array an array containing the element-wise results. The returned array must have a data type of ``bool``. + + .. note:: + Comparison of arrays without a corresponding promotable data type (see :ref:`type-promotion`) is undefined and thus implementation-dependent. """ @@ -1570,6 +1580,9 @@ def less(x1: array, x2: array, /) -> array: ------- out: array an array containing the element-wise results. The returned array must have a data type of ``bool``. + + .. note:: + Comparison of arrays without a corresponding promotable data type (see :ref:`type-promotion`) is undefined and thus implementation-dependent. """ @@ -1591,6 +1604,9 @@ def less_equal(x1: array, x2: array, /) -> array: ------- out: array an array containing the element-wise results. The returned array must have a data type of ``bool``. + + .. note:: + Comparison of arrays without a corresponding promotable data type (see :ref:`type-promotion`) is undefined and thus implementation-dependent. """ @@ -2141,6 +2157,9 @@ def not_equal(x1: array, x2: array, /) -> array: .. note:: For discussion of complex number equality, see :ref:`complex-numbers`. + .. note:: + Comparison of arrays without a corresponding promotable data type (see :ref:`type-promotion`) is undefined and thus implementation-dependent. + .. versionchanged:: 2022.12 Added complex data type support. """ From a7bcfe6932f71f508460257214058b8a3aef81f7 Mon Sep 17 00:00:00 2001 From: minerharry <35383543+minerharry@users.noreply.github.com> Date: Mon, 2 Dec 2024 04:56:03 -0500 Subject: [PATCH 52/57] Update copies_views_and_mutation.rst (#865) word order typo --- spec/draft/design_topics/copies_views_and_mutation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/draft/design_topics/copies_views_and_mutation.rst b/spec/draft/design_topics/copies_views_and_mutation.rst index 52be1c805..1ca5a039c 100644 --- a/spec/draft/design_topics/copies_views_and_mutation.rst +++ b/spec/draft/design_topics/copies_views_and_mutation.rst @@ -6,7 +6,7 @@ Copy-view behaviour and mutability .. admonition:: Mutating views :class: important - Array API consumers are *strongly* advised to avoid *any* mutating operations when an array object may be either a "view" (i.e., an array whose data refers to memory that belongs to another array) or own memory of which one or more other array objects may be views. This admonition may become more strict in the future (e.g., this specification may require that view mutation be prohibited and trigger an exception). Accordingly, only perform mutation operations (e.g., in-place assignment) when absolutely confident that array data belongs to one, and only one, array object. + Array API consumers are *strongly* advised to avoid *any* mutating operations when an array object may either be a "view" (i.e., an array whose data refers to memory that belongs to another array) or own memory of which one or more other array objects may be views. This admonition may become more strict in the future (e.g., this specification may require that view mutation be prohibited and trigger an exception). Accordingly, only perform mutation operations (e.g., in-place assignment) when absolutely confident that array data belongs to one, and only one, array object. Strided array implementations (e.g. NumPy, PyTorch, CuPy, MXNet) typically have the concept of a "view", meaning an array containing data in memory that From 05ec5e72b0fe7b63f9822ede7b20a5108b7b20d3 Mon Sep 17 00:00:00 2001 From: Matt Haberland Date: Wed, 11 Dec 2024 11:29:12 -0800 Subject: [PATCH 53/57] docs: clarify that implementations may add additional arguments (#870) --- spec/draft/purpose_and_scope.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/draft/purpose_and_scope.md b/spec/draft/purpose_and_scope.md index f375c9512..b2019b7dd 100644 --- a/spec/draft/purpose_and_scope.md +++ b/spec/draft/purpose_and_scope.md @@ -410,8 +410,8 @@ all the functions, arguments, data types, syntax, and semantics described in this specification. A conforming implementation of the array API standard may provide additional -values, objects, properties, data types, and functions beyond those described -in this specification. +features (e.g., values, objects, properties, data types, functions, and function +arguments) beyond those described in this specification. Libraries which aim to provide a conforming implementation but haven't yet completed such an implementation may, and are encouraged to, provide details on From 5cdcf75b66647a45374656605b66c9e922cfe01c Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 12 Dec 2024 01:13:14 -0800 Subject: [PATCH 54/57] docs: add note regarding unspecified behavior for 0d arrays in `cumulative_sum` PR-URL: https://github.com/data-apis/array-api/pull/851 Closes: https://github.com/data-apis/array-api/issues/797 --- src/array_api_stubs/_draft/statistical_functions.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/array_api_stubs/_draft/statistical_functions.py b/src/array_api_stubs/_draft/statistical_functions.py index 9d3563e26..4cb6de0a8 100644 --- a/src/array_api_stubs/_draft/statistical_functions.py +++ b/src/array_api_stubs/_draft/statistical_functions.py @@ -18,7 +18,7 @@ def cumulative_sum( Parameters ---------- x: array - input array. Should have a numeric data type. + input array. Should have one or more dimensions (axes). Should have a numeric data type. axis: Optional[int] axis along which a cumulative sum must be computed. If ``axis`` is negative, the function must determine the axis along which to compute a cumulative sum by counting from the last dimension. @@ -48,6 +48,8 @@ def cumulative_sum( Notes ----- + - When ``x`` is a zero-dimensional array, behavior is unspecified and thus implementation-defined. + **Special Cases** For both real-valued and complex floating-point operands, special cases must be handled as if the operation is implemented by successive application of :func:`~array_api.add`. From 5ffffeed9afc5643215bc051662c3647de4e8a3e Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 12 Dec 2024 02:02:44 -0800 Subject: [PATCH 55/57] feat: add `count_nonzero` to specification PR-URL: https://github.com/data-apis/array-api/pull/803 Closes: https://github.com/data-apis/array-api/issues/794 --- .../API_specification/searching_functions.rst | 1 + .../_draft/searching_functions.py | 47 +++++++++++++++---- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/spec/draft/API_specification/searching_functions.rst b/spec/draft/API_specification/searching_functions.rst index c952f1aad..1a584f158 100644 --- a/spec/draft/API_specification/searching_functions.rst +++ b/spec/draft/API_specification/searching_functions.rst @@ -22,6 +22,7 @@ Objects in API argmax argmin + count_nonzero nonzero searchsorted where diff --git a/src/array_api_stubs/_draft/searching_functions.py b/src/array_api_stubs/_draft/searching_functions.py index 029459b9a..4eee3173b 100644 --- a/src/array_api_stubs/_draft/searching_functions.py +++ b/src/array_api_stubs/_draft/searching_functions.py @@ -1,7 +1,7 @@ -__all__ = ["argmax", "argmin", "nonzero", "searchsorted", "where"] +__all__ = ["argmax", "argmin", "count_nonzero", "nonzero", "searchsorted", "where"] -from ._types import Optional, Tuple, Literal, array +from ._types import Optional, Tuple, Literal, Union, array def argmax(x: array, /, *, axis: Optional[int] = None, keepdims: bool = False) -> array: @@ -54,15 +54,41 @@ def argmin(x: array, /, *, axis: Optional[int] = None, keepdims: bool = False) - """ -def nonzero(x: array, /) -> Tuple[array, ...]: +def count_nonzero( + x: array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + keepdims: bool = False, +) -> array: """ - Returns the indices of the array elements which are non-zero. + Counts the number of array elements which are non-zero. - .. note:: - If ``x`` has a complex floating-point data type, non-zero elements are those elements having at least one component (real or imaginary) which is non-zero. + Parameters + ---------- + x: array + input array. + axis: Optional[Union[int, Tuple[int, ...]]] + axis or axes along which to count non-zero values. By default, the number of non-zero values must be computed over the entire array. If a tuple of integers, the number of non-zero values must be computed over multiple axes. Default: ``None``. + keepdims: bool + if ``True``, the reduced axes (dimensions) must be included in the result as singleton dimensions, and, accordingly, the result must be compatible with the input array (see :ref:`broadcasting`). Otherwise, if ``False``, the reduced axes (dimensions) must not be included in the result. Default: ``False``. - .. note:: - If ``x`` has a boolean data type, non-zero elements are those elements which are equal to ``True``. + Returns + ------- + out: array + if the number of non-zeros values was computed over the entire array, a zero-dimensional array containing the total number of non-zero values; otherwise, a non-zero-dimensional array containing the counts along the specified axes. The returned array must have the default array index data type. + + Notes + ----- + + - If ``x`` has a complex floating-point data type, non-zero elements are those elements having at least one component (real or imaginary) which is non-zero. + - If ``x`` has a boolean data type, non-zero elements are those elements which are equal to ``True``. + """ + + +def nonzero(x: array, /) -> Tuple[array, ...]: + """ + Returns the indices of the array elements which are non-zero. .. admonition:: Data-dependent output shape :class: admonition important @@ -76,12 +102,15 @@ def nonzero(x: array, /) -> Tuple[array, ...]: Returns ------- - out: Typle[array, ...] + out: Tuple[array, ...] a tuple of ``k`` arrays, one for each dimension of ``x`` and each of size ``n`` (where ``n`` is the total number of non-zero elements), containing the indices of the non-zero elements in that dimension. The indices must be returned in row-major, C-style order. The returned array must have the default array index data type. Notes ----- + - If ``x`` has a complex floating-point data type, non-zero elements are those elements having at least one component (real or imaginary) which is non-zero. + - If ``x`` has a boolean data type, non-zero elements are those elements which are equal to ``True``. + .. versionchanged:: 2022.12 Added complex data type support. """ From c492972ffe6db9911ffaeb222962b7ca5f916919 Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 12 Dec 2024 02:14:05 -0800 Subject: [PATCH 56/57] feat: add `cumulative_prod` specification PR-URL: https://github.com/data-apis/array-api/pull/793 Closes: https://github.com/data-apis/array-api/issues/598 --- .../statistical_functions.rst | 1 + .../_draft/statistical_functions.py | 64 ++++++++++++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/spec/draft/API_specification/statistical_functions.rst b/spec/draft/API_specification/statistical_functions.rst index 20e02b3f9..eb5e1a5d6 100644 --- a/spec/draft/API_specification/statistical_functions.rst +++ b/spec/draft/API_specification/statistical_functions.rst @@ -18,6 +18,7 @@ Objects in API :toctree: generated :template: method.rst + cumulative_prod cumulative_sum max mean diff --git a/src/array_api_stubs/_draft/statistical_functions.py b/src/array_api_stubs/_draft/statistical_functions.py index 4cb6de0a8..347a97fff 100644 --- a/src/array_api_stubs/_draft/statistical_functions.py +++ b/src/array_api_stubs/_draft/statistical_functions.py @@ -1,9 +1,71 @@ -__all__ = ["cumulative_sum", "max", "mean", "min", "prod", "std", "sum", "var"] +__all__ = [ + "cumulative_sum", + "cumulative_prod", + "max", + "mean", + "min", + "prod", + "std", + "sum", + "var", +] from ._types import Optional, Tuple, Union, array, dtype +def cumulative_prod( + x: array, + /, + *, + axis: Optional[int] = None, + dtype: Optional[dtype] = None, + include_initial: bool = False, +) -> array: + """ + Calculates the cumulative product of elements in the input array ``x``. + + Parameters + ---------- + x: array + input array. Should have one or more dimensions (axes). Should have a numeric data type. + axis: Optional[int] + axis along which a cumulative product must be computed. If ``axis`` is negative, the function must determine the axis along which to compute a cumulative product by counting from the last dimension. + + If ``x`` is a one-dimensional array, providing an ``axis`` is optional; however, if ``x`` has more than one dimension, providing an ``axis`` is required. + + dtype: Optional[dtype] + data type of the returned array. If ``None``, the returned array must have the same data type as ``x``, unless ``x`` has an integer data type supporting a smaller range of values than the default integer data type (e.g., ``x`` has an ``int16`` or ``uint32`` data type and the default integer data type is ``int64``). In those latter cases: + + - if ``x`` has a signed integer data type (e.g., ``int16``), the returned array must have the default integer data type. + - if ``x`` has an unsigned integer data type (e.g., ``uint16``), the returned array must have an unsigned integer data type having the same number of bits as the default integer data type (e.g., if the default integer data type is ``int32``, the returned array must have a ``uint32`` data type). + + If the data type (either specified or resolved) differs from the data type of ``x``, the input array should be cast to the specified data type before computing the product (rationale: the ``dtype`` keyword argument is intended to help prevent overflows). Default: ``None``. + + include_initial: bool + boolean indicating whether to include the initial value as the first value in the output. By convention, the initial value must be the multiplicative identity (i.e., one). Default: ``False``. + + Returns + ------- + out: array + an array containing the cumulative products. The returned array must have a data type as described by the ``dtype`` parameter above. + + Let ``N`` be the size of the axis along which to compute the cumulative product. The returned array must have a shape determined according to the following rules: + + - if ``include_initial`` is ``True``, the returned array must have the same shape as ``x``, except the size of the axis along which to compute the cumulative product must be ``N+1``. + - if ``include_initial`` is ``False``, the returned array must have the same shape as ``x``. + + Notes + ----- + + - When ``x`` is a zero-dimensional array, behavior is unspecified and thus implementation-defined. + + **Special Cases** + + For both real-valued and complex floating-point operands, special cases must be handled as if the operation is implemented by successive application of :func:`~array_api.multiply`. + """ + + def cumulative_sum( x: array, /, From 07e62e9f5c19012ccc034946d6e12166929422f1 Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 12 Dec 2024 02:16:25 -0800 Subject: [PATCH 57/57] docs: update description Co-authored-by: ndgrigorian <46709016+ndgrigorian@users.noreply.github.com> --- src/array_api_stubs/_draft/searching_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/array_api_stubs/_draft/searching_functions.py b/src/array_api_stubs/_draft/searching_functions.py index 62afd235a..69c0f195b 100644 --- a/src/array_api_stubs/_draft/searching_functions.py +++ b/src/array_api_stubs/_draft/searching_functions.py @@ -155,7 +155,7 @@ def top_k( mode: Literal["largest", "smallest"] = "largest", ) -> Tuple[array, array]: """ - Returns the ``k`` largest (or smallest) elements of an input array ``x`` along a specified dimension. + Returns the values and indices of the ``k`` largest (or smallest) elements of an input array ``x`` along a specified dimension. Parameters ----------