Skip to content

Commit ead8b6d

Browse files
authored
Merge branch 'master' into add-uint64-to-bincount
2 parents d2e0482 + 25fb39b commit ead8b6d

18 files changed

+398
-280
lines changed

.github/workflows/conda-package.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ jobs:
218218
- name: Run tests
219219
if: env.rerun-tests-on-failure != 'true'
220220
run: |
221-
if [[ ${{ matrix.python }} == ${{ env.python-ver-test-all-dtypes }} ]]; then
221+
if [[ "${{ matrix.python }}" == "${{ env.python-ver-test-all-dtypes }}" ]]; then
222222
export DPNP_TEST_ALL_INT_TYPES=1
223223
python -m pytest -ra --pyargs ${{ env.package-name }}.tests
224224
else
@@ -238,7 +238,7 @@ jobs:
238238
. $CONDA/etc/profile.d/mamba.sh
239239
mamba activate ${{ env.test-env-name }}
240240
241-
if [[ ${{ matrix.python }} == ${{ env.python-ver-test-all-dtypes }} ]]; then
241+
if [[ "${{ matrix.python }}" == "${{ env.python-ver-test-all-dtypes }}" ]]; then
242242
export DPNP_TEST_ALL_INT_TYPES=1
243243
python -m pytest -ra --pyargs ${{ env.package-name }}.tests
244244
else
@@ -383,9 +383,10 @@ jobs:
383383
384384
- name: Run tests
385385
if: env.rerun-tests-on-failure != 'true'
386+
shell: pwsh
386387
run: |
387388
if (${{ matrix.python }} -eq ${{ env.python-ver-test-all-dtypes }}) {
388-
set DPNP_TEST_ALL_INT_TYPES=1
389+
$env:DPNP_TEST_ALL_INT_TYPES=1
389390
python -m pytest -ra --pyargs ${{ env.package-name }}.tests
390391
} else {
391392
python -m pytest -n auto -ra --pyargs ${{ env.package-name }}.tests
@@ -399,9 +400,10 @@ jobs:
399400
timeout_minutes: ${{ env.rerun-tests-timeout }}
400401
max_attempts: ${{ env.rerun-tests-max-attempts }}
401402
retry_on: any
403+
shell: pwsh
402404
command: |
403405
if ( ${{ matrix.python }} -eq ${{ env.python-ver-test-all-dtypes }} ) {
404-
set DPNP_TEST_ALL_INT_TYPES=1
406+
$env:DPNP_TEST_ALL_INT_TYPES=1
405407
python -m pytest -ra --pyargs ${{ env.package-name }}.tests
406408
} else {
407409
python -m pytest -n auto -ra --pyargs ${{ env.package-name }}.tests

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/conftest.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
import numpy
3333
import pytest
3434

35+
from . import config as dtype_config
36+
3537
if numpy.lib.NumpyVersion(numpy.__version__) >= "2.0.0b1":
3638
from numpy.exceptions import ComplexWarning
3739
else:
@@ -128,20 +130,25 @@ def pytest_collection_modifyitems(config, items):
128130

129131
dev = dpctl.select_default_device()
130132
is_cpu = dev.is_cpu
131-
is_gpu_no_fp64 = not dev.has_aspect_fp64
133+
is_gpu = dev.is_gpu
134+
support_fp64 = dev.has_aspect_fp64
132135
is_cuda = dpnp.is_cuda_backend(dev)
133136

134137
print("")
138+
print(
139+
f"DPNP Test scope includes all integer dtypes: {bool(dtype_config.all_int_types)}"
140+
)
135141
print(f"DPNP current device is CPU: {is_cpu}")
136-
print(f"DPNP current device is GPU without fp64 support: {is_gpu_no_fp64}")
142+
print(f"DPNP current device is GPU: {is_gpu}")
143+
print(f"DPNP current device supports fp64: {support_fp64}")
137144
print(f"DPNP current device is GPU with cuda backend: {is_cuda}")
138145
print(f"DPNP version: {dpnp.__version__}, location: {dpnp}")
139146
print(f"NumPy version: {numpy.__version__}, location: {numpy}")
140147
print(f"Python version: {sys.version}")
141148
print("")
142-
if not is_cpu or os.getenv("DPNP_QUEUE_GPU") == "1":
149+
if is_gpu or os.getenv("DPNP_QUEUE_GPU") == "1":
143150
excluded_tests.extend(get_excluded_tests(test_exclude_file_gpu))
144-
if is_gpu_no_fp64:
151+
if not support_fp64:
145152
excluded_tests.extend(
146153
get_excluded_tests(test_exclude_file_gpu_no_fp64)
147154
)

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:

0 commit comments

Comments
 (0)