Skip to content

Commit 70dea63

Browse files
authored
implement dpnp.blackman (#2363)
In this PR, `dpnp.blackman` is implemented.
1 parent 58bd91e commit 70dea63

File tree

8 files changed

+187
-7
lines changed

8 files changed

+187
-7
lines changed

CHANGELOG.md

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

1111
* Added implementation of `dpnp.hamming` [#2341](https://github.com/IntelPython/dpnp/pull/2341), [#2357](https://github.com/IntelPython/dpnp/pull/2357)
1212
* Added implementation of `dpnp.hanning` [#2358](https://github.com/IntelPython/dpnp/pull/2358)
13+
* Added implementation of `dpnp.blackman` [#2363](https://github.com/IntelPython/dpnp/pull/2363)
1314

1415
### Changed
1516

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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 BlackmanFunctor
36+
{
37+
private:
38+
T *data = nullptr;
39+
const std::size_t N;
40+
41+
public:
42+
BlackmanFunctor(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.42) - T(0.5) * sycl::cospi(T(2) * i / (N - 1)) +
49+
T(0.08) * sycl::cospi(T(4) * i / (N - 1));
50+
}
51+
};
52+
53+
template <typename fnT, typename T>
54+
struct BlackmanFactory
55+
{
56+
fnT get()
57+
{
58+
if constexpr (std::is_floating_point_v<T>) {
59+
return window_impl<T, BlackmanFunctor>;
60+
}
61+
else {
62+
return nullptr;
63+
}
64+
}
65+
};
66+
67+
} // namespace dpnp::extensions::window::kernels

dpnp/backend/extensions/window/window_py.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <pybind11/pybind11.h>
3131
#include <pybind11/stl.h>
3232

33+
#include "blackman.hpp"
3334
#include "common.hpp"
3435
#include "hamming.hpp"
3536
#include "hanning.hpp"
@@ -40,6 +41,7 @@ using window_ns::window_fn_ptr_t;
4041

4142
namespace dpctl_td_ns = dpctl::tensor::type_dispatch;
4243

44+
static window_fn_ptr_t blackman_dispatch_vector[dpctl_td_ns::num_types];
4345
static window_fn_ptr_t hamming_dispatch_vector[dpctl_td_ns::num_types];
4446
static window_fn_ptr_t hanning_dispatch_vector[dpctl_td_ns::num_types];
4547

@@ -48,6 +50,21 @@ PYBIND11_MODULE(_window_impl, m)
4850
using arrayT = dpctl::tensor::usm_ndarray;
4951
using event_vecT = std::vector<sycl::event>;
5052

53+
{
54+
window_ns::init_window_dispatch_vectors<
55+
window_ns::kernels::BlackmanFactory>(blackman_dispatch_vector);
56+
57+
auto blackman_pyapi = [&](sycl::queue &exec_q, const arrayT &result,
58+
const event_vecT &depends = {}) {
59+
return window_ns::py_window(exec_q, result, depends,
60+
blackman_dispatch_vector);
61+
};
62+
63+
m.def("_blackman", blackman_pyapi, "Call Blackman kernel",
64+
py::arg("sycl_queue"), py::arg("result"),
65+
py::arg("depends") = py::list());
66+
}
67+
5168
{
5269
window_ns::init_window_dispatch_vectors<
5370
window_ns::kernels::HammingFactory>(hamming_dispatch_vector);
@@ -58,7 +75,7 @@ PYBIND11_MODULE(_window_impl, m)
5875
hamming_dispatch_vector);
5976
};
6077

61-
m.def("_hamming", hamming_pyapi, "Call hamming kernel",
78+
m.def("_hamming", hamming_pyapi, "Call Hamming kernel",
6279
py::arg("sycl_queue"), py::arg("result"),
6380
py::arg("depends") = py::list());
6481
}
@@ -73,7 +90,7 @@ PYBIND11_MODULE(_window_impl, m)
7390
hanning_dispatch_vector);
7491
};
7592

76-
m.def("_hanning", hanning_pyapi, "Call hanning kernel",
93+
m.def("_hanning", hanning_pyapi, "Call Hanning kernel",
7794
py::arg("sycl_queue"), py::arg("result"),
7895
py::arg("depends") = py::list());
7996
}

dpnp/dpnp_iface_window.py

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

48-
__all__ = ["hamming", "hanning"]
48+
__all__ = ["blackman", "hamming", "hanning"]
4949

5050

5151
def _call_window_kernel(
@@ -81,6 +81,99 @@ def _call_window_kernel(
8181
return result
8282

8383

84+
def blackman(M, device=None, usm_type=None, sycl_queue=None):
85+
r"""
86+
Return the Blackman window.
87+
88+
The Blackman window is a taper formed by using the first three terms of a
89+
summation of cosines. It was designed to have close to the minimal leakage
90+
possible. It is close to optimal, only slightly worse than a Kaiser window.
91+
92+
For full documentation refer to :obj:`numpy.blackman`.
93+
94+
Parameters
95+
----------
96+
M : int
97+
Number of points in the output window. If zero or less, an empty array
98+
is returned.
99+
device : {None, string, SyclDevice, SyclQueue, Device}, optional
100+
An array API concept of device where the output array is created.
101+
`device` can be ``None``, a oneAPI filter selector string, an instance
102+
of :class:`dpctl.SyclDevice` corresponding to a non-partitioned SYCL
103+
device, an instance of :class:`dpctl.SyclQueue`, or a
104+
:class:`dpctl.tensor.Device` object returned by
105+
:attr:`dpnp.ndarray.device`.
106+
107+
Default: ``None``.
108+
usm_type : {None, "device", "shared", "host"}, optional
109+
The type of SYCL USM allocation for the output array.
110+
111+
Default: ``None``.
112+
sycl_queue : {None, SyclQueue}, optional
113+
A SYCL queue to use for output array allocation and copying. The
114+
`sycl_queue` can be passed as ``None`` (the default), which means
115+
to get the SYCL queue from `device` keyword if present or to use
116+
a default queue.
117+
118+
Default: ``None``.
119+
120+
Returns
121+
-------
122+
out : dpnp.ndarray of shape (M,)
123+
The window, with the maximum value normalized to one (the value one
124+
appears only if the number of samples is odd).
125+
126+
See Also
127+
--------
128+
:obj:`dpnp.bartlett` : Return the Bartlett window.
129+
:obj:`dpnp.hamming` : Return the Hamming window.
130+
:obj:`dpnp.hanning` : Return the Hanning window.
131+
:obj:`dpnp.kaiser` : Return the Kaiser window.
132+
133+
Notes
134+
-----
135+
The Blackman window is defined as
136+
137+
.. math:: w(n) = 0.42 - 0.5\cos\left(\frac{2\pi{n}}{M-1}\right)
138+
+ 0.08\cos\left(\frac{4\pi{n}}{M-1}\right)
139+
\qquad 0 \leq n \leq M-1
140+
141+
Examples
142+
--------
143+
>>> import dpnp as np
144+
>>> np.blackman(12)
145+
array([-1.38777878e-17, 3.26064346e-02, 1.59903635e-01, 4.14397981e-01,
146+
7.36045180e-01, 9.67046769e-01, 9.67046769e-01, 7.36045180e-01,
147+
4.14397981e-01, 1.59903635e-01, 3.26064346e-02, -1.38777878e-17])
148+
149+
Creating the output array on a different device or with a
150+
specified usm_type:
151+
152+
>>> x = np.blackman(3) # default case
153+
>>> x, x.device, x.usm_type
154+
(array([-1.38777878e-17, 1.00000000e+00, -1.38777878e-17]),
155+
Device(level_zero:gpu:0),
156+
'device')
157+
158+
>>> y = np.blackman(3, device="cpu")
159+
>>> y, y.device, y.usm_type
160+
(array([-1.38777878e-17, 1.00000000e+00, -1.38777878e-17]),
161+
Device(opencl:cpu:0),
162+
'device')
163+
164+
>>> z = np.blackman(3, usm_type="host")
165+
>>> z, z.device, z.usm_type
166+
(array([-1.38777878e-17, 1.00000000e+00, -1.38777878e-17]),
167+
Device(level_zero:gpu:0),
168+
'host')
169+
170+
"""
171+
172+
return _call_window_kernel(
173+
M, wi._blackman, device=device, usm_type=usm_type, sycl_queue=sycl_queue
174+
)
175+
176+
84177
def hamming(M, device=None, usm_type=None, sycl_queue=None):
85178
r"""
86179
Return the Hamming window.

dpnp/tests/test_sycl_queue.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def assert_sycl_queue_equal(result, expected):
5353
"func, arg, kwargs",
5454
[
5555
pytest.param("arange", [-25.7], {"stop": 10**8, "step": 15}),
56+
pytest.param("blackman", [10], {}),
5657
pytest.param("eye", [4, 2], {}),
5758
pytest.param("empty", [(2, 2)], {}),
5859
pytest.param(

dpnp/tests/test_usm_type.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ def test_array_creation_from_array(func, args, usm_type_x, usm_type_y):
177177
"func, arg, kwargs",
178178
[
179179
pytest.param("arange", [-25.7], {"stop": 10**8, "step": 15}),
180+
pytest.param("blackman", [10], {}),
180181
pytest.param("eye", [4, 2], {}),
181182
pytest.param("empty", [(3, 4)], {}),
182183
pytest.param(

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", "hanning"])
10+
@pytest.mark.parametrize("func", ["blackman", "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", "hanning"])
35+
@pytest.mark.parametrize("func", ["blackman", "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"] when supported
14-
"name": ["hamming", "hanning"],
13+
# TODO: add ["bartlett"] when supported
14+
"name": ["blackman", "hamming", "hanning"],
1515
}
1616
)
1717
)

0 commit comments

Comments
 (0)