Skip to content

Commit 16d6ea1

Browse files
antonwolfyvtavana
andauthored
Add implementation of dpnp.spacing function (#2125)
* Add _spacing to ufunc extension * Add implemntation of dpnp.spacing * Unmute umath tests for dpnp.spacing * Add CFD tests * Add more tests to cover different use cases * Add dedicated sign handling for float16 dtype * Fix typo in the comment * Updated comment in test * Update dpnp/dpnp_iface_mathematical.py Co-authored-by: vtavana <[email protected]> --------- Co-authored-by: vtavana <[email protected]>
1 parent 2c4f3b5 commit 16d6ea1

File tree

11 files changed

+375
-4
lines changed

11 files changed

+375
-4
lines changed

dpnp/backend/extensions/ufunc/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ set(_elementwise_sources
3838
${CMAKE_CURRENT_SOURCE_DIR}/elementwise_functions/ldexp.cpp
3939
${CMAKE_CURRENT_SOURCE_DIR}/elementwise_functions/logaddexp2.cpp
4040
${CMAKE_CURRENT_SOURCE_DIR}/elementwise_functions/radians.cpp
41+
${CMAKE_CURRENT_SOURCE_DIR}/elementwise_functions/spacing.cpp
4142
)
4243

4344
set(python_module_name _ufunc_impl)

dpnp/backend/extensions/ufunc/elementwise_functions/common.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "ldexp.hpp"
3939
#include "logaddexp2.hpp"
4040
#include "radians.hpp"
41+
#include "spacing.hpp"
4142

4243
namespace py = pybind11;
4344

@@ -61,5 +62,6 @@ void init_elementwise_functions(py::module_ m)
6162
init_ldexp(m);
6263
init_logaddexp2(m);
6364
init_radians(m);
65+
init_spacing(m);
6466
}
6567
} // namespace dpnp::extensions::ufunc
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
//*****************************************************************************
2+
// Copyright (c) 2024, Intel Corporation
3+
// All rights reserved.
4+
//
5+
// Redistribution and use in source and binary forms, with or without
6+
// modification, are permitted provided that the following conditions are met:
7+
// - Redistributions of source code must retain the above copyright notice,
8+
// this list of conditions and the following disclaimer.
9+
// - Redistributions in binary form must reproduce the above copyright notice,
10+
// this list of conditions and the following disclaimer in the documentation
11+
// and/or other materials provided with the distribution.
12+
//
13+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16+
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
17+
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18+
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19+
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20+
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21+
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22+
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23+
// THE POSSIBILITY OF SUCH DAMAGE.
24+
//*****************************************************************************
25+
26+
#include <sycl/sycl.hpp>
27+
28+
#include "dpctl4pybind11.hpp"
29+
30+
#include "kernels/elementwise_functions/spacing.hpp"
31+
#include "populate.hpp"
32+
#include "spacing.hpp"
33+
34+
// include a local copy of elementwise common header from dpctl tensor:
35+
// dpctl/tensor/libtensor/source/elementwise_functions/elementwise_functions.hpp
36+
// TODO: replace by including dpctl header once available
37+
#include "../../elementwise_functions/elementwise_functions.hpp"
38+
39+
// dpctl tensor headers
40+
#include "kernels/elementwise_functions/common.hpp"
41+
#include "utils/type_dispatch.hpp"
42+
43+
namespace dpnp::extensions::ufunc
44+
{
45+
namespace py = pybind11;
46+
namespace py_int = dpnp::extensions::py_internal;
47+
48+
namespace impl
49+
{
50+
namespace ew_cmn_ns = dpctl::tensor::kernels::elementwise_common;
51+
namespace td_ns = dpctl::tensor::type_dispatch;
52+
53+
/**
54+
* @brief A factory to define pairs of supported types for which
55+
* sycl::spacing<T> function is available.
56+
*
57+
* @tparam T Type of input vector `a` and of result vector `y`.
58+
*/
59+
template <typename T>
60+
struct OutputType
61+
{
62+
using value_type =
63+
typename std::disjunction<td_ns::TypeMapResultEntry<T, sycl::half>,
64+
td_ns::TypeMapResultEntry<T, float>,
65+
td_ns::TypeMapResultEntry<T, double>,
66+
td_ns::DefaultResultEntry<void>>::result_type;
67+
};
68+
69+
using dpnp::kernels::spacing::SpacingFunctor;
70+
71+
template <typename argT,
72+
typename resT = argT,
73+
unsigned int vec_sz = 4,
74+
unsigned int n_vecs = 2,
75+
bool enable_sg_loadstore = true>
76+
using ContigFunctor = ew_cmn_ns::UnaryContigFunctor<argT,
77+
resT,
78+
SpacingFunctor<argT, resT>,
79+
vec_sz,
80+
n_vecs,
81+
enable_sg_loadstore>;
82+
83+
template <typename argTy, typename resTy, typename IndexerT>
84+
using StridedFunctor = ew_cmn_ns::
85+
UnaryStridedFunctor<argTy, resTy, IndexerT, SpacingFunctor<argTy, resTy>>;
86+
87+
using ew_cmn_ns::unary_contig_impl_fn_ptr_t;
88+
using ew_cmn_ns::unary_strided_impl_fn_ptr_t;
89+
90+
static unary_contig_impl_fn_ptr_t
91+
spacing_contig_dispatch_vector[td_ns::num_types];
92+
static int spacing_output_typeid_vector[td_ns::num_types];
93+
static unary_strided_impl_fn_ptr_t
94+
spacing_strided_dispatch_vector[td_ns::num_types];
95+
96+
MACRO_POPULATE_DISPATCH_VECTORS(spacing);
97+
} // namespace impl
98+
99+
void init_spacing(py::module_ m)
100+
{
101+
using arrayT = dpctl::tensor::usm_ndarray;
102+
using event_vecT = std::vector<sycl::event>;
103+
{
104+
impl::populate_spacing_dispatch_vectors();
105+
using impl::spacing_contig_dispatch_vector;
106+
using impl::spacing_output_typeid_vector;
107+
using impl::spacing_strided_dispatch_vector;
108+
109+
auto spacing_pyapi = [&](const arrayT &src, const arrayT &dst,
110+
sycl::queue &exec_q,
111+
const event_vecT &depends = {}) {
112+
return py_int::py_unary_ufunc(src, dst, exec_q, depends,
113+
spacing_output_typeid_vector,
114+
spacing_contig_dispatch_vector,
115+
spacing_strided_dispatch_vector);
116+
};
117+
m.def("_spacing", spacing_pyapi, "", py::arg("src"), py::arg("dst"),
118+
py::arg("sycl_queue"), py::arg("depends") = py::list());
119+
120+
auto spacing_result_type_pyapi = [&](const py::dtype &dtype) {
121+
return py_int::py_unary_ufunc_result_type(
122+
dtype, spacing_output_typeid_vector);
123+
};
124+
m.def("_spacing_result_type", spacing_result_type_pyapi);
125+
}
126+
}
127+
} // namespace dpnp::extensions::ufunc
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//*****************************************************************************
2+
// Copyright (c) 2024, Intel Corporation
3+
// All rights reserved.
4+
//
5+
// Redistribution and use in source and binary forms, with or without
6+
// modification, are permitted provided that the following conditions are met:
7+
// - Redistributions of source code must retain the above copyright notice,
8+
// this list of conditions and the following disclaimer.
9+
// - Redistributions in binary form must reproduce the above copyright notice,
10+
// this list of conditions and the following disclaimer in the documentation
11+
// and/or other materials provided with the distribution.
12+
//
13+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16+
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
17+
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18+
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19+
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20+
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21+
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22+
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23+
// THE POSSIBILITY OF SUCH DAMAGE.
24+
//*****************************************************************************
25+
26+
#pragma once
27+
28+
#include <pybind11/pybind11.h>
29+
30+
namespace py = pybind11;
31+
32+
namespace dpnp::extensions::ufunc
33+
{
34+
void init_spacing(py::module_ m);
35+
} // namespace dpnp::extensions::ufunc
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//*****************************************************************************
2+
// Copyright (c) 2024, Intel Corporation
3+
// All rights reserved.
4+
//
5+
// Redistribution and use in source and binary forms, with or without
6+
// modification, are permitted provided that the following conditions are met:
7+
// - Redistributions of source code must retain the above copyright notice,
8+
// this list of conditions and the following disclaimer.
9+
// - Redistributions in binary form must reproduce the above copyright notice,
10+
// this list of conditions and the following disclaimer in the documentation
11+
// and/or other materials provided with the distribution.
12+
//
13+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16+
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
17+
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18+
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19+
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20+
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21+
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22+
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23+
// THE POSSIBILITY OF SUCH DAMAGE.
24+
//*****************************************************************************
25+
26+
#pragma once
27+
28+
#include <sycl/sycl.hpp>
29+
30+
namespace dpnp::kernels::spacing
31+
{
32+
template <typename argT, typename resT>
33+
struct SpacingFunctor
34+
{
35+
// is function constant for given argT
36+
using is_constant = typename std::false_type;
37+
// constant value, if constant
38+
// constexpr resT constant_value = resT{};
39+
// is function defined for sycl::vec
40+
using supports_vec = typename std::false_type;
41+
// do both argT and resT support subgroup store/load operation
42+
using supports_sg_loadstore = typename std::true_type;
43+
44+
resT operator()(const argT &x) const
45+
{
46+
if (sycl::isnan(x)) {
47+
return x;
48+
}
49+
50+
if (sycl::isinf(x)) {
51+
return std::numeric_limits<resT>::quiet_NaN();
52+
}
53+
54+
constexpr argT inf = std::numeric_limits<argT>::infinity();
55+
if constexpr (std::is_same_v<argT, sycl::half>) {
56+
// numpy always computes spacing towards +inf for float16 dtype
57+
return sycl::nextafter(x, inf) - x;
58+
}
59+
else {
60+
return sycl::nextafter(x, sycl::copysign(inf, x)) - x;
61+
}
62+
}
63+
};
64+
} // namespace dpnp::kernels::spacing

