Skip to content

Commit a981ac6

Browse files
authored
Merge branch 'master' into fix-all-int-windows
2 parents efeb836 + 70dea63 commit a981ac6

16 files changed

+381
-272
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_counting.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,19 @@ def count_nonzero(a, axis=None, *, keepdims=False, out=None):
5858
Axis or tuple of axes along which to count non-zeros.
5959
Default value means that non-zeros will be counted along a flattened
6060
version of `a`.
61+
6162
Default: ``None``.
62-
keepdims : bool, optional
63+
keepdims : {None, bool}, optional
6364
If this is set to ``True``, the axes that are counted are left in the
6465
result as dimensions with size one. With this option, the result will
6566
broadcast correctly against the input array.
67+
6668
Default: ``False``.
6769
out : {None, dpnp.ndarray, usm_ndarray}, optional
6870
The array into which the result is written. The data type of `out` must
6971
match the expected shape and the expected data type of the result.
7072
If ``None`` then a new array is returned.
73+
7174
Default: ``None``.
7275
7376
Returns

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_indexing.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,15 +1200,17 @@ def test_empty_indices_error(self):
12001200
)
12011201

12021202
def test_empty_indices(self):
1203-
assert_equal(
1204-
dpnp.ravel_multi_index(
1205-
(dpnp.array([], dtype=int), dpnp.array([], dtype=int)), (5, 3)
1206-
),
1207-
[],
1208-
)
1209-
assert_equal(
1210-
dpnp.ravel_multi_index(dpnp.array([[], []], dtype=int), (5, 3)), []
1211-
)
1203+
a = numpy.array([], dtype=int)
1204+
ia = dpnp.array(a)
1205+
result = dpnp.ravel_multi_index((ia, ia), (5, 3))
1206+
expected = numpy.ravel_multi_index((a, a), (5, 3))
1207+
assert_equal(result, expected)
1208+
1209+
a = numpy.array([[], []], dtype=int)
1210+
ia = dpnp.array(a)
1211+
result = dpnp.ravel_multi_index(ia, (5, 3))
1212+
expected = numpy.ravel_multi_index(a, (5, 3))
1213+
assert_equal(result, expected)
12121214

12131215

12141216
class TestUnravelIndex:

dpnp/tests/test_linalg.py

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,9 @@ def test_empty(self, shape, p):
293293
expected = numpy.linalg.cond(a, p=p)
294294
assert_dtype_allclose(result, expected)
295295

296-
@pytest.mark.parametrize("dtype", get_all_dtypes(no_bool=True))
296+
@pytest.mark.parametrize(
297+
"dtype", get_all_dtypes(no_none=True, no_bool=True)
298+
)
297299
@pytest.mark.parametrize(
298300
"shape", [(4, 4), (2, 4, 3, 3)], ids=["(4, 4)", "(2, 4, 3, 3)"]
299301
)
@@ -2408,10 +2410,8 @@ def test_qr(self, dtype, shape, mode):
24082410
if mode in ("complete", "reduced"):
24092411
result = dpnp.linalg.qr(ia, mode)
24102412
dpnp_q, dpnp_r = result.Q, result.R
2411-
assert_almost_equal(
2412-
dpnp.matmul(dpnp_q, dpnp_r),
2413-
a,
2414-
decimal=5,
2413+
assert dpnp.allclose(
2414+
dpnp.matmul(dpnp_q, dpnp_r), ia, atol=1e-05
24152415
)
24162416
else: # mode=="raw"
24172417
dpnp_q, dpnp_r = dpnp.linalg.qr(ia, mode)
@@ -2424,15 +2424,13 @@ def test_qr(self, dtype, shape, mode):
24242424
@pytest.mark.parametrize(
24252425
"shape",
24262426
[(32, 32), (8, 16, 16)],
2427-
ids=[
2428-
"(32, 32)",
2429-
"(8, 16, 16)",
2430-
],
2427+
ids=["(32, 32)", "(8, 16, 16)"],
24312428
)
24322429
@pytest.mark.parametrize("mode", ["r", "raw", "complete", "reduced"])
24332430
def test_qr_large(self, dtype, shape, mode):
24342431
a = generate_random_numpy_array(shape, dtype, seed_value=81)
24352432
ia = dpnp.array(a)
2433+
24362434
if mode == "r":
24372435
np_r = numpy.linalg.qr(a, mode)
24382436
dpnp_r = dpnp.linalg.qr(ia, mode)
@@ -2443,11 +2441,7 @@ def test_qr_large(self, dtype, shape, mode):
24432441
if mode in ("complete", "reduced"):
24442442
result = dpnp.linalg.qr(ia, mode)
24452443
dpnp_q, dpnp_r = result.Q, result.R
2446-
assert_almost_equal(
2447-
dpnp.matmul(dpnp_q, dpnp_r),
2448-
a,
2449-
decimal=5,
2450-
)
2444+
assert dpnp.allclose(dpnp.matmul(dpnp_q, dpnp_r), ia, atol=1e-5)
24512445
else: # mode=="raw"
24522446
dpnp_q, dpnp_r = dpnp.linalg.qr(ia, mode)
24532447
assert_allclose(dpnp_q, np_q, atol=1e-4)
@@ -3169,9 +3163,7 @@ def test_test_tensorinv_errors(self):
31693163
class TestTensorsolve:
31703164
@pytest.mark.parametrize("dtype", get_all_dtypes())
31713165
@pytest.mark.parametrize(
3172-
"axes",
3173-
[None, (1,), (2,)],
3174-
ids=["None", "(1,)", "(2,)"],
3166+
"axes", [None, (1,), (2,)], ids=["None", "(1,)", "(2,)"]
31753167
)
31763168
def test_tensorsolve_axes(self, dtype, axes):
31773169
a = numpy.eye(12).reshape(12, 3, 4).astype(dtype)

0 commit comments

Comments
 (0)