Skip to content

Commit 5a39d91

Browse files
antonwolfyvtavana
andauthored
Add implementation of dpnp.sinc function (#2133)
* Add _sinc to ufunc extension * Add dpnp.sinc function to the interface * Add third party tests * Add complex type handling * Mute tests for dono.i0 * Add CFD tests * Add more tests to cover different use cases * Add a proper handling of corner cases * Update dpnp/dpnp_iface_mathematical.py Co-authored-by: vtavana <120411540+vtavana@users.noreply.github.com> * Removed limitation block * Use dedicated DPNPSinc class for dpnp.sinc implementation --------- Co-authored-by: vtavana <120411540+vtavana@users.noreply.github.com>
1 parent 16d6ea1 commit 5a39d91

File tree

11 files changed

+565
-0
lines changed

11 files changed

+565
-0
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/sinc.cpp
4142
${CMAKE_CURRENT_SOURCE_DIR}/elementwise_functions/spacing.cpp
4243
)
4344

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 "sinc.hpp"
4142
#include "spacing.hpp"
4243

4344
namespace py = pybind11;
@@ -62,6 +63,7 @@ void init_elementwise_functions(py::module_ m)
6263
init_ldexp(m);
6364
init_logaddexp2(m);
6465
init_radians(m);
66+
init_sinc(m);
6567
init_spacing(m);
6668
}
6769
} // 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/sinc.hpp"
31+
#include "populate.hpp"
32+
#include "sinc.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::sinc<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 = typename std::disjunction<
63+
td_ns::TypeMapResultEntry<T, sycl::half>,
64+
td_ns::TypeMapResultEntry<T, float>,
65+
td_ns::TypeMapResultEntry<T, double>,
66+
td_ns::TypeMapResultEntry<T, std::complex<float>>,
67+
td_ns::TypeMapResultEntry<T, std::complex<double>>,
68+
td_ns::DefaultResultEntry<void>>::result_type;
69+
};
70+
71+
using dpnp::kernels::sinc::SincFunctor;
72+
73+
template <typename argT,
74+
typename resT = argT,
75+
unsigned int vec_sz = 4,
76+
unsigned int n_vecs = 2,
77+
bool enable_sg_loadstore = true>
78+
using ContigFunctor = ew_cmn_ns::UnaryContigFunctor<argT,
79+
resT,
80+
SincFunctor<argT, resT>,
81+
vec_sz,
82+
n_vecs,
83+
enable_sg_loadstore>;
84+
85+
template <typename argTy, typename resTy, typename IndexerT>
86+
using StridedFunctor = ew_cmn_ns::
87+
UnaryStridedFunctor<argTy, resTy, IndexerT, SincFunctor<argTy, resTy>>;
88+
89+
using ew_cmn_ns::unary_contig_impl_fn_ptr_t;
90+
using ew_cmn_ns::unary_strided_impl_fn_ptr_t;
91+
92+
static unary_contig_impl_fn_ptr_t sinc_contig_dispatch_vector[td_ns::num_types];
93+
static int sinc_output_typeid_vector[td_ns::num_types];
94+
static unary_strided_impl_fn_ptr_t
95+
sinc_strided_dispatch_vector[td_ns::num_types];
96+
97+
MACRO_POPULATE_DISPATCH_VECTORS(sinc);
98+
} // namespace impl
99+
100+
void init_sinc(py::module_ m)
101+
{
102+
using arrayT = dpctl::tensor::usm_ndarray;
103+
using event_vecT = std::vector<sycl::event>;
104+
{
105+
impl::populate_sinc_dispatch_vectors();
106+
using impl::sinc_contig_dispatch_vector;
107+
using impl::sinc_output_typeid_vector;
108+
using impl::sinc_strided_dispatch_vector;
109+
110+
auto sinc_pyapi = [&](const arrayT &src, const arrayT &dst,
111+
sycl::queue &exec_q,
112+
const event_vecT &depends = {}) {
113+
return py_int::py_unary_ufunc(
114+
src, dst, exec_q, depends, sinc_output_typeid_vector,
115+
sinc_contig_dispatch_vector, sinc_strided_dispatch_vector);
116+
};
117+
m.def("_sinc", sinc_pyapi, "", py::arg("src"), py::arg("dst"),
118+
py::arg("sycl_queue"), py::arg("depends") = py::list());
119+
120+
auto sinc_result_type_pyapi = [&](const py::dtype &dtype) {
121+
return py_int::py_unary_ufunc_result_type(
122+
dtype, sinc_output_typeid_vector);
123+
};
124+
m.def("_sinc_result_type", sinc_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_sinc(py::module_ m);
35+
} // namespace dpnp::extensions::ufunc
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
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+
#define SYCL_EXT_ONEAPI_COMPLEX
29+
#if __has_include(<sycl/ext/oneapi/experimental/sycl_complex.hpp>)
30+
#include <sycl/ext/oneapi/experimental/sycl_complex.hpp>
31+
#else
32+
#include <sycl/ext/oneapi/experimental/complex/complex.hpp>
33+
#endif
34+
35+
#include <sycl/sycl.hpp>
36+
37+
// dpctl tensor headers
38+
#include "utils/type_utils.hpp"
39+
40+
namespace dpnp::kernels::sinc
41+
{
42+
namespace tu_ns = dpctl::tensor::type_utils;
43+
44+
namespace impl
45+
{
46+
namespace exprm_ns = sycl::ext::oneapi::experimental;
47+
48+
template <typename Tp>
49+
inline Tp sin(const Tp &in)
50+
{
51+
if constexpr (tu_ns::is_complex<Tp>::value) {
52+
using realTp = typename Tp::value_type;
53+
54+
constexpr realTp q_nan = std::numeric_limits<realTp>::quiet_NaN();
55+
56+
realTp const &in_re = std::real(in);
57+
realTp const &in_im = std::imag(in);
58+
59+
const bool in_re_finite = sycl::isfinite(in_re);
60+
const bool in_im_finite = sycl::isfinite(in_im);
61+
/*
62+
* Handle the nearly-non-exceptional cases where
63+
* real and imaginary parts of input are finite.
64+
*/
65+
if (in_re_finite && in_im_finite) {
66+
Tp res = exprm_ns::sin(exprm_ns::complex<realTp>(in)); // sin(in);
67+
if (in_re == realTp(0)) {
68+
res.real(sycl::copysign(realTp(0), in_re));
69+
}
70+
return res;
71+
}
72+
73+
/*
74+
* since sin(in) = -I * sinh(I * in), for special cases,
75+
* we calculate real and imaginary parts of z = sinh(I * in) and
76+
* then return { imag(z) , -real(z) } which is sin(in).
77+
*/
78+
const realTp x = -in_im;
79+
const realTp y = in_re;
80+
const bool xfinite = in_im_finite;
81+
const bool yfinite = in_re_finite;
82+
/*
83+
* sinh(+-0 +- I Inf) = sign(d(+-0, dNaN))0 + I dNaN.
84+
* The sign of 0 in the result is unspecified. Choice = normally
85+
* the same as dNaN.
86+
*
87+
* sinh(+-0 +- I NaN) = sign(d(+-0, NaN))0 + I d(NaN).
88+
* The sign of 0 in the result is unspecified. Choice = normally
89+
* the same as d(NaN).
90+
*/
91+
if (x == realTp(0) && !yfinite) {
92+
const realTp sinh_im = q_nan;
93+
const realTp sinh_re = sycl::copysign(realTp(0), x * sinh_im);
94+
return Tp{sinh_im, -sinh_re};
95+
}
96+
97+
/*
98+
* sinh(+-Inf +- I 0) = +-Inf + I +-0.
99+
*
100+
* sinh(NaN +- I 0) = d(NaN) + I +-0.
101+
*/
102+
if (y == realTp(0) && !xfinite) {
103+
if (std::isnan(x)) {
104+
const realTp sinh_re = x;
105+
const realTp sinh_im = y;
106+
return Tp{sinh_im, -sinh_re};
107+
}
108+
const realTp sinh_re = x;
109+
const realTp sinh_im = sycl::copysign(realTp(0), y);
110+
return Tp{sinh_im, -sinh_re};
111+
}
112+
113+
/*
114+
* sinh(x +- I Inf) = dNaN + I dNaN.
115+
*
116+
* sinh(x + I NaN) = d(NaN) + I d(NaN).
117+
*/
118+
if (xfinite && !yfinite) {
119+
const realTp sinh_re = q_nan;
120+
const realTp sinh_im = x * sinh_re;
121+
return Tp{sinh_im, -sinh_re};
122+
}
123+
124+
/*
125+
* sinh(+-Inf + I NaN) = +-Inf + I d(NaN).
126+
* The sign of Inf in the result is unspecified. Choice = normally
127+
* the same as d(NaN).
128+
*
129+
* sinh(+-Inf +- I Inf) = +Inf + I dNaN.
130+
* The sign of Inf in the result is unspecified.
131+
* Choice = always - here for sinh to have positive result for
132+
* imaginary part of sin.
133+
*
134+
* sinh(+-Inf + I y) = +-Inf cos(y) + I Inf sin(y)
135+
*/
136+
if (std::isinf(x)) {
137+
if (!yfinite) {
138+
const realTp sinh_re = -x * x;
139+
const realTp sinh_im = x * (y - y);
140+
return Tp{sinh_im, -sinh_re};
141+
}
142+
const realTp sinh_re = x * sycl::cos(y);
143+
const realTp sinh_im =
144+
std::numeric_limits<realTp>::infinity() * sycl::sin(y);
145+
return Tp{sinh_im, -sinh_re};
146+
}
147+
148+
/*
149+
* sinh(NaN + I NaN) = d(NaN) + I d(NaN).
150+
*
151+
* sinh(NaN +- I Inf) = d(NaN) + I d(NaN).
152+
*
153+
* sinh(NaN + I y) = d(NaN) + I d(NaN).
154+
*/
155+
const realTp y_m_y = (y - y);
156+
const realTp sinh_re = (x * x) * y_m_y;
157+
const realTp sinh_im = (x + x) * y_m_y;
158+
return Tp{sinh_im, -sinh_re};
159+
}
160+
else {
161+
if (in == Tp(0)) {
162+
return in;
163+
}
164+
return sycl::sin(in);
165+
}
166+
}
167+
} // namespace impl
168+
169+
template <typename argT, typename Tp>
170+
struct SincFunctor
171+
{
172+
// is function constant for given argT
173+
using is_constant = typename std::false_type;
174+
// constant value, if constant
175+
// constexpr Tp constant_value = Tp{};
176+
// is function defined for sycl::vec
177+
using supports_vec = typename std::false_type;
178+
// do both argT and Tp support subgroup store/load operation
179+
using supports_sg_loadstore = typename std::negation<
180+
std::disjunction<tu_ns::is_complex<Tp>, tu_ns::is_complex<argT>>>;
181+
182+
Tp operator()(const argT &x) const
183+
{
184+
constexpr argT pi =
185+
static_cast<argT>(3.1415926535897932384626433832795029L);
186+
const argT y = pi * x;
187+
188+
if (y == argT(0)) {
189+
return Tp(1);
190+
}
191+
192+
if constexpr (tu_ns::is_complex<argT>::value) {
193+
return impl::sin(y) / y;
194+
}
195+
else {
196+
return sycl::sinpi(x) / y;
197+
}
198+
}
199+
};
200+
} // namespace dpnp::kernels::sinc

0 commit comments

Comments
 (0)