dpnp/dpnp_iface_mathematical.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@
130130
"round",
131131
"sign",
132132
"signbit",
133+
"spacing",
133134
"subtract",
134135
"sum",
135136
"trapezoid",
@@ -3749,6 +3750,62 @@ def real_if_close(a, tol=100):
37493750
)
37503751

37513752

3753+
_SPACING_DOCSTRING = """
3754+
Return the distance between `x` and the nearest adjacent number.
3755+
3756+
For full documentation refer to :obj:`numpy.spacing`.
3757+
3758+
Parameters
3759+
----------
3760+
x : {dpnp.ndarray, usm_ndarray}
3761+
The array of values to find the spacing of, expected to have a real-valued
3762+
data type.
3763+
out : {None, dpnp.ndarray, usm_ndarray}, optional
3764+
Output array to populate.
3765+
Array must have the correct shape and the expected data type.
3766+
Default: ``None``.
3767+
order : {"C", "F", "A", "K"}, optional
3768+
Memory layout of the newly output array, if parameter `out` is ``None``.
3769+
Default: ``"K"``.
3770+
3771+
Returns
3772+
-------
3773+
out : dpnp.ndarray
3774+
The spacing of values of `x`. The data type of the returned array is
3775+
determined by the Type Promotion Rules.
3776+
3777+
Limitations
3778+
-----------
3779+
Parameters `where` and `subok` are supported with their default values.
3780+
Keyword argument `kwargs` is currently unsupported.
3781+
Otherwise ``NotImplementedError`` exception will be raised.
3782+
3783+
Notes
3784+
-----
3785+
It can be considered as a generalization of EPS:
3786+
``dpnp.spacing(dpnp.float64(1)) == dpnp.finfo(dpnp.float64).eps``, and there
3787+
should not be any representable number between ``x + spacing(x)`` and `x` for
3788+
any finite `x`.
3789+
3790+
Spacing of +- inf and NaN is ``NaN``.
3791+
3792+
Examples
3793+
--------
3794+
>>> import dpnp as np
3795+
>>> a = np.array(1)
3796+
>>> b = np.spacing(a)
3797+
>>> b == np.finfo(b.dtype).eps
3798+
array(True)
3799+
"""
3800+
3801+
spacing = DPNPUnaryFunc(
3802+
"spacing",
3803+
ufi._spacing_result_type,
3804+
ufi._spacing,
3805+
_SPACING_DOCSTRING,
3806+
)
3807+
3808+
37523809
_SUBTRACT_DOCSTRING = """
37533810
Calculates the difference between each element `x1_i` of the input
37543811
array `x1` and the respective element `x2_i` of the input array `x2`.

tests/skipped_tests.tbl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ tests/test_umath.py::test_umaths[('divmod', 'ff')]
1717
tests/test_umath.py::test_umaths[('divmod', 'dd')]
1818
tests/test_umath.py::test_umaths[('frexp', 'f')]
1919
tests/test_umath.py::test_umaths[('frexp', 'd')]
20-
tests/test_umath.py::test_umaths[('spacing', 'f')]
21-
tests/test_umath.py::test_umaths[('spacing', 'd')]
2220

2321
tests/third_party/cupy/core_tests/test_ndarray_conversion.py::TestNdarrayToBytes_param_0_{shape=()}::test_item
2422
tests/third_party/cupy/core_tests/test_ndarray_conversion.py::TestNdarrayToBytes_param_1_{shape=(1,)}::test_item

tests/skipped_tests_gpu.tbl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ tests/test_umath.py::test_umaths[('divmod', 'dd')]
2424
tests/test_umath.py::test_umaths[('floor_divide', 'ff')]
2525
tests/test_umath.py::test_umaths[('frexp', 'f')]
2626
tests/test_umath.py::test_umaths[('frexp', 'd')]
27-
tests/test_umath.py::test_umaths[('spacing', 'f')]
28-
tests/test_umath.py::test_umaths[('spacing', 'd')]
2927

3028
tests/third_party/cupy/random_tests/test_distributions.py::TestDistributionsGeometric_param_2_{p_shape=(3, 2), shape=(4, 3, 2)}::test_geometric
3129
tests/third_party/cupy/random_tests/test_distributions.py::TestDistributionsGeometric_param_3_{p_shape=(3, 2), shape=(3, 2)}::test_geometric

0 commit comments

Comments
 (0)