Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Also, that release drops support for Python 3.9, making Python 3.10 the minimum
* Added support for the `out` keyword to accept a tuple, bringing ufunc signatures into alignment with those in NumPy [#2664](https://github.com/IntelPython/dpnp/pull/2664)
* Unified public API definitions in `dpnp.linalg` and `dpnp.scipy` submodules [#2663](https://github.com/IntelPython/dpnp/pull/2663)
* Aligned the signature of `dpnp.reshape` function with Python array API by making `shape` a required argument [#2673](https://github.com/IntelPython/dpnp/pull/2673)
* Disallowed conversion of `dpnp.ndarray` with more than one dimension to Python scalars (`int`, `float`, `complex`) to align with NumPy 2.4.0 [#2694](https://github.com/IntelPython/dpnp/pull/2694)

### Deprecated

Expand Down
11 changes: 11 additions & 0 deletions dpnp/dpnp_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ def __bytes__(self):

def __complex__(self, /):
"""Convert a zero-dimensional array to a Python complex object."""
self._check_scalar_convertible()
return self._array_obj.__complex__()

def __contains__(self, value, /):
Expand Down Expand Up @@ -300,6 +301,7 @@ def __eq__(self, other, /):

def __float__(self, /):
"""Convert a zero-dimensional array to a Python float object."""
self._check_scalar_convertible()
return self._array_obj.__float__()

def __floordiv__(self, other, /):
Expand Down Expand Up @@ -391,6 +393,7 @@ def __index__(self, /):

def __int__(self, /):
"""Convert a zero-dimensional array to a Python int object."""
self._check_scalar_convertible()
return self._array_obj.__int__()

def __invert__(self, /):
Expand Down Expand Up @@ -608,6 +611,14 @@ def __xor__(self, other, /):
r"""Return :math:`\text{self ^ value}`."""
return dpnp.bitwise_xor(self, other)

def _check_scalar_convertible(self):
"""Raise if array cannot be converted to a Python scalar."""
if self.ndim != 0:
raise TypeError(
"Only 0-dimensional dpnp.ndarray can be converted "
"to a Python scalar"
)

@staticmethod
def _create_from_usm_ndarray(usm_ary: dpt.usm_ndarray):
"""
Expand Down
70 changes: 44 additions & 26 deletions dpnp/tests/test_ndarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
assert_allclose,
assert_array_equal,
assert_equal,
assert_raises,
assert_raises_regex,
)

Expand All @@ -17,6 +18,7 @@
get_complex_dtypes,
get_float_dtypes,
has_support_aspect64,
numpy_version,
)
from .third_party.cupy import testing

Expand Down Expand Up @@ -530,34 +532,50 @@ def test_print_dpnp_zero_shape():
assert result == expected


# Numpy will raise an error when converting a.ndim > 0 to a scalar
# TODO: Discuss dpnp behavior according to these future changes
@pytest.mark.filterwarnings("ignore::DeprecationWarning")
@pytest.mark.parametrize("func", [bool, float, int, complex])
@pytest.mark.parametrize("shape", [tuple(), (1,), (1, 1), (1, 1, 1)])
@pytest.mark.parametrize(
"dtype", get_all_dtypes(no_float16=False, no_complex=True)
)
def test_scalar_type_casting(func, shape, dtype):
a = numpy.full(shape, 5, dtype=dtype)
ia = dpnp.full(shape, 5, dtype=dtype)
assert func(a) == func(ia)
class TestPythonScalarConversion:
@pytest.mark.parametrize("shape", [tuple(), (1,), (1, 1), (1, 1, 1)])
@pytest.mark.parametrize(
"dtype", get_all_dtypes(no_float16=False, no_complex=True)
)
def test_bool_conversion(shape, dtype):
a = numpy.full(shape, 5, dtype=dtype)
ia = dpnp.full(shape, 5, dtype=dtype)
assert bool(a) == bool(ia)

@pytest.mark.parametrize("shape", [tuple(), (1,), (1, 1), (1, 1, 1)])
@pytest.mark.parametrize(
"dtype", get_all_dtypes(no_float16=False, no_complex=True)
)
def test_bool_method_conversion(shape, dtype):
a = numpy.full(shape, 5, dtype=dtype)
ia = dpnp.full(shape, 5, dtype=dtype)
assert a.__bool__() == ia.__bool__()

# Numpy will raise an error when converting a.ndim > 0 to a scalar
# TODO: Discuss dpnp behavior according to these future changes
@pytest.mark.filterwarnings("ignore::DeprecationWarning")
@pytest.mark.parametrize(
"method", ["__bool__", "__float__", "__int__", "__complex__"]
)
@pytest.mark.parametrize("shape", [tuple(), (1,), (1, 1), (1, 1, 1)])
@pytest.mark.parametrize(
"dtype", get_all_dtypes(no_float16=False, no_complex=True)
)
def test_scalar_type_casting_by_method(method, shape, dtype):
a = numpy.full(shape, 4.7, dtype=dtype)
ia = dpnp.full(shape, 4.7, dtype=dtype)
assert_allclose(getattr(a, method)(), getattr(ia, method)(), rtol=1e-06)
@pytest.mark.parametrize("func", [float, int, complex])
@pytest.mark.parametrize("shape", [tuple(), (1,), (1, 1), (1, 1, 1)])
@pytest.mark.parametrize(
"dtype", get_all_dtypes(no_float16=False, no_complex=True)
)
def test_non_bool_conversion(func, shape, dtype):
a = numpy.full(shape, 5, dtype=dtype)
ia = dpnp.full(shape, 5, dtype=dtype)
assert_raises(TypeError, func(ia))

if numpy_version() >= "2.4.0":
assert_raises(TypeError, func(a))

@pytest.mark.parametrize("method", ["__float__", "__int__", "__complex__"])
@pytest.mark.parametrize("shape", [tuple(), (1,), (1, 1), (1, 1, 1)])
@pytest.mark.parametrize(
"dtype", get_all_dtypes(no_float16=False, no_complex=True)
)
def test_non_bool_method_conversion(method, shape, dtype):
a = numpy.full(shape, 5, dtype=dtype)
ia = dpnp.full(shape, 5, dtype=dtype)
assert_raises(TypeError, getattr(ia, method)())

if numpy_version() >= "2.4.0":
assert_raises(TypeError, getattr(a, method)())


@pytest.mark.parametrize("shape", [(1,), (1, 1), (1, 1, 1)])
Expand Down
Loading