Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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 @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Allowed input array of `uint64` dtype in `dpnp.bincount` [#2361](https://github.com/IntelPython/dpnp/pull/2361)
* The vector norms `ord={None, 1, 2, inf}` and the matrix norms `ord={None, 1, 2, inf, "fro", "nuc"}` now consistently return zero for empty arrays, which are arrays with at least one axis of size zero. This change affects `dpnp.linalg.norm`, `dpnp.linalg.vector_norm`, and `dpnp.linalg.matrix_norm`. Previously, dpnp would either raise errors or return zero depending on the parameters provided [#2371](https://github.com/IntelPython/dpnp/pull/2371)
* Improved performance of `dpnp.nansum`, `dpnp.nanprod`, `dpnp.nancumsum`, and `dpnp.nancumprod` by reusing `dpnp.nan_to_num` function in implementation of the functions [#2339](https://github.com/IntelPython/dpnp/pull/2339)
* Updated `dpnp.fix` to return output with the same data-type of input [#2392](https://github.com/IntelPython/dpnp/pull/2392)

### Fixed

Expand Down
10 changes: 9 additions & 1 deletion dpnp/backend/extensions/ufunc/elementwise_functions/fix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,15 @@ template <typename T>
struct OutputType
{
using value_type =
typename std::disjunction<td_ns::TypeMapResultEntry<T, sycl::half>,
typename std::disjunction<td_ns::TypeMapResultEntry<T, std::uint8_t>,
td_ns::TypeMapResultEntry<T, std::uint16_t>,
td_ns::TypeMapResultEntry<T, std::uint32_t>,
td_ns::TypeMapResultEntry<T, std::uint64_t>,
td_ns::TypeMapResultEntry<T, std::int8_t>,
td_ns::TypeMapResultEntry<T, std::int16_t>,
td_ns::TypeMapResultEntry<T, std::int32_t>,
td_ns::TypeMapResultEntry<T, std::int64_t>,
td_ns::TypeMapResultEntry<T, sycl::half>,
td_ns::TypeMapResultEntry<T, float>,
td_ns::TypeMapResultEntry<T, double>,
td_ns::DefaultResultEntry<void>>::result_type;
Expand Down
7 changes: 6 additions & 1 deletion dpnp/backend/kernels/elementwise_functions/fix.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ struct FixFunctor

resT operator()(const argT &x) const
{
return (x >= 0.0) ? sycl::floor(x) : sycl::ceil(x);
if constexpr (std::is_integral_v<argT>) {
return x;
}
else {
return (x >= 0.0) ? sycl::floor(x) : sycl::ceil(x);
}
}
};
} // namespace dpnp::kernels::fix
15 changes: 7 additions & 8 deletions dpnp/dpnp_iface_mathematical.py
Original file line number Diff line number Diff line change
Expand Up @@ -1728,14 +1728,14 @@ def ediff1d(ary, to_end=None, to_begin=None):
Round to nearest integer towards zero.

Round an array of floats element-wise to nearest integer towards zero.
The rounded values are returned as floats.
The rounded values have the same data-type as the input.

For full documentation refer to :obj:`numpy.fix`.

Parameters
----------
x : {dpnp.ndarray, usm_ndarray}
An array of floats to be rounded.
Input array, expected to have a real-valued data type.
out : {None, dpnp.ndarray, usm_ndarray}, optional
Output array to populate.
Array must have the correct shape and the expected data type.
Expand All @@ -1749,12 +1749,11 @@ def ediff1d(ary, to_end=None, to_begin=None):
Returns
-------
out : dpnp.ndarray
An array with the rounded values and with the same dimensions as the input.
The returned array will have the default floating point data type for the
device where `a` is allocated.
If `out` is ``None`` then a float array is returned with the rounded values.
Otherwise the result is stored there and the return value `out` is
a reference to that array.
An array with the same dimensions and data-type as the input.
If second argument is not supplied then a new array is returned
with the rounded values.
If a second argument is supplied the result is stored there.
The return value `out` is then a reference to that array.

Limitations
-----------
Expand Down
4 changes: 2 additions & 2 deletions dpnp/dpnp_iface_statistics.py
Original file line number Diff line number Diff line change
Expand Up @@ -797,8 +797,8 @@ def cov(
Default: ``None``.
dtype : {None, str, dtype object}, optional
Data-type of the result. By default, the return data-type will have
at least floating point type based on the capabilities of the device on
which the input arrays reside.
the default floating point data-type of the device on which the input
arrays reside.

Default: ``None``.

Expand Down
6 changes: 6 additions & 0 deletions dpnp/tests/test_binary_ufuncs.py
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,7 @@ def test_float_nan(self, dt):
expected = numpy.nextafter(numpy.nan, a)
assert_equal(result, expected)

@pytest.mark.skipif(not has_support_aspect16(), reason="no fp16 support")
@pytest.mark.parametrize("val", [0x7C00, 0x8000], ids=["val1", "val2"])
def test_f16_strides(self, val):
a = numpy.arange(val, dtype=numpy.uint16).astype(numpy.float16)
Expand All @@ -702,6 +703,7 @@ def test_f16_strides(self, val):
expected = numpy.nextafter(a[1:], -hinf)
assert_equal(result, expected)

@pytest.mark.skipif(not has_support_aspect16(), reason="no fp16 support")
@pytest.mark.parametrize("val", [0x7C00, 0x8000], ids=["val1", "val2"])
def test_f16_array_inf(self, val):
a = numpy.arange(val, dtype=numpy.uint16).astype(numpy.float16)
Expand All @@ -716,6 +718,7 @@ def test_f16_array_inf(self, val):
expected = numpy.nextafter(-hinf, a)
assert_equal(result, expected)

@pytest.mark.skipif(not has_support_aspect16(), reason="no fp16 support")
@pytest.mark.parametrize(
"sign1, sign2",
[
Expand All @@ -734,6 +737,7 @@ def test_f16_inf(self, sign1, sign2):
expected = numpy.nextafter(hinf1, hinf2)
assert_equal(result, expected)

@pytest.mark.skipif(not has_support_aspect16(), reason="no fp16 support")
@pytest.mark.parametrize("val", [0x7C00, 0x8000], ids=["val1", "val2"])
def test_f16_array_nan(self, val):
a = numpy.arange(val, dtype=numpy.uint16).astype(numpy.float16)
Expand All @@ -748,6 +752,7 @@ def test_f16_array_nan(self, val):
expected = numpy.nextafter(nan, a)
assert_equal(result, expected)

@pytest.mark.skipif(not has_support_aspect16(), reason="no fp16 support")
@pytest.mark.parametrize(
"val1, val2",
[
Expand All @@ -765,6 +770,7 @@ def test_f16_inf_nan(self, val1, val2):
expected = numpy.nextafter(v1, v2)
assert_equal(result, expected)

@pytest.mark.skipif(not has_support_aspect16(), reason="no fp16 support")
@pytest.mark.parametrize(
"val, scalar",
[
Expand Down
173 changes: 59 additions & 114 deletions dpnp/tests/test_mathematical.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,30 +36,6 @@
from .third_party.cupy import testing


def _get_output_data_type(dtype):
"""Return a data type specified by input `dtype` and device capabilities."""
dtype_float16 = any(
dpnp.issubdtype(dtype, t) for t in (dpnp.bool, dpnp.int8, dpnp.uint8)
)
dtype_float32 = any(
dpnp.issubdtype(dtype, t) for t in (dpnp.int16, dpnp.uint16)
)
if dtype_float16:
out_dtype = dpnp.float16 if has_support_aspect16() else dpnp.float32
elif dtype_float32:
out_dtype = dpnp.float32
elif dpnp.issubdtype(dtype, dpnp.complexfloating):
out_dtype = dpnp.complex64
if has_support_aspect64() and dtype != dpnp.complex64:
out_dtype = dpnp.complex128
else:
out_dtype = dpnp.float32
if has_support_aspect64() and dtype != dpnp.float32:
out_dtype = dpnp.float64

return out_dtype


@pytest.mark.parametrize("deg", [True, False])
class TestAngle:
def test_angle_bool(self, deg):
Expand Down Expand Up @@ -774,68 +750,6 @@ def test_errors(self):
assert_raises(ExecutionPlacementError, dpnp.ediff1d, ia, to_end=to_end)


class TestFix:
@pytest.mark.parametrize(
"dt", get_all_dtypes(no_none=True, no_complex=True)
)
def test_basic(self, dt):
a = get_abs_array([[1.0, 1.1, 1.5, 1.8], [-1.0, -1.1, -1.5, -1.8]], dt)
ia = dpnp.array(a)

result = dpnp.fix(ia)
expected = numpy.fix(a)
assert_array_equal(result, expected)

@pytest.mark.parametrize("xp", [numpy, dpnp])
@pytest.mark.parametrize("dt", get_complex_dtypes())
def test_complex(self, xp, dt):
a = xp.array([1.1, -1.1], dtype=dt)
with pytest.raises((ValueError, TypeError)):
xp.fix(a)

@pytest.mark.parametrize(
"a_dt", get_all_dtypes(no_none=True, no_bool=True, no_complex=True)
)
def test_out(self, a_dt):
a = get_abs_array(
[[1.0, 1.1, 1.5, 1.8], [-1.0, -1.1, -1.5, -1.8]], a_dt
)
ia = dpnp.array(a)

out_dt = _get_output_data_type(a.dtype)
out = numpy.zeros_like(a, dtype=out_dt)
iout = dpnp.array(out)

result = dpnp.fix(ia, out=iout)
expected = numpy.fix(a, out=out)
assert_array_equal(result, expected)

@pytest.mark.skipif(not has_support_aspect16(), reason="no fp16 support")
@pytest.mark.parametrize("dt", [bool, numpy.float16])
def test_out_float16(self, dt):
a = numpy.array(
[[1.0, 1.1], [1.5, 1.8], [-1.0, -1.1], [-1.5, -1.8]], dtype=dt
)
out = numpy.zeros_like(a, dtype=numpy.float16)
ia, iout = dpnp.array(a), dpnp.array(out)

result = dpnp.fix(ia, out=iout)
expected = numpy.fix(a, out=out)
assert_array_equal(result, expected)

@pytest.mark.parametrize("xp", [numpy, dpnp])
@pytest.mark.parametrize("dt", [bool] + get_integer_dtypes())
def test_out_invalid_dtype(self, xp, dt):
a = xp.array([[1.5, 1.8], [-1.0, -1.1]])
out = xp.zeros_like(a, dtype=dt)

with pytest.raises((ValueError, TypeError)):
xp.fix(a, out=out)

def test_scalar(self):
assert_raises(TypeError, dpnp.fix, -3.4)


class TestGradient:
@pytest.mark.parametrize("dt", get_all_dtypes(no_none=True, no_bool=True))
def test_basic(self, dt):
Expand Down Expand Up @@ -2291,50 +2205,81 @@ def test_projection(self, dtype):
assert dpnp.allclose(result, expected)


@pytest.mark.parametrize("func", ["ceil", "floor", "trunc"])
@pytest.mark.parametrize("func", ["ceil", "floor", "trunc", "fix"])
class TestRoundingFuncs:
@pytest.mark.parametrize(
"dtype", get_all_dtypes(no_none=True, no_complex=True)
"dt", get_all_dtypes(no_none=True, no_complex=True)
)
def test_out(self, func, dtype):
a = generate_random_numpy_array(10, dtype)
def test_basic(self, func, dt):
a = generate_random_numpy_array((2, 4), dt)
ia = dpnp.array(a)

result = getattr(dpnp, func)(ia)
expected = getattr(numpy, func)(a)
assert_array_equal(result, expected)

ia = dpnp.array(a)
out_dt = numpy.int8 if dtype == dpnp.bool else dtype
iout = dpnp.empty(expected.shape, dtype=out_dt)
@pytest.mark.parametrize("xp", [numpy, dpnp])
@pytest.mark.parametrize("dt", get_complex_dtypes())
def test_complex(self, func, xp, dt):
a = xp.array([1.1, -1.1], dtype=dt)
with pytest.raises((ValueError, TypeError)):
getattr(xp, func)(a)

@testing.with_requires("numpy>=2.1.0")
@pytest.mark.parametrize(
"dt", get_all_dtypes(no_none=True, no_complex=True)
)
def test_out(self, func, dt):
a = generate_random_numpy_array(10, dt)
dt_out = numpy.int8 if dt == dpnp.bool else dt
out = numpy.empty(a.shape, dtype=dt_out)
ia, iout = dpnp.array(a), dpnp.array(out)

expected = getattr(numpy, func)(a, out=out)
result = getattr(dpnp, func)(ia, out=iout)
assert result is iout
assert_array_equal(result, expected)

@pytest.mark.skipif(not has_support_aspect16(), reason="no fp16 support")
def test_out_float16(self, func):
a = generate_random_numpy_array((4, 2), numpy.float16)
out = numpy.zeros_like(a, dtype=numpy.float16)
ia, iout = dpnp.array(a), dpnp.array(out)

result = getattr(dpnp, func)(ia, out=iout)
expected = getattr(numpy, func)(a, out=out)
assert result is iout
# numpy.ceil, numpy.floor, numpy.trunc always return float dtype for
# NumPy < 2.1.0 while output has the dtype of input for NumPy >= 2.1.0
# (dpnp follows the latter behavior except for boolean dtype where it
# returns int8)
if numpy_version() < "2.1.0" or dtype == dpnp.bool:
check_type = False
else:
check_type = True
assert_dtype_allclose(result, expected, check_type=check_type)
assert_array_equal(result, expected)

@pytest.mark.parametrize("xp", [numpy, dpnp])
@pytest.mark.parametrize(
"dtype", get_all_dtypes(no_complex=True, no_none=True)[:-1]
"dt_out", get_all_dtypes(no_none=True, no_complex=True)[:-1]
)
def test_invalid_dtype(self, func, dtype):
dpnp_dtype = get_all_dtypes(no_complex=True, no_none=True)[-1]
ia = dpnp.arange(10, dtype=dpnp_dtype)
iout = dpnp.empty(10, dtype=dtype)

with pytest.raises(ValueError):
getattr(dpnp, func)(ia, out=iout)
def test_invalid_dtype(self, func, xp, dt_out):
dt_in = get_all_dtypes(no_none=True, no_complex=True)[-1]
a = xp.arange(10, dtype=dt_in)
out = xp.empty(10, dtype=dt_out)
if dt_out == numpy.float32 and dt_in == numpy.float64:
if xp == dpnp:
# NumPy allows "same_kind" casting, dpnp does not
assert_raises(ValueError, getattr(dpnp, func), a, out=out)
else:
numpy_error = numpy._core._exceptions._UFuncOutputCastingError
assert_raises(
(ValueError, numpy_error), getattr(xp, func), a, out=out
)

@pytest.mark.parametrize("xp", [numpy, dpnp])
@pytest.mark.parametrize(
"shape", [(0,), (15,), (2, 2)], ids=["(0,)", "(15,)", "(2, 2)"]
)
def test_invalid_shape(self, func, shape):
ia = dpnp.arange(10, dtype=dpnp.float32)
iout = dpnp.empty(shape, dtype=dpnp.float32)
def test_invalid_shape(self, func, xp, shape):
a = xp.arange(10, dtype=xp.float32)
out = xp.empty(shape, dtype=xp.float32)
assert_raises(ValueError, getattr(xp, func), a, out=out)

assert_raises(ValueError, getattr(dpnp, func), ia, out=iout)
def test_scalar(self, func):
assert_raises(TypeError, getattr(dpnp, func), -3.4)


class TestHypot:
Expand Down
Loading