Skip to content

Commit edf2dbf

Browse files
authored
implementation of dpnp.hanning (#2358)
In this PR, `dpnp.hanning` is implemented.
1 parent b91e43b commit edf2dbf

File tree

9 files changed

+206
-26
lines changed

9 files changed

+206
-26
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
### Added
1010

1111
* Added implementation of `dpnp.hamming` [#2341](https://github.com/IntelPython/dpnp/pull/2341), [#2357](https://github.com/IntelPython/dpnp/pull/2357)
12+
* Added implementation of `dpnp.hanning` [#2358](https://github.com/IntelPython/dpnp/pull/2358)
1213

1314
### Changed
1415

doc/known_words.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ Mises
5757
multinomial
5858
multivalued
5959
namespace
60+
namespaces
6061
namedtuple
6162
NaN
6263
NaT
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
//*****************************************************************************
2+
// Copyright (c) 2025, 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 "common.hpp"
29+
#include <sycl/sycl.hpp>
30+
31+
namespace dpnp::extensions::window::kernels
32+
{
33+
34+
template <typename T>
35+
class HanningFunctor
36+
{
37+
private:
38+
T *data = nullptr;
39+
const std::size_t N;
40+
41+
public:
42+
HanningFunctor(T *data, const std::size_t N) : data(data), N(N) {}
43+
44+
void operator()(sycl::id<1> id) const
45+
{
46+
const auto i = id.get(0);
47+
48+
data[i] = T(0.5) - T(0.5) * sycl::cospi(T(2) * i / (N - 1));
49+
}
50+
};
51+
52+
template <typename fnT, typename T>
53+
struct HanningFactory
54+
{
55+
fnT get()
56+
{
57+
if constexpr (std::is_floating_point_v<T>) {
58+
return window_impl<T, HanningFunctor>;
59+
}
60+
else {
61+
return nullptr;
62+
}
63+
}
64+
};
65+
66+
} // namespace dpnp::extensions::window::kernels

dpnp/backend/extensions/window/window_py.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
#include "common.hpp"
3434
#include "hamming.hpp"
35+
#include "hanning.hpp"
3536

3637
namespace window_ns = dpnp::extensions::window;
3738
namespace py = pybind11;
@@ -40,6 +41,7 @@ using window_ns::window_fn_ptr_t;
4041
namespace dpctl_td_ns = dpctl::tensor::type_dispatch;
4142

4243
static window_fn_ptr_t hamming_dispatch_vector[dpctl_td_ns::num_types];
44+
static window_fn_ptr_t hanning_dispatch_vector[dpctl_td_ns::num_types];
4345

4446
PYBIND11_MODULE(_window_impl, m)
4547
{
@@ -60,4 +62,19 @@ PYBIND11_MODULE(_window_impl, m)
6062
py::arg("sycl_queue"), py::arg("result"),
6163
py::arg("depends") = py::list());
6264
}
65+
66+
{
67+
window_ns::init_window_dispatch_vectors<
68+
window_ns::kernels::HanningFactory>(hanning_dispatch_vector);
69+
70+
auto hanning_pyapi = [&](sycl::queue &exec_q, const arrayT &result,
71+
const event_vecT &depends = {}) {
72+
return window_ns::py_window(exec_q, result, depends,
73+
hanning_dispatch_vector);
74+
};
75+
76+
m.def("_hanning", hanning_pyapi, "Call hanning kernel",
77+
py::arg("sycl_queue"), py::arg("result"),
78+
py::arg("depends") = py::list());
79+
}
6380
}

dpnp/dpnp_iface_window.py

Lines changed: 115 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,40 @@
4545
import dpnp
4646
import dpnp.backend.extensions.window._window_impl as wi
4747

48-
__all__ = ["hamming"]
48+
__all__ = ["hamming", "hanning"]
49+
50+
51+
def _call_window_kernel(
52+
M, _window_kernel, device=None, usm_type=None, sycl_queue=None
53+
):
54+
55+
try:
56+
M = int(M)
57+
except Exception as e:
58+
raise TypeError("M must be an integer") from e
59+
60+
cfd_kwarg = {
61+
"device": device,
62+
"usm_type": usm_type,
63+
"sycl_queue": sycl_queue,
64+
}
65+
66+
if M < 1:
67+
return dpnp.empty(0, **cfd_kwarg)
68+
if M == 1:
69+
return dpnp.ones(1, **cfd_kwarg)
70+
71+
result = dpnp.empty(M, **cfd_kwarg)
72+
exec_q = result.sycl_queue
73+
_manager = dpu.SequentialOrderManager[exec_q]
74+
75+
ht_ev, win_ev = _window_kernel(
76+
exec_q, dpnp.get_usm_ndarray(result), depends=_manager.submitted_events
77+
)
78+
79+
_manager.add_event_pair(ht_ev, win_ev)
80+
81+
return result
4982

5083

5184
def hamming(M, device=None, usm_type=None, sycl_queue=None):
@@ -127,30 +160,90 @@ def hamming(M, device=None, usm_type=None, sycl_queue=None):
127160
128161
"""
129162

130-
try:
131-
M = int(M)
132-
except Exception as e:
133-
raise TypeError("M must be an integer") from e
163+
return _call_window_kernel(
164+
M, wi._hamming, device=device, usm_type=usm_type, sycl_queue=sycl_queue
165+
)
134166

135-
cfd_kwarg = {
136-
"device": device,
137-
"usm_type": usm_type,
138-
"sycl_queue": sycl_queue,
139-
}
140167

141-
if M < 1:
142-
return dpnp.empty(0, **cfd_kwarg)
143-
if M == 1:
144-
return dpnp.ones(1, **cfd_kwarg)
168+
def hanning(M, device=None, usm_type=None, sycl_queue=None):
169+
r"""
170+
Return the Hanning window.
145171
146-
result = dpnp.empty(M, **cfd_kwarg)
147-
exec_q = result.sycl_queue
148-
_manager = dpu.SequentialOrderManager[exec_q]
172+
The Hanning window is a taper formed by using a weighted cosine.
149173
150-
ht_ev, win_ev = wi._hamming(
151-
exec_q, dpnp.get_usm_ndarray(result), depends=_manager.submitted_events
152-
)
174+
For full documentation refer to :obj:`numpy.hanning`.
153175
154-
_manager.add_event_pair(ht_ev, win_ev)
176+
Parameters
177+
----------
178+
M : int
179+
Number of points in the output window. If zero or less, an empty array
180+
is returned.
181+
device : {None, string, SyclDevice, SyclQueue, Device}, optional
182+
An array API concept of device where the output array is created.
183+
`device` can be ``None``, a oneAPI filter selector string, an instance
184+
of :class:`dpctl.SyclDevice` corresponding to a non-partitioned SYCL
185+
device, an instance of :class:`dpctl.SyclQueue`, or a
186+
:class:`dpctl.tensor.Device` object returned by
187+
:attr:`dpnp.ndarray.device`.
155188
156-
return result
189+
Default: ``None``.
190+
usm_type : {None, "device", "shared", "host"}, optional
191+
The type of SYCL USM allocation for the output array.
192+
193+
Default: ``None``.
194+
sycl_queue : {None, SyclQueue}, optional
195+
A SYCL queue to use for output array allocation and copying. The
196+
`sycl_queue` can be passed as ``None`` (the default), which means
197+
to get the SYCL queue from `device` keyword if present or to use
198+
a default queue.
199+
200+
Default: ``None``.
201+
202+
Returns
203+
-------
204+
out : dpnp.ndarray of shape (M,)
205+
The window, with the maximum value normalized to one (the value one
206+
appears only if the number of samples is odd).
207+
208+
See Also
209+
--------
210+
:obj:`dpnp.bartlett` : Return the Bartlett window.
211+
:obj:`dpnp.blackman` : Return the Blackman window.
212+
:obj:`dpnp.hamming` : Return the Hamming window.
213+
:obj:`dpnp.kaiser` : Return the Kaiser window.
214+
215+
Notes
216+
-----
217+
The Hanning window is defined as
218+
219+
.. math:: w(n) = 0.5 - 0.5\cos\left(\frac{2\pi{n}}{M-1}\right)
220+
\qquad 0 \leq n \leq M-1
221+
222+
Examples
223+
--------
224+
>>> import dpnp as np
225+
>>> np.hanning(12)
226+
array([0. , 0.07937323, 0.29229249, 0.57115742, 0.82743037,
227+
0.97974649, 0.97974649, 0.82743037, 0.57115742, 0.29229249,
228+
0.07937323, 0. ])
229+
230+
Creating the output array on a different device or with a
231+
specified usm_type:
232+
233+
>>> x = np.hanning(4) # default case
234+
>>> x, x.device, x.usm_type
235+
(array([0. , 0.75, 0.75, 0. ]), Device(level_zero:gpu:0), 'device')
236+
237+
>>> y = np.hanning(4, device="cpu")
238+
>>> y, y.device, y.usm_type
239+
(array([0. , 0.75, 0.75, 0. ]), Device(opencl:cpu:0), 'device')
240+
241+
>>> z = np.hanning(4, usm_type="host")
242+
>>> z, z.device, z.usm_type
243+
(array([0. , 0.75, 0.75, 0. ]), Device(level_zero:gpu:0), 'host')
244+
245+
"""
246+
247+
return _call_window_kernel(
248+
M, wi._hanning, device=device, usm_type=usm_type, sycl_queue=sycl_queue
249+
)

dpnp/tests/test_sycl_queue.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ def assert_sycl_queue_equal(result, expected):
6868
pytest.param("full", [(2, 2)], {"fill_value": 5}),
6969
pytest.param("geomspace", [1, 4, 8], {}),
7070
pytest.param("hamming", [10], {}),
71+
pytest.param("hanning", [10], {}),
7172
pytest.param("identity", [4], {}),
7273
pytest.param("linspace", [0, 4, 8], {}),
7374
pytest.param("logspace", [0, 4, 8], {}),

dpnp/tests/test_usm_type.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ def test_array_creation_from_array(func, args, usm_type_x, usm_type_y):
192192
pytest.param("full", [(2, 2)], {"fill_value": 5}),
193193
pytest.param("geomspace", [1, 4, 8], {}),
194194
pytest.param("hamming", [10], {}),
195+
pytest.param("hanning", [10], {}),
195196
pytest.param("identity", [4], {}),
196197
pytest.param("linspace", [0, 4, 8], {}),
197198
pytest.param("logspace", [0, 4, 8], {}),

dpnp/tests/test_window.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from .helper import assert_dtype_allclose
88

99

10-
@pytest.mark.parametrize("func", ["hamming"])
10+
@pytest.mark.parametrize("func", ["hamming", "hanning"])
1111
@pytest.mark.parametrize(
1212
"M",
1313
[
@@ -32,7 +32,7 @@ def test_window(func, M):
3232
assert_dtype_allclose(result, expected)
3333

3434

35-
@pytest.mark.parametrize("func", ["hamming"])
35+
@pytest.mark.parametrize("func", ["hamming", "hanning"])
3636
@pytest.mark.parametrize(
3737
"M",
3838
[

dpnp/tests/third_party/cupy/math_tests/test_window.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
*testing.product(
1111
{
1212
"m": [0, 1, -1, 1024],
13-
# TODO: add ["bartlett", "blackman", "hanning"] when supported
14-
"name": ["hamming"],
13+
# TODO: add ["bartlett", "blackman"] when supported
14+
"name": ["hamming", "hanning"],
1515
}
1616
)
1717
)

0 commit comments

Comments
 (0)