Skip to content

Commit

Permalink
Align casting methods with Python behaviour (#497)
Browse files Browse the repository at this point in the history
* Introduce conservative `x.__complex__()`

* Don't hard require same-kind arrays for builtin cast methods

* Align casting methods with Python behaviour

* Remove unnecessary `__int__` note on out-of-scope beyond bounds

* `__index__`: leave bool arrays unspecified and restrict out to `int`

* Update `__complex__` note with complex results

* Clarify `__bool__` note language

* "real-valued" -> "numeric" input array in `__bool__` note

* Clearer and spec-consistent language for casting methods

Co-authored-by: Athan <kgryte@gmail.com>

* Special case neg zeros in `x.__int__()`

* Special cases over note in `x.__bool__()`

* Special cases over notes for boolean cross-kind casting

* Add missing backticks

* Update copy

* Add special cases for real-valued floating-point operands

Co-authored-by: Athan <kgryte@gmail.com>
  • Loading branch information
honno and kgryte committed Dec 14, 2022
1 parent 7d9752f commit adca8ce
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 7 deletions.
83 changes: 76 additions & 7 deletions spec/API_specification/array_api/array_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,19 +265,58 @@ def __array_namespace__(self: array, /, *, api_version: Optional[str] = None) ->

def __bool__(self: array, /) -> bool:
"""
Converts a zero-dimensional boolean array to a Python ``bool`` object.
Converts a zero-dimensional array to a Python ``bool`` object.
**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))``.
Parameters
----------
self: array
zero-dimensional array instance. Must have a boolean data type.
zero-dimensional array instance.
Returns
-------
out: bool
a Python ``bool`` object representing the single element of the array.
"""

def __complex__(self: array, /) -> complex:
"""
Converts a zero-dimensional array to a Python ``complex`` object.
**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``.
Parameters
----------
self: array
zero-dimensional array instance.
Returns
-------
out: complex
a Python ``complex`` object representing the single element of the array instance.
"""

def __dlpack__(self: array, /, *, stream: Optional[Union[int, Any]] = None) -> PyCapsule:
"""
Exports the array for consumption by :func:`~array_api.from_dlpack` as a DLPack capsule.
Expand Down Expand Up @@ -413,12 +452,22 @@ def __eq__(self: array, other: Union[int, float, bool, array], /) -> array:

def __float__(self: array, /) -> float:
"""
Converts a zero-dimensional floating-point array to a Python ``float`` object.
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.
**Special cases**
For boolean operands,
- If ``self`` is ``True``, the result is ``1``.
- If ``self`` is ``False``, the result is ``0``.
Parameters
----------
self: array
zero-dimensional array instance. Must have a real-valued floating-point data type.
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
-------
Expand Down Expand Up @@ -561,7 +610,7 @@ def __index__(self: array, /) -> int:
Parameters
----------
self: array
zero-dimensional array instance. Must have an integer data type.
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
-------
Expand All @@ -571,17 +620,37 @@ def __index__(self: array, /) -> int:

def __int__(self: array, /) -> int:
"""
Converts a zero-dimensional integer array to a Python ``int`` object.
Converts a zero-dimensional array to a Python ``int`` object.
**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``.
Parameters
----------
self: array
zero-dimensional array instance. Must have an integer data type.
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.
**Raises**
For floating-point operands,
- If ``self`` is either ``+infinity`` or ``-infinity``, raise ``OverflowError``.
- If ``self`` is ``NaN``, raise ``ValueError``.
"""

def __invert__(self: array, /) -> array:
Expand Down
1 change: 1 addition & 0 deletions spec/API_specification/array_object.rst
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ Methods
array.__and__
array.__array_namespace__
array.__bool__
array.__complex__
array.__dlpack__
array.__dlpack_device__
array.__eq__
Expand Down

0 comments on commit adca8ce

Please sign in to comment